본문 바로가기

DevOps

[AWS EKS] (5) EKS 스터디 2주차 ( iptables - CNI HOST PORT CHAIN )

CloudNet@팀의 EKS 스터디 AEWS 2기에 작성된 자료를 토대로 작성합니다.

 

이번 포스팅에서는  iptables 사용시 이슈사항을 소개해보겠습니다.

운영환경의 경험을 기반으로 풀어보는 시간을 가지도록 하겠습니다.

 

✅  iptables 란 ? 

우선 문서에서 보는것처럼 iptables의 정의를 나열하지 않겠습니다.

정말 쉽게 풀어서 이야기 드려도 여러번 읽어보셔야 자기것이 됩니다.

자 이제 iptables에 대해서 이야기 해보죠.

 

 

다음과 같은 그림을 먼저 봐주세요.

netfilter hook ( 후크 선장 아시죠? ) 입니다.

iptables는 방화벽 소프트웨어고 5개의 후크를 가지고 있습니다. 

 

netfilter hook에 (firewall 관련한) kernel modules들을 등록해두고 traffic이 지나갈때 검사를 합니다.

이 모듈들 이름이 nat, mangle, raw ,filter , security 입니다.

 

 

✅  Hook ? 

무슨말인지 하나도 모르시겠죠 ....

조금더 구체화 해보겠습니다.

 

5개의 후크는 VM에서 간단한 명령어를 한번 쳐보면서 알아볼게요

단어 그대로 이해 해보죠 어렵지 않습니다.

  • INPUT 노드로 들어오는 트래픽에 대한 트리거
  • OUTPUT 노드에서 나가는 트래픽에 대한 트리거
  • FORWARD는 다른 노드로 넘어가는 트래픽에 대한 트리거
  • PREROUTING 라우팅 전에 트리거
  • POSTROUTING 라우팅 하고 트리거 
             +================+      +=====================+
             | hostNetwork IP |      | hostNetwork process |
             +================+      +=====================+
                         ^                |
  -  -  -  -  -  -  -  - | -  -  -  -  - [*] -  -  -  -  -  -  -  -  -
                         |                v
                     +-------+        +--------+
                     | input |        | output |
                     +-------+        +--------+
                         ^                |
      +------------+     |   +---------+  v      +-------------+
      | prerouting |-[*]-+-->| forward |--+-[*]->| postrouting |
      +------------+         +---------+         +-------------+
            ^                                           |
 -  -  -  - | -  -  -  -  -  -  -  -  -  -  -  -  -  -  |  -  -  -  -
            |                                           v
       +---------+                                  +--------+
   --->| ingress |                                  | egress |--->
       +---------+                                  +--------+

iptables -t nat -S

# 모든 룰이 출력됩니다. 그중에 제일 위에 4개만 보죠 
# 5개의 후크중에 4개 


[root@ip-192-168-3-215 ~]#
[root@ip-192-168-3-215 ~]# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
중략 


[root@ip-192-168-3-215 modules-load.d]# iptables -t filter -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N KUBE-EXTERNAL-SERVICES
-N KUBE-FIREWALL
-N KUBE-FORWARD
-N KUBE-KUBELET-CANARY
-N KUBE-NODEPORTS
-N KUBE-PROXY-CANARY
-N KUBE-PROXY-FIREWALL
-N KUBE-SERVICES
-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL
-A INPUT -m comment --comment "kubernetes health check service ports" -j KUBE-NODEPORTS
-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES
-A INPUT -j KUBE-FIREWALL
-A INPUT -p udp -m udp --dport 9999 -m limit --limit 5/min -j LOG --log-prefix "UDP_DROP: "
-A INPUT -p udp -m udp --dport 9999 -j DROP
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL
-A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A FORWARD -m conntrack --ctstate NEW -m comment --comment "kubernetes externally-visible service portals" -j KUBE-EXTERNAL-SERVICES
-A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL
-A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -j KUBE-FIREWALL
-A KUBE-FIREWALL ! -s 127.0.0.0/8 -d 127.0.0.0/8 -m comment --comment "block incoming localnet connections" -m conntrack ! --ctstate RELATED,ESTABLISHED,DNAT -j DROP
-A KUBE-FORWARD -m conntrack --ctstate INVALID -m nfacct --nfacct-name  ct_state_invalid_dropped_pkts -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding conntrack rule" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
[root@ip-192-168-3-215 modules-load.d]#

netfilter hooks

  1. NF_IP_PRE_ROUTING : incoming 트래픽을 라우팅 하기 전에 트리거
  2. NF_IP_LOCAL_IN : incoming 패킷의 목적지가 “로컬” 라우팅 이후 트리거
  3. NF_IP_FORWARD : incoming 패킷이 다른 호스트로 포워딩되는 경우로 해당 호스트로 라우팅 이후 트리거
  4. NF_IP_LOCAL_OUT : 로컬에서 생성된 Outbound 트래픽에 의해 트리거
  5. NF_IP_POST_ROUTING : 라우팅 이후 Outbound or 포워딩 트래픽에 의해 트리거



✅  Tables 과 Chain 

