티스토리 뷰

작성일: 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 타입이 정석인데, 우리 환경에서 가능할까?

검증 과정:

  1. root + 특권 포트 (80/443)

    • ✅ 간단하지만 Pod Security Standards: Restricted 위반
    • ❌ 컨테이너 탈출 시 노드 전체 장악 가능
  2. hostNetwork + iptables

    • ✅ non-root로 실행 가능
    • 노드당 1개 Pod만 가능 → 고가용성 불가
    • Rolling Update 시 다운타임 발생 → 서비스 중단
  3. NodePort + iptables최종 선택

    • non-root 실행 (UID 1000, 비특권 포트 8000/8443)
    • 다중 Pod 실행 (최소 2개, HPA로 자동 스케일)
    • Rolling Update 무중단 배포
    • 추가 인프라 불필요 (MetalLB/Cloud LB 비용 0원)
  4. 환경 제약사항

    • 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개만 가능!

문제점:

  1. 노드당 하나의 Pod만 실행 가능

    • 같은 노드에 두 개의 Kong Pod 불가능 (포트 충돌)
    • 고가용성(HA) 불가능
  2. 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초 다운타임 발생!
  3. 재시작 시 다운타임

    • Kong 설정 변경, 버전 업그레이드 등
    • 무조건 서비스 중단 발생
  4. 오토스케일링 불가능

    • 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 포트로 노출

이점:

  1. Rolling Update 가능 (무중단 배포)
  2. 여러 Pod 실행 가능 (고가용성)
  3. 오토스케일링 가능 (HPA 사용)
  4. 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개 Pod
  • runAsNonRoot: 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. 단순성

필요한 것:

  1. NodePort Service (Helm values)
  2. iptables 규칙 2개 (80→30080, 443→30443)
  3. 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)
  • ✅ 추가 리소스 불필요 (열악한 노드 환경)

마무리

핵심 요약

  1. 특권 포트 문제: Linux 1024 미만 포트는 root만 바인딩 가능
  2. 보안 우선: root 실행은 큰 보안 위험, non-root 필수
  3. NodePort + iptables: NodePort(30080/30443) + 커널 레벨 iptables 포워딩 (80→30080, 443→30443)
  4. 비교 우위: 보안(non-root) + 고가용성(다중 Pod) + 무중단 배포 + 단순성(2개 규칙)
  5. 실무 선택: 제한된 리소스 환경에서 추가 인프라(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 포워딩을 운영한 경험을 바탕으로 작성되었습니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/02   »
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
글 보관함