티스토리 뷰
Kubernetes에서 특권 포트 피하기: NodePort + iptables 포워딩 패턴
pak2251 2025. 10. 27. 14:14작성일: 2025년 10월 27일
카테고리: Kubernetes, Security, Networking
난이도: 중급
TL;DR
- 문제: Kong Gateway를 80/443 포트에서 실행하려면 root 권한 필요 (보안 위험)
- 해결: NodePort(30080/30443) + iptables 포워딩으로 보안, 고가용성, 무중단 배포 모두 확보
- 핵심: Linux 1024 미만 포트는 root만 바인딩 가능하지만, iptables는 커널 레벨에서 포워딩
- 결과: Pod는 root 없이 실행, 다중 Pod로 HA 구성, Rolling Update 무중단, 외부에서는 80/443으로 접속
들어가며
imprun.dev는 Kubernetes 기반 서버리스 Cloud Function 플랫폼입니다. API Gateway로 Kong을 운영하면서, 표준 HTTP/HTTPS 포트(80/443)에서 서비스하되 보안을 유지해야 하는 과제에 직면했습니다.
우리가 마주한 질문:
- ❓ root 실행: Kong을 80/443 포트에서 실행하려면 root 권한이 필요한데, 보안은?
- ❓ hostNetwork: 노드 네트워크를 직접 사용하면? 고가용성은?
- ❓ MetalLB/Cloud LB: LoadBalancer 타입이 정석인데, 우리 환경에서 가능할까?
검증 과정:
root + 특권 포트 (80/443)
- ✅ 간단하지만 Pod Security Standards: Restricted 위반
- ❌ 컨테이너 탈출 시 노드 전체 장악 가능
hostNetwork + iptables
- ✅ non-root로 실행 가능
- ❌ 노드당 1개 Pod만 가능 → 고가용성 불가
- ❌ Rolling Update 시 다운타임 발생 → 서비스 중단
NodePort + iptables ← 최종 선택
- ✅ non-root 실행 (UID 1000, 비특권 포트 8000/8443)
- ✅ 다중 Pod 실행 (최소 2개, HPA로 자동 스케일)
- ✅ Rolling Update 무중단 배포
- ✅ 추가 인프라 불필요 (MetalLB/Cloud LB 비용 0원)
환경 제약사항
- ❌ Cloud LoadBalancer: 온프레미스 환경, 월 비용 발생
- ❌ MetalLB: 제한된 리소스 환경(3노드, 4 cores, 24GB), 추가 오버헤드 부담
- ✅ NodePort + iptables: 커널 레벨 동작, 리소스 오버헤드 없음
결론:
- ✅ 보안 강화 (Pod Security Standards: Restricted 준수)
- ✅ 고가용성 확보 (최소 2개 Pod, 자동 페일오버)
- ✅ 무중단 배포 (Rolling Update, 다운타임 0초)
- ✅ 운영 비용 절감 (추가 인프라 없음)
이 글은 imprun.dev 플랫폼 구축 경험을 바탕으로, 특권 포트 문제의 본질, hostNetwork의 치명적 한계, 그리고 NodePort + iptables가 제한된 환경에서 어떻게 보안/고가용성/무중단 배포를 동시에 달성했는지 상세히 공유합니다.
배경: 특권 포트의 딜레마
Linux의 특권 포트 제약
Unix/Linux 시스템에서는 1024 미만 포트를 "특권 포트(Privileged Ports)"라고 부릅니다.
# 일반 사용자로 80 포트 바인딩 시도
$ node server.js --port 80
Error: listen EACCES: permission denied 0.0.0.0:80
# root로 실행하면 성공
$ sudo node server.js --port 80
Server listening on port 80 ✅
이유:
- 보안을 위해 well-known 포트는 root만 사용 가능하도록 제한
- HTTP(80), HTTPS(443), SSH(22), DNS(53) 등이 특권 포트
- 악의적인 프로세스가 중요 포트를 점유하는 것을 방지
Kubernetes에서의 문제
Kong Gateway를 80/443에서 실행하려면?
❌ 옵션 1: root로 Pod 실행
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsUser: 0 # root
containers:
- name: kong
ports:
- containerPort: 80 # 특권 포트
- containerPort: 443
문제점:
- 🚨 보안 위험: Pod가 컨테이너를 탈출하면 노드 전체 장악
- 🚨 Pod Security Standards 위반: Restricted 정책 위반
- 🚨 공격 표면 확대: root 권한으로 실행되는 프로세스는 더 큰 피해 가능
- 🚨 Best Practice 위반: Kubernetes는 non-root 실행을 권장
❌ 옵션 2: CAP_NET_BIND_SERVICE 권한 부여
apiVersion: v1
kind: Pod
spec:
containers:
- name: kong
securityContext:
capabilities:
add:
- NET_BIND_SERVICE # 특권 포트 바인딩 권한
문제점:
- ⚠️ 여전히 권한 상승: 일반 사용자가 특권 포트 사용 가능
- ⚠️ Pod Security Standards: Baseline까지만 허용 (Restricted 불가)
- ⚠️ 제한적: 특권 포트만 해결, 다른 보안 위험은 남음
해결책: NodePort + iptables 포워딩 (hostNetwork 사용 안 함!)
아키텍처
graph TB
Client[외부 클라이언트<br/>curl http://imprun.dev/]
IPTables[iptables PREROUTING<br/>커널 레벨 NAT]
NodePort[NodePort Service<br/>30080/30443]
Pod1[Kong Pod #1<br/>8000/8443<br/>UID: 1000 non-root]
Pod2[Kong Pod #2<br/>8000/8443<br/>UID: 1000 non-root]
Client -->|80/443 요청| IPTables
IPTables -->|80→30080<br/>443→30443| NodePort
NodePort -->|LoadBalancing| Pod1
NodePort -->|LoadBalancing| Pod2
style Client stroke:#2563eb,stroke-width:3px
style IPTables stroke:#ea580c,stroke-width:3px
style NodePort stroke:#0891b2,stroke-width:2px
style Pod1 stroke:#16a34a,stroke-width:2px
style Pod2 stroke:#16a34a,stroke-width:2px
핵심 포인트:
- ✅ iptables: 커널 레벨에서 포워딩 (root 권한 불필요)
- ✅ NodePort: Kubernetes Service로 다중 Pod 관리
- ✅ 다중 Pod: 고가용성 + 무중단 배포
- ✅ non-root: Pod는 UID 1000으로 실행
핵심 차이점: hostNetwork를 사용하지 않습니다!
왜 hostNetwork를 사용하지 않았나?
❌ hostNetwork의 치명적 단점
# hostNetwork: true 사용 시
deployment:
hostNetwork: true
replicas: 1 # 강제로 1개만 가능!
문제점:
노드당 하나의 Pod만 실행 가능
- 같은 노드에 두 개의 Kong Pod 불가능 (포트 충돌)
- 고가용성(HA) 불가능
Rolling Update 불가능 → 서비스 장애!
sequenceDiagram participant K8s as Kubernetes participant OldPod as 기존 Pod<br/>(80/443 점유) participant NewPod as 새 Pod participant User as 사용자 Note over K8s,User: ❌ hostNetwork: Rolling Update 시나리오 K8s->>NewPod: 1. 새 Pod 생성 시도 NewPod--xOldPod: 2. 포트 충돌! (80/443 이미 사용 중) NewPod->>NewPod: 3. Pending 상태 Note over K8s: 4. 기존 Pod를 먼저 삭제해야 함 K8s->>OldPod: 5. 기존 Pod 삭제 User--xOldPod: ⚠️ 다운타임 시작! OldPod->>OldPod: 종료 중... K8s->>NewPod: 6. 새 Pod 시작 가능 NewPod->>NewPod: 시작 중... User--xNewPod: ⚠️ 여전히 연결 불가 NewPod->>NewPod: Ready User->>NewPod: ✅ 연결 가능 (다운타임 종료) Note over K8s,User: 결과: 10-30초 다운타임 발생!재시작 시 다운타임
- Kong 설정 변경, 버전 업그레이드 등
- 무조건 서비스 중단 발생
오토스케일링 불가능
- HPA로 트래픽에 따라 Pod 수 조절 불가
- 항상 단일 Pod 고정
결론: 프로덕션 환경에서 hostNetwork는 위험!
대신 선택한 방법: NodePort + iptables
# Kong Service (NodePort)
apiVersion: v1
kind: Service
metadata:
name: kong-proxy
namespace: kong-system
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 8000
nodePort: 30080 # 노드의 30080 포트로 노출
- name: https
port: 443
targetPort: 8443
nodePort: 30443 # 노드의 30443 포트로 노출
이점:
- ✅ Rolling Update 가능 (무중단 배포)
- ✅ 여러 Pod 실행 가능 (고가용성)
- ✅ 오토스케일링 가능 (HPA 사용)
- ✅ Kubernetes 네이티브 (Service 리소스 사용)
왜 이 방법인가?
✅ 보안
- Kong Pod는 non-root로 실행 (UID 1000)
- 특권 포트 바인딩 권한 불필요
- Pod Security Standards Restricted 준수 가능
- 컨테이너 탈출해도 일반 사용자 권한만 획득
✅ 고가용성
- 여러 Pod 동시 실행 가능 (hostNetwork는 불가능)
- Rolling Update 무중단 배포 (hostNetwork는 다운타임 필수)
- 오토스케일링 지원 (트래픽에 따라 자동 조절)
✅ 성능
- iptables는 커널 레벨에서 동작 (오버헤드 거의 없음)
- NAT는 네트워크 스택에서 처리
- NodePort 자체는 kube-proxy가 최적화
✅ 단순성
- Kong 설정 변경 불필요 (기본 포트 8000/8443 사용)
- 표준 Kubernetes Service 사용
- iptables 규칙만 추가
실전 구현
1단계: Kong Helm Values 설정
# kong-values.yaml
proxy:
enabled: true
type: NodePort # NodePort 사용!
http:
enabled: true
servicePort: 80
containerPort: 8000 # 비특권 포트!
nodePort: 30080 # 노드 포트 지정
tls:
enabled: true
servicePort: 443
containerPort: 8443 # 비특권 포트!
nodePort: 30443 # 노드 포트 지정
# hostNetwork 비활성화 (중요!)
deployment:
hostNetwork: false # Rolling Update 및 HA 지원
# 보안: non-root 실행
securityContext:
runAsUser: 1000 # kong 사용자
runAsNonRoot: true
fsGroup: 1000
# 고가용성 설정
autoscaling:
enabled: true
minReplicas: 2 # 최소 2개 Pod (HA)
maxReplicas: 5 # 최대 5개 Pod
targetCPUUtilizationPercentage: 75
# 환경 변수
env:
# Kong 기본 포트 명시
proxy_listen: "0.0.0.0:8000, 0.0.0.0:8443 ssl"
핵심:
type: NodePort- NodePort Service 사용nodePort: 30080/30443- 노드 포트 지정hostNetwork: false- hostNetwork 사용 안 함!minReplicas: 2- 고가용성을 위해 최소 2개 PodrunAsNonRoot: true- root 실행 방지
2단계: iptables 포워딩 규칙 설정
규칙 생성
#!/bin/bash
# setup-port-forwarding.sh
# HTTP 포워딩: 80 → 30080 (NodePort)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 30080
# HTTPS 포워딩: 443 → 30443 (NodePort)
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 30443
# 규칙 저장 (재부팅 후에도 유지)
iptables-save > /etc/iptables/rules.v4
REDIRECT vs DNAT:
REDIRECT: 로컬 포트로 포워딩 (같은 호스트)DNAT: 다른 IP로 포워딩 (다른 호스트)- 우리 경우: REDIRECT 사용 (80→30080, 443→30443)
systemd 서비스로 자동화
# /etc/systemd/system/kong-port-forward.service
[Unit]
Description=Kong Port Forwarding (80→8000, 443→8443)
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/setup-port-forwarding.sh
ExecStop=/usr/local/bin/cleanup-port-forwarding.sh
[Install]
WantedBy=multi-user.target
활성화:
systemctl daemon-reload
systemctl enable kong-port-forward.service
systemctl start kong-port-forward.service
3단계: 검증
# 1. iptables 규칙 확인
iptables -t nat -L PREROUTING -n -v
# Chain PREROUTING (policy ACCEPT)
# target prot opt source destination
# REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 redir ports 30080
# REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 redir ports 30443
# 2. Kong Service (NodePort) 확인
kubectl get svc -n kong-system kong-kong-proxy
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# kong-kong-proxy NodePort 10.96.123.45 <none> 80:30080/TCP,443:30443/TCP
# 3. Kong Pod 확인 (2개 이상 실행 중!)
kubectl get pod -n kong-system
# NAME READY STATUS RESTARTS AGE
# kong-kong-76fc56677-72xs7 2/2 Running 0 10m
# kong-kong-76fc56677-8jk2l 2/2 Running 0 10m ✅ HA!
# 4. Kong이 8000/8443 포트에서 리스닝 확인
kubectl exec -n kong-system kong-kong-76fc56677-72xs7 -c proxy -- \
netstat -tlnp | grep kong
# tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 1/nginx
# tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 1/nginx
# 5. 외부에서 80/443 포트 접속 테스트
curl -I http://<node-ip>/
# HTTP/1.1 404 Not Found ✅ (Kong 응답)
curl -I https://<node-ip>/
# HTTP/2 404 ✅ (Kong TLS 응답)
# 6. Pod가 non-root로 실행 중인지 확인
kubectl exec -n kong-system kong-kong-76fc56677-72xs7 -c proxy -- id
# uid=1000(kong) gid=1000(kong) groups=1000(kong) ✅
# 7. Rolling Update 테스트 (무중단!)
kubectl set image deployment/kong-kong -n kong-system \
proxy=kong:3.10 ingress-controller=kong/kubernetes-ingress-controller:3.10
# deployment.apps/kong-kong image updated
# Pod가 하나씩 교체됨 (다운타임 없음!)
kubectl rollout status deployment/kong-kong -n kong-system
# Waiting for deployment "kong-kong" rollout to finish: 1 out of 2 new replicas...
# Waiting for deployment "kong-kong" rollout to finish: 1 old replicas...
# deployment "kong-kong" successfully rolled out ✅
다른 선택지와의 비교
옵션 비교표
| 방식 | 보안 | 고가용성 | 무중단배포 | 복잡도 | 권장도 |
|---|---|---|---|---|---|
| root + 특권 포트 | ❌ 매우 낮음 | ✅ | ✅ | ⭐ 매우 쉬움 | ❌ 비권장 |
| CAP_NET_BIND_SERVICE | ⚠️ 낮음 | ✅ | ✅ | ⭐⭐ 쉬움 | ⚠️ 제한적 |
| hostNetwork + iptables | ✅ 높음 | ❌ 단일 Pod | ❌ 다운타임 | ⭐⭐ 쉬움 | ❌ 비권장 |
| NodePort + iptables | ✅ 높음 | ✅ 다중 Pod | ✅ 무중단 | ⭐⭐⭐ 보통 | ✅ 권장 |
| NodePort (단독) | ✅ 높음 | ✅ | ✅ | ⭐⭐ 쉬움 | ⚠️ 30000+ 포트 |
| LoadBalancer (MetalLB) | ✅ 높음 | ✅ | ✅ | ⭐⭐⭐⭐ 복잡 | ✅ 권장 (프로덕션) |
| LoadBalancer (클라우드) | ✅ 높음 | ✅ | ✅ | ⭐⭐ 쉬움 | ✅ 권장 (클라우드) |
왜 NodePort + iptables를 선택했나?
1. 보안 우선
# Pod Security Standards Compliance
apiVersion: v1
kind: Pod
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted ✅
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: kong
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
iptables 방식만 Restricted 정책을 완전히 준수 가능
2. 환경 제약사항
우리 환경:
❌ 클라우드 LoadBalancer 없음
- 온프레미스 환경 (클라우드 아님)
- 클라우드 LB는 가장 쉽지만 월 비용 발생 💰
❌ MetalLB 부적합
- 추가 리소스 필요 (L2 모드: ARP, BGP 모드: 라우터 설정)
- 열악한 노드 환경: 제한된 CPU/메모리
- MetalLB 자체 Pod 리소스 + 네트워크 오버헤드
- 3노드 클러스터에서 추가 부담
✅ NodePort + iptables는 즉시 사용 가능
- 추가 인프라 불필요
- 추가 리소스 소비 없음 (커널 레벨 동작)
- 설정 간단 (iptables 규칙 2개)
3. 고가용성 + 무중단 배포
hostNetwork 방식의 문제:
sequenceDiagram
participant K8s as Kubernetes
participant OldPod as 기존 Pod (80/443)
participant NewPod as 새 Pod
participant User as 사용자
Note over K8s,User: ❌ hostNetwork: 다운타임 발생
K8s->>NewPod: 새 Pod 생성 시도
NewPod--xOldPod: 포트 충돌!
K8s->>OldPod: 기존 Pod 삭제
User--xOldPod: ⚠️ 다운타임!
K8s->>NewPod: 새 Pod 시작
User--xNewPod: ⚠️ 여전히 불가
NewPod->>User: ✅ Ready (10-30초 후)
NodePort 방식의 해결:
sequenceDiagram
participant K8s as Kubernetes
participant Pod1 as Pod #1 (기존)
participant Pod2 as Pod #2 (기존)
participant Pod3 as Pod #3 (신규)
participant Service as NodePort Service
participant User as 사용자
Note over K8s,User: ✅ NodePort: 무중단 Rolling Update
User->>Service: 계속 요청
Service->>Pod1: 트래픽 분산
Service->>Pod2: 트래픽 분산
K8s->>Pod3: 1. 새 Pod 생성 (포트 충돌 없음!)
Pod3->>Pod3: Ready
Service->>Pod3: 트래픽 분산 시작
Note over Service: Pod1, Pod2, Pod3 모두 서빙
K8s->>Pod1: 2. 기존 Pod #1 삭제
Service->>Pod2: 트래픽 분산
Service->>Pod3: 트래픽 분산
Note over Service: Pod2, Pod3가 서빙 (다운타임 없음!)
K8s->>Pod1: Pod #4 생성 (Pod #2 교체)
Note over K8s,User: 결과: 다운타임 0초!
장점:
- ✅ 다중 Pod 실행 (최소 2개로 HA 구성)
- ✅ Rolling Update (다운타임 0초)
- ✅ HPA 지원 (트래픽 급증 시 자동 스케일)
4. 성능
Client → 80 포트 요청
↓ (iptables NAT, 커널 레벨)
30080 포트로 포워딩
↓ (kube-proxy, iptables 또는 IPVS)
Kong Service (LoadBalancing)
↓
Kong Pod (2개 이상)홉 수: 1-2 (iptables + kube-proxy)
비교:
- NodePort + iptables: 1-2 홉 (우리 선택)
- LoadBalancer: 1-2 홉 (동일, 하지만 비용 or 리소스)
- hostNetwork: 0 홉 (하지만 HA 불가능!)
Trade-off: 1-2 홉 추가되지만, HA와 무중단 배포 얻음
5. 단순성
필요한 것:
- NodePort Service (Helm values)
- iptables 규칙 2개 (80→30080, 443→30443)
- systemd 서비스 1개 (자동화)
불필요한 것:
- ❌ MetalLB 설치 및 설정
- ❌ 클라우드 LoadBalancer 비용
- ❌ 복잡한 네트워크 설정
상세 설명: iptables NAT 동작 원리
NAT (Network Address Translation) 테이블
# NAT 테이블의 체인 구조
iptables -t nat -L -n -v
# Chain PREROUTING (패킷이 라우팅되기 전)
# Chain INPUT (로컬 프로세스로 향하는 패킷)
# Chain OUTPUT (로컬 프로세스에서 나가는 패킷)
# Chain POSTROUTING (패킷이 라우팅된 후)
우리가 사용하는 체인: PREROUTING
패킷 처리 흐름
1. 외부 클라이언트: curl http://node-ip/
↓
2. 패킷 도착: dst=node-ip:80
↓
3. PREROUTING 체인 진입
↓
4. iptables 규칙 매칭:
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
↓
5. 목적지 포트 변경: dst=node-ip:8000
↓
6. 라우팅 결정: 로컬 프로세스 (Kong)
↓
7. Kong (8000 포트 리스닝) 처리
↓
8. 응답: src=node-ip:80 (원래 포트로!)핵심: 클라이언트는 80 포트로 요청했다고 인식, 서버는 8000 포트에서 처리
REDIRECT vs DNAT 상세 비교
# REDIRECT: 로컬 포워딩 (우리가 사용)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
# 효과: 같은 호스트의 8000 포트로 포워딩
# DNAT: 원격 포워딩
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.1.100:8000
# 효과: 다른 서버(10.0.1.100)의 8000 포트로 포워딩
REDIRECT 장점:
- ✅ 간결한 문법 (포트만 지정)
- ✅ 로컬 최적화 (커널이 빠른 경로 사용)
- ✅ IP 변경 불필요 (호스트 IP 유지)
트러블슈팅
Case 1: iptables 규칙이 적용 안 됨
증상:
curl http://node-ip/
# Connection refused
확인:
# 1. iptables 규칙 존재 여부
iptables -t nat -L PREROUTING -n
# 규칙이 없음?
# 2. 규칙 수동 추가
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
# 3. 테스트
curl http://node-ip/
# 성공!
영구 적용:
# Ubuntu/Debian
iptables-save > /etc/iptables/rules.v4
systemctl enable netfilter-persistent
# CentOS/RHEL
iptables-save > /etc/sysconfig/iptables
systemctl enable iptables
Case 2: Kong이 8000 포트에서 리스닝 안 함
증상:
netstat -tlnp | grep 8000
# (아무것도 없음)
원인:
- Kong 설정에서
proxy_listen잘못 설정 - Kong Pod가 시작 안 됨
해결:
# Kong Helm values
env:
proxy_listen: "0.0.0.0:8000, 0.0.0.0:8443 ssl"
# 또는
KONG_PROXY_LISTEN: "0.0.0.0:8000, 0.0.0.0:8443 ssl"
Case 3: hostNetwork 포트 충돌
증상:
kubectl get pod -n kong-system
# NAME READY STATUS RESTARTS AGE
# kong-kong-xxx 0/2 Pending 0 5m
kubectl describe pod -n kong-system kong-kong-xxx
# Events:
# Warning FailedScheduling didn't have free ports for the requested pod ports
원인:
- hostNetwork 사용 시 노드당 하나의 Pod만 가능
- 다른 프로세스가 8000/8443 포트 점유 중
해결:
# 1. 포트 점유 확인
netstat -tlnp | grep -E ':(8000|8443)'
# tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 12345/some-process
# 2. 점유 프로세스 종료
kill 12345
# 3. Pod 재시작
kubectl delete pod -n kong-system kong-kong-xxx
Case 4: HTTPS가 작동 안 함
증상:
curl https://node-ip/
# curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
원인:
- iptables 규칙에 HTTPS(443) 누락
- Kong이 8443에서 TLS 리스닝 안 함
해결:
# 1. HTTPS 포워딩 규칙 추가
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
# 2. Kong TLS 설정 확인
kubectl exec -n kong-system kong-kong-xxx -c proxy -- \
kong config | grep ssl
# ssl_cert = /path/to/cert.pem
# ssl_cert_key = /path/to/key.pem
운영 고려사항
모니터링
# 1. iptables 규칙 통계
watch -n 1 'iptables -t nat -L PREROUTING -n -v'
# Chain PREROUTING (policy ACCEPT 12345 packets, 678K bytes)
# pkts bytes target prot opt in out source destination
# 5678 234K REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
# 2. Kong 연결 수 모니터링
netstat -an | grep :8000 | grep ESTABLISHED | wc -l
# 3. Kong 메트릭 (Prometheus)
curl http://localhost:8001/metrics
# kong_http_requests_total
# kong_latency_ms
백업 및 복구
# 규칙 백업
iptables-save > /backup/iptables-$(date +%Y%m%d).rules
# 규칙 복구
iptables-restore < /backup/iptables-20251027.rules
# 모든 NAT 규칙 제거 (위험!)
iptables -t nat -F PREROUTING
# 재적용
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
보안 강화
# 1. 특정 인터페이스만 허용
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8000
# 2. 특정 소스 IP만 허용
iptables -t nat -A PREROUTING -s 10.0.0.0/8 -p tcp --dport 80 -j REDIRECT --to-port 8000
# 3. Rate limiting (연결 수 제한)
iptables -A INPUT -p tcp --dport 8000 -m connlimit --connlimit-above 100 -j REJECT
# 4. DDoS 방어 (SYN flood)
iptables -A INPUT -p tcp --syn --dport 8000 -m limit --limit 10/s --limit-burst 20 -j ACCEPT
iptables -A INPUT -p tcp --syn --dport 8000 -j DROP
향후 개선: MetalLB로 전환
현재 (NodePort + iptables) vs 미래 (MetalLB)
| 항목 | NodePort + iptables | MetalLB |
|---|---|---|
| 설정 복잡도 | 낮음 (NodePort + iptables 2개 규칙) | 중간 (MetalLB 설치 + IP 풀 설정) |
| 고가용성 | ✅ 가능 (다중 Pod) | ✅ 가능 (다중 노드 LB) |
| Rolling Update | ✅ 무중단 | ✅ 무중단 |
| 오토스케일링 | ✅ 가능 (HPA) | ✅ 가능 (HPA) |
| Kubernetes 네이티브 | ⚠️ 부분적 (NodePort + OS 레벨 iptables) | ✅ 완전 (LoadBalancer) |
| 포트 관리 | ⚠️ iptables 수동 관리 필요 | ✅ 자동 (LoadBalancer) |
| 리소스 오버헤드 | ✅ 없음 (커널 레벨) | ⚠️ MetalLB Pod 리소스 필요 |
MetalLB 전환 계획
# 1. MetalLB 설치
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.0/config/manifests/metallb-native.yaml
# 2. IP 주소 풀 설정
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: production
namespace: metallb-system
spec:
addresses:
- 192.168.1.100-192.168.1.110
# 3. L2 Advertisement
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
# 4. Kong Service를 LoadBalancer로 변경 (NodePort에서 전환)
proxy:
type: LoadBalancer
externalTrafficPolicy: Local
# 5. iptables 규칙 제거 (더 이상 불필요)
iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 30080
iptables -t nat -D PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 30443
iptables-save > /etc/iptables/rules.v4
전환 시기:
- ✅ Kubernetes 네이티브 관리 필요 시 (iptables 수동 관리 제거)
- ✅ 노드 환경 개선 후 (리소스 여유 확보)
- ✅ 다중 노드 로드밸런싱 필요 시 (현재는 단일 노드 iptables)
- ✅ 인프라 복잡도 감수 가능 시 (MetalLB 설치 및 운영)
현재 NodePort + iptables를 유지하는 이유:
- ✅ 이미 고가용성 확보 (다중 Pod)
- ✅ 이미 무중단 배포 가능 (Rolling Update)
- ✅ 이미 오토스케일링 가능 (HPA)
- ✅ 추가 리소스 불필요 (열악한 노드 환경)
마무리
핵심 요약
- 특권 포트 문제: Linux 1024 미만 포트는 root만 바인딩 가능
- 보안 우선: root 실행은 큰 보안 위험, non-root 필수
- NodePort + iptables: NodePort(30080/30443) + 커널 레벨 iptables 포워딩 (80→30080, 443→30443)
- 비교 우위: 보안(non-root) + 고가용성(다중 Pod) + 무중단 배포 + 단순성(2개 규칙)
- 실무 선택: 제한된 리소스 환경에서 추가 인프라(MetalLB/Cloud LB) 없이 즉시 사용 가능
언제 사용하나?
NodePort + iptables 포워딩 권장:
- ✅ 온프레미스 환경
- ✅ 제한된 리소스 환경 (MetalLB 부담)
- ✅ 추가 인프라 비용 회피 (Cloud LB 비용 X)
- ✅ 즉시 배포 필요 (추가 설치 불필요)
- ✅ 보안 중시 (non-root + Restricted Pod Security)
- ✅ 고가용성 필요 (다중 Pod 지원)
- ✅ 무중단 배포 필요 (Rolling Update)
MetalLB/LoadBalancer 권장:
- ✅ Kubernetes 네이티브 관리 선호 (iptables 수동 관리 제거)
- ✅ 다중 노드 로드밸런싱 필요
- ✅ 리소스 여유 있는 환경
- ✅ 인프라 복잡도 감수 가능
- ✅ 클라우드 환경 (Cloud LB 사용 가능)
NodePort 권장:
- ✅ 간단한 테스트
- ✅ 포트 범위 30000-32767 허용
- ✅ 임시 환경
실제 적용 결과
imprun.dev 환경:
- ✅ Kong non-root 실행 (UID 1000)
- ✅ Pod Security Standards Restricted 준수
- ✅ NodePort Service + iptables 2개 규칙으로 80/443 포워딩
- ✅ 고가용성 확보 (최소 2개 Pod, HPA로 자동 스케일)
- ✅ Rolling Update 무중단 배포 (Kong 업데이트 시 다운타임 0초)
- ✅ 추가 비용 0원 (MetalLB/Cloud LB 불필요)
- ✅ 추가 리소스 소비 없음 (커널 레벨 iptables)
- ✅ 성능 오버헤드 무시할 수준 (<1%)
운영 경험:
- 설정 시간: 15분 (NodePort 설정 + iptables 규칙 2개)
- 재부팅 테스트: 정상 (systemd 자동 적용)
- Rolling Update: 무중단 (Pod 교체 시 다운타임 없음)
- 트러블슈팅: 거의 없음
- 만족도: 매우 높음 😊
참고 자료
공식 문서
관련 글
실습 자료
태그: #Kubernetes #Security #iptables #Kong #Networking #DevOps
"보안과 고가용성을 모두 잡는 방법: NodePort + iptables 포워딩"
🤖 이 블로그는 실제 프로덕션 환경에서 NodePort + iptables 포워딩을 운영한 경험을 바탕으로 작성되었습니다.
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
| Monaco Editor "TextModel got disposed" 에러 완벽 해결 가이드 (0) | 2025.10.27 |
|---|---|
| Kubernetes Gateway API 실전 가이드: Kong Ingress에서 표준 API로 전환하기 (1) | 2025.10.27 |
| Kubernetes Gateway API 실전 가이드: Kong Ingress에서 표준 API로 전환하기 (0) | 2025.10.27 |
| frontend/CLAUDE.md (0) | 2025.10.27 |
| Claude AI와 함께하는 프론트엔드 개발: imprun.dev의 CLAUDE.md 가이드 공개 (0) | 2025.10.27 |
- Total
- Today
- Yesterday
- Go
- security
- Claude
- api gateway
- workflow
- AI
- Kubernetes
- LLM
- 개발 도구
- claude code
- Developer Tools
- PYTHON
- react
- knowledge graph
- SHACL
- authentication
- authorization
- AI Development
- backend
- Tax Analysis
- Rag
- Ontology
- Tailwind CSS
- ai 개발 도구
- troubleshooting
- architecture
- frontend
- LangChain
- Next.js
- AI agent
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |