ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Kubernetes에서 특권 포트 피하기: NodePort + iptables 포워딩 패턴
    실제 경험과 인사이트를 AI와 함께 정리한 글 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 타입이 정석인데, 우리 환경에서 가능할까?

    검증 과정:

    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 포워딩을 운영한 경험을 바탕으로 작성되었습니다.

Designed by Tistory.