후크에 있는 트리거가 발동되면 해당 커널 netfilter hook에 있던 모듈이 실행 됩니다. 

어떤 함수가 동작하는거죠.

다음으론 , 두가지 개념을 익혀두어야 하는데요 Tables과 Chain입니다.

 

☑️ tables (define general aim of the rules)

테이블은 = 어떤 결정을 하느냐 ? 

NAT? NAT 패킷의 src,dst address의 수정/방법 여부를 결정.

Filter? 필터링. 패킷의 전송 여부를 결정

mangle

raw

security

목표:

Known 이슈사항 정리

istio iptables environment, xtables parameter problem: iptables-restore: unable to initialize table 'nat' issue # 44118

 

- istio 설치시 워커노드에 iptable 모듈이 없는 경우 발생

# iptable과 nf_table의 차이점 : 

 iptables는 기존 Netfilter 기반의 패킷 필터링 및 NAT 프레임워크로, istio 와 같은 서비스 메시의 트래픽 리다이렉션을 위해 이를 적극적으로 활용합니다.

[root@ip-192-168-3-215 modules-load.d]# lsmod
Module                  Size  Used by

nft_chain_nat          16384  7
nf_nat                 57344  3 xt_nat,nft_chain_nat,xt_MASQUERADE
nfnetlink_acct         16384  4 xt_nfacct
xt_conntrack           16384  19
nf_conntrack          184320  7 xt_conntrack,nf_nat,xt_state,xt_nat,nf_conntrack_netlink,xt_connmark,xt_MASQUERADE
nf_defrag_ipv6         24576  1 nf_conntrack
nf_defrag_ipv4         16384  1 nf_conntrack
xt_comment             16384  96
nft_compat             20480  169
nf_tables             311296  354 nft_compat,nft_chain_nat,nft_limit
nfnetlink              20480  8 nft_compat,nfnetlink_acct,nf_conntrack_netlink,nf_tables

 

  • 모듈 설치 /etc/modules-load.d
$ cd /etc/modules-load.d/

$ vi istio-iptables.conf
iptable_nat
iptable_mangle
iptable_filter
iptable_raw

$ modprobe iptable_nat
$ modprobe iptable_mangle
$ modprobe iptable_filter
$ modprobe iptable_raw
  • 모듈 확인
[root@ip-192-168-3-215 modules-load.d]# lsmod
Module                  Size  Used by
iptable_raw            16384  0
iptable_filter         16384  0
iptable_mangle         16384  0
iptable_nat            16384  0

 

☑️ chains (determine when rules will be evaluated)

체인은 = hook 안에서 또 언제 트리거 되고 어떠한 위치에서 적용되는지 

언제 트리거가 되고 방화벽 룰이 어떠한 위치에서 적용이 되는지에 대한 것이에요. 

 

 

K8s service Network ( iptables)

https://velog.io/@gyomang/EKS-Networking-Service%EC%9D%98-iptables-Ingress

해당 그림이 잘표현되어 있어서 가져와봤습니다. 감사합니다. ^^

 

✅  AWS iptables 정책 적용 순서 

 iptables 정책 적용 순서 : PREROUTING → KUBE-SERVICES → KUBE-SVC-### → KUBE-SEP-#<파드1> , KUBE-SEP-#<파드2> , KUBE-SEP-#<파드3>

# PREROUTING
[root@ip-192-168-3-215 ~]# iptables -v --numeric --table nat --list PREROUTING | column -t
Chain  PREROUTING  (policy               ACCEPT  0    packets,  0    bytes)
pkts   bytes       target                prot    opt  in        out  source     destination
36909  3519K       KUBE-SERVICES         all     --   *         *    0.0.0.0/0  0.0.0.0/0    /*  kubernetes  service   portals      */
16     960         AWS-CONNMARK-CHAIN-0  all     --   eni+      *    0.0.0.0/0  0.0.0.0/0    /*  AWS,        outbound  connections  */
26383  2892K       CONNMARK              all     --   *         *    0.0.0.0/0  0.0.0.0/0    /*  AWS,        CONNMARK  */           CONNMARK  restore  mask  0x80
# KUBE-SERVICES
[root@ip-192-168-3-215 ~]# iptables -v --numeric --table nat --list KUBE-SERVICES | column
Chain KUBE-SERVICES (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 KUBE-SVC-ERIFXISQEP7F7OF4  tcp  --  *      *       0.0.0.0/0            10.100.0.10          /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
    0     0 KUBE-SVC-JD5MR3NA4I4DYORP  tcp  --  *      *       0.0.0.0/0            10.100.0.10          /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
    0     0 KUBE-SVC-Z4ANX4WAEWEBLCTM  tcp  --  *      *       0.0.0.0/0            10.100.254.40        /* kube-system/metrics-server:https cluster IP */ tcp dpt:443
    0     0 KUBE-SVC-NPX46M4PTMTKRN6Y  tcp  --  *      *       0.0.0.0/0            10.100.0.1           /* default/kubernetes:https cluster IP */ tcp dpt:443
    0     0 KUBE-SVC-I7SKRZYQ7PWYV5X7  tcp  --  *      *       0.0.0.0/0            10.100.227.12        /* kube-system/eks-extension-metrics-api:metrics-api cluster IP */ tcp dpt:443
    0     0 KUBE-SVC-7EJNTS7AENER2WX5  tcp  --  *      *       0.0.0.0/0            10.100.153.164       /* kube-system/kube-ops-view:http cluster IP */ tcp dpt:8080
    0     0 KUBE-SVC-UAGC4PYEYZJJEW6D  tcp  --  *      *       0.0.0.0/0            10.100.79.8          /* kube-system/aws-load-balancer-webhook-service:webhook-server cluster IP */ tcp dpt:443
    0     0 KUBE-SVC-TCOU7JCQXEZGVUNU  udp  --  *      *       0.0.0.0/0            10.100.0.10          /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
 1120 61085 KUBE-NODEPORTS  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
#KUBE-SVC-XXX
[root@ip-192-168-3-215 ~]# iptables -v --numeric --table nat --list KUBE-SVC-TCOU7JCQXEZGVUNU | column
Chain KUBE-SVC-TCOU7JCQXEZGVUNU (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 KUBE-SEP-4JJWD7DPDSPAPXRS  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kube-system/kube-dns:dns -> 192.168.1.77:53 */ statistic mode random probability 0.50000000000
    0     0 KUBE-SEP-CMUTYLZJRO6PLWPI  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kube-system/kube-dns:dns -> 192.168.2.64:53 */
[root@ip-192-168-3-215 ~]# iptables -v --numeric --table nat --list KUBE-SEP-4JJWD7DPDSPAPXRS | column
#KUBE-SEP-XXX
Chain KUBE-SEP-4JJWD7DPDSPAPXRS (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 KUBE-MARK-MASQ  all  --  *      *       192.168.1.77         0.0.0.0/0            /* kube-system/kube-dns:dns */
    0     0 DNAT       udp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kube-system/kube-dns:dns */ udp to:192.168.1.77:53
[root@ip-192-168-3-215 ~]#

이슈사항 정리 ( ingress-controller-pod)

Dockershim 사용 종료에 따른 Known issue (SideEffect)

  • 프라이빗 클라우드에서 ingress-controller 사용시 PREROUTING을 보면 다음과 같다.
[root@mzctest-w-worker1 ~]# iptables -t nat -L PREROUTING -nv
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  42M 5365M cali-PREROUTING  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:6gwbT8clXdHdC1b1 */
  42M 5365M KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
  13M 2085M DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
  13M 2085M CNI-HOSTPORT-DNAT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
  • PREROUTING 체인의 타겟에 CNI-HOSTPORT-DNAT 라는 타겟이 있는데 해당 타겟의 조건은 로컬 노드의 IP로 향하는 트래픽을 처리 한다.
  • CNI-HOSTPORT-DNAT 일반적으로 CNI 플러그인이 관리하는 HostPort 기능을 처리하는 역할을 한다.
  • 즉, Pod가 특정 호스트의 포트를 바인딩 했을때, 해당 트래픽을 해당 Pod로 DNAT(목적지 변경) 처리 하는것이다.
  • k8s 버전 업그레이드 , 드레인 , etcd 롤백 간에 해당 체인의 타겟에 대한 삭제와 재생성이 제대로 이루어지지 않는 경우 ingress 서비스 통신 장애를 일으킨다.
[root@mzctest-w-worker1 ~]# iptables -t nat -L PREROUTING -nv
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  42M 5365M cali-PREROUTING  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:6gwbT8clXdHdC1b1 */
  42M 5365M KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
  13M 2085M DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
  13M 2085M CNI-HOSTPORT-DNAT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
  #### 중복
  0   0 CNI-HOSTPORT-DNAT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
  • HOSTPORT 파드에 대한 메타데이터 마운트 위치 확인 
  • k8s version 1.23.6까지는 HOSTPORT로 생성된 파드들의 메타데이터가 /var/lib/dockershim/sandbox 내부에존재함 
  • 해당 파일을 지우고 HOSTPORT 파드를 재기동 하면 다음과 같이 Duplicate 된다.

해결방법

  • 쿠버네티스 버전 1.24 이상에서는 없는 이슈임 ( 1.23에서 1.24로 올릴때만 주의)
  • kubelet의 /var/lib/dockershim 경로를 VM에 매핑시켜주면 해결됨 
services:
  kubelet:
    extra_binds:
      -"/var/lib/dockershim:/var/lib/dockershim"
  • 엔진에 따라 kubelet 설정을 수정하는 config들 위치가 다를수 있음. 
  • 이미 1.23.6에서 HOSTPORT iptables 중복이 발생한 상태에서 진행
  • kubelet 컨테이너 내부의 Pod metadata 를 삭제하고 nginx-ingress-controller를 재기동 하면, 1.23.6에서 중복이 발생한 규칙을 제외하고는 정상 상태 유지
  • 1.23.6에서 중복이 발생한 규칙을 없애기 위해서는 iptables 명령어를 통해 해당 규칙을 제거하거나 노드 재기동하여 flush