이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Kubernetes

75. kubernetes LoadBalancer Networking 분석 (kube-proxy : iptable mode)

이쿠우우 2021. 5. 6. 15:06
반응형

 

 

 

 

 

kubernetes LoadBalancer Networking 분석

kube-proxy = iptable mode

CNI = Flannel

 

 

 


[kubernetes kube-proxy 관련 글 목록]

54. Kubernetes kube-proxy Mode 분석

70. Kubernetes kube-proxy IPVS Mode 설정

74. Kubernetes NodePort Networking 분석 (kube-proxy : iptable mode)

75. kubernetes LoadBalancer Networking 분석 (kube-proxy : iptable mode)

76. kubernetes NodePort Networking 분석 (kube-proxy : IPVS mode)

 


환경

 

[Master Node server]

OS = CentOS 7

리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

docker version : 1.13.1

docker api verison : 1.26

 

 

[Worker Node server]

OS = CentOS 7

리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

docker version : 1.13.1

api verison : 1.26

 

 

 

 

[kubernetes version]

1.19.2

 

 

 

[Test Pod 정보]

1개의 Pod 안에 tomcat 과 mysql container 2개를 생성

 

Pod는 worker node에 배포되어 있음

tomcat container port = 8080

mysql container port = 3306

 

 

 


 

 

tcpdump 

External -> LoadBalancer IP -> Container  

 

 

 

 

Pod는 Worker Node에 배포되어있는 상태.

 

 

 

 

환경

Tomcat Pod IP  = 10.244.1.160

Service Object Type = LoadBalancer 

LoadBalancer IP = 10.0.2.10

LoadBalancer NodePort = 31857

Tomcat Pod 배포 Node = Worker Node

Tomcat Pod Port = 8080

Master Node IP = 10.0.2.15

Worker Node IP = 10.0.2.4

 

이외의 정보는 문서 상단위 "환경"에서 설명한 정보와 동일

 

external 환경에서 

curl [LoadBalancer IP]:8080

명령으로 Pod에 접근

 

 

LoadBalancer Routing 

 

 

 

 

 

1. Worker Node : enp0s3 

 

 

VirtualBox의 경우 : enp0s3

VMware 의 경우 : ens192

에 해당하는 인터페이스임,

 

[명령어]

tcpdump -i enp0s3 -ne  dst 10.0.2.10  -vv -c 10

 

[MAC 정보]
출발지 : 52:54:00:12:35:00  (virtualbox router mac)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 192.168.56.1  (external에서 접근한 ip주소)
도착지 : 10.0.2.10 (LoadBalancer의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (LoadBalancer Service Object에 설정된 Port 8080)

( 192.168.56.1, 52:54:00:12:35:00 )

external에서 접근한 ip주소와

vritualBox 의 Router Mac 주소

 

test환경은 virtualbox 환경이라 해당 ip로 접근함.

virtualbox 에서 포트포워딩 설정되어있음.

 

 

 

 

패킷 흐름 분석

해당 인터페이스의 내용은 virtualBox 포트포워딩에 관한 내용.

 

 

 

 

2. Worker Node : kube-proxy

 

 

LoadBalancer Type의 Service Object에 할당된 NodePort는 31857임으로 

lsof 명령으로 31857 port를 사용하는 프로세스를 확인

 

[명령어]

lsof -i:31857

 

[netstat 명령]

netstat -nap | grep kube-proxy

 

 

[확인 결과]

kube-proxy pod가 모든 NodePort 수신하고 있음.

NodePort Type의 service object를 생성하면

kube-proxy가 모든 NodePort를 Port 예약하고 

다른 프로세스가 사용할 수 없도록 함. 

즉 test환경에서는

31857 port를 kube-proxy가 점유하고 있음.

 

 

2.1. kube-proxy Mode 확인

 

Kube-proxy는 user space, iptables, ipvs 와 같은 mode로 동작할 수 있음으로

어떤 mode로 동작하는지 확인해야함.

 

[kube-proxy mode 확인 명령어]

kubectl logs -f [베포되어있는 kube-proxy pod 이름] -n kube-system

예) kubectl logs -f pod/kube-proxy-8fc7f -n kube-system

 

[kube-porxy mode 확인 결과]

아래 log 내용을 보아

default mode의 경우 : iptables

test 환경의 kube-proxy의 경우 default 설정을 사용했음으로 

iptables mode로 동작함.

 

[kube-proxy iptable Mode란?]

kube-proxy의 iptables Mode는 

netfilter를 사용해서 Chain Rule이라는 규칙을 지정한 뒤

패킷이 Chain Rule에 매칭되면 지정한 규칙에 따라서 

패킷을 포워딩 함.

 

 

 

 

 

2.2. kube-proxy Netfiler (iptables) 도식화

 

 

 

 

 

2.3. kube-proxy Netfiler (iptables) 분석 

 

2.3.1. 모든 패킷은 kube-proxy가 생성한 Netfiler를 거쳐야함.

 

[명령어]

iptables -t nat -L OUTPUT

 

[명령어]

iptables -t nat -L PREROUTING | column -t

 

[분석]

kube-proxy에 의해 생성된 KUBE-SERVICES Chain을 확인할 수 있음.

 

참고) PREROUTING Chain은 raw와 mangle에도 존재하는데 kube-proxy는 nat에만 규칙을 생성함.

 

 

[패킷 흐름]

enp0s3 인터페이스로 전달된 모든 패킷은

Host의 Network Namespace로 전달되고

해당 패킷은 OUTPUT Rule 중 

src = anywhere

dst = anywhere 

Rule에 매칭되어 

kube-proxy가 생성한 KUBE-SERVICES Chain Rule에 적용되기 시작함.

 

 

 

2.3.2. KUBE-SERVICES Chain의 Rule을 확인

 

[명령어]

iptables -t nat -L KUBE-SERVICES -n  | column -t

 

 

[패킷 흐름]

패킷은 PREROUTING Chain Rule에 의해 

KUBE-SERVICES Chain Rule의 영향을 받게됨.

KUBE-SERVICES에 설정된 src, dst, dpt 와 

패킷 정보과 일치한다면 해당 KUBE-SERVICES Rule이 적용되지만

예제에서는 

[MAC 정보]
출발지 : 52:54:00:12:35:00  (Master Node의 virbr0 인터페이스 MAC주소)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 192.168.56.1  (external에서 접근한 ip주소)
도착지 : 10.0.2.10 (LoadBalancer의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (LoadBalancer Service Object에 설정된 Port 8080)

 

위와같이 도착지 IP가 LoadBalancer의 IP이기 때문에

해당 IP인 10.0.2.10과 동일한 KUBE-FW-WIPTPKZMBRCHTN3G 가 적용됨.

 

 

 

 

2.3.3. KUBE-FW-WIPTPKZMBRCHTN3G Chain의 Rule을 확인

 

[명령어]

iptables -t nat -L KUBE-FW-WIPTPKZMBRCHTN3G -n  | column -t

 

[분석]

KUBE-FW-WIPTPKZMBRCHTN3G Rule을 확인해보면

Kubernetes Cluster에 있는 모든 LoadBalancer Type의 Service Object정보를 확인할 수 있음

예제 환경에서는 LoadBalancer Type Service Objec가 한개 생성되어있어서 위와같이 1개를 확인할 수 있음

상위 정보의 주석부분( /*  default/iksoon-tomcat-loadbalancer loadbalancer IP  */)을 통해 

namespace = default

service object name =  iksoon-tomcat-loadbalancer loadbalancer

정보를 확인 할 수 있음

 

 

[KUBE-MARK-MASQ]

KUBE-MARK-MASQ는 iptables의 MASQUERADE (SNAT)옵션에 해당함.

external network에서 kubernetes cluster에 생성된 service obejct로 접근 할 시

접근하는 모든 패킷에 Netfilter Mart를 추가하는 역할을 함. 

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node(Host) cni0 인터페이스 MAC주소 변경됨)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 10.244.1.1 (Worker Node(Host)의  cni0인터페이스 IP주소로 변경됨)
도착지 : 10.0.2.10 (LoadBalancer의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (LoadBalancer Service Object에 설정된 Port 8080)

Netfilter Mark로 패킷의 Source IP 주소를 Node(Host) IP주소로 변경함

해당 KUBE-MARK-MASQ을 통과한다는 증거는 아래와 같음

[명령어]

watch -n 1 iptables -t nat -L KUBE-FW-WIPTPKZMBRCHTN3G -nv

패킷이 KUBE-MARK-MASQ와 KUBE-SVC가 동시에 올라감.

 

[KUBE-MARK-DROP]

NAT가 활성화되지 않은 패킷에 Netfilter 마크를 추가함. 

해당 패킷은 KUBE-FIREWALL체인 에서 폐기됨.

즉 정상적으로 Chain에 진입한 패킷인 아니면 폐기하기 위해 Mark하는 것으로 추정됨.

 

 

 

[패킷 흐름]

KUBE-FW-WIPTPKZMBRCHTN3G Rule에 있는

KUBE-SVC-WIPTPKZMBRCHTN3G로 이동함

 

 

 

2.3.4. KUBE-SVC-WIPTPKZMBRCHTN3G Chain의 Rule을 확인

 

[명령어]

iptables -t nat -L KUBE-SVC-WIPTPKZMBRCHTN3G -n  | column -t

 

[분석]

KUBE-SVC-XXX Table에서는

iptables의 statistic 기능을 이용하여 

요청 Packet은 Service를 구성하는 Pod들로 랜덤하고 균등하게 

Load Balancing하는 역할을 수행함.

예제 환경에서 해당 service object에 매칭 되어있는 pod가 1개 임으로

위와같이 1개의 KUBE-SEP가 조회 되지만

Service Object에 다수의 pod가 매칭되는 환경이라면

다수의 KUBE-SEP가 생성되어 있고

해당 SEP 중에 한곳으로 Load Balancing 됨.

예시)

deployment replicaset 설정이 3인 경우 

service obejct 에 3개의 pod가 매칭되어 

아래와 같이 3개의 KUBE-SEP가 조회됨.

 

 

[패킷 흐름]

예제 환경에서는 KUBE-SEP가 

KUBE-SEP-PWTXQ3FUHXN2P2HF 하나 있음으로

KUBE-SEP-PWTXQ3FUHXN2P2HF로 전달됨.

 

 

2.3.5. KUBE-SEP-PWTXQ3FUHXN2P2HF Chain의 Rule을 확인

 

[명령어]

iptables -t nat -L KUBE-SEP-PWTXQ3FUHXN2P2HF  -n  | column -t

 

[분석]

KUBE-MARK-MASQ와 DNAT가 존재함.

 

KUBE-SEP-XXX Table에서 요청 Packet은 

Pod의 IP 및 Service에서 설정한 Port로 DNAT를 수행한다. 

Pod의 IP로 DNAT된 요청 Packet은 CNI Plugin을 통해 

구축된 Container Network를 통해서 해당 Pod에게 전달됨.

즉 Master Node의 cni0로 아래의 패킷을 만들어서 보냄.

 

 

[KUBE-MARK-MASQ]

Netfilter Mark로 패킷의 Source IP 주소를 Node(Host) IP주소로 변경함

상위 KUBE-FW 과정에서 TCP 패킷의 Source IP주소를 변경했고

여기서는 prot가 all로 설정되어 있는데

해당 KUBE-MARK-MASQ은 통과하지 않는것을 확인함.

위와같이 주장하는 근거는 아래와 같음

[명령어]

watch -n 1 iptables -t nat -L KUBE-SEP-PWTXQ3FUHXN2P2HF -nv

위와같이 KUBE-MARK-MASQ는 카운팅되지 않고 DNAT만 카운팅됨

 

 

[DNAT]

최종적으로 목표로 했던 Pod IP주소와 Port번호로 패킷을 변경해줌.

즉 kube-proxy가 생성한 Netfilter를 통과한 결과물임.

도착지 MAC 정보는 Host의 ARP Table에 따라서 입력됨.

[MAC 정보]
출발지 : 2e:bf:78:0e:53:8f    (Master Node flannel.1인터페이스 MAC주소)
도착지 : ?? (Host의 ARP Tables 정보를 보고 도착지 IP에 해당하는 MAC으로 변경됨.)



[IP주소 정보]
출발지 : 10.244.0.0 (master node의 flannel.1 인터페이스 ip주소)
도착지 : 10.244.1.160 (목적지 Pod의 IP주소로 변경)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : 8080 (LoadBalancer에 맵핑된 목적지 Pod의 Port번호로 변경)

 

 

 

2.4. 패킷 흐름 분석

enp0s3 인터페이스로 전달된 모든 패킷은

kube-proxy가 설정한 Netfilter Chain Rule을 통과해야함.

netfilter를 통과하면서 Marking 된 패킷은 아래와 같이

출발지 정보는 Node(Host)의 정보로 변경되고

도착지 정보는 Pod의 정보로 변경됨.

[MAC 정보]
출발지 : 2e:bf:78:0e:53:8f    (Master Node flannel.1인터페이스 MAC주소)
도착지 : ?? (Host의 ARP Tables 정보를 보고 도착지 IP에 해당하는 MAC으로 변경됨.)



[IP주소 정보]
출발지 : 10.244.0.0 (master node의 flannel.1 인터페이스 ip주소)
도착지 : 10.244.1.117 (목적지 Pod의 IP주소로 변경)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : 8080 (LoadBalancer에 맵핑된 목적지 Pod의 Port번호로 변경)

 

 

 

 

 

3. Worker Node : flannel.1 확인

 

[명령어]

tcpdump -i flannel.1   -ne tcp  -vv -c 10

 

패킷 흐름 분석

worker node의 flannel.1 인터페이스에 어떠한 패킷도 들어오지 않음.

 

 

 

 

4. Worker Node : flannel.1의 ARP Table확인

 

flannel.1에 어떠한 패킷도 들어오지 않는 이유를 확인하기 위해

Worker Node의 ARP Table을 확인.

 

CentOS의 경우 arp 명령으로 확인할 수 있지만 아래와 같은 방법으로도 확인할 수 있음

 

kubectl 명령은 Master Node에서 실행함.

 

[명령어]

kubectl get pod -o wide -n kube-system

명령으로 Worker Node에서 동작 중인 Flannel Pod를 확인

 

[명령어]

kubectl -it exec kube-flannel-ds-5qrzc -n kube-system  -- arp -a

명령으로 Worker Node Flannel 의 ARP Table확인

 

 

[확인 결과]

이전에 통신을 했었음으로 ARP Table 목적지 Pod의 IP주소와 Mac주소가 있음.

그래서 패킷이 Worker Node의 flannel.1에서 탐지되지 않고

cni0 인터페이스로 바로 이동함.

 

 

 

5. Worker Node : cni0 확인

 

 

[명령어]

tcpdump -i cni0 -ne tcp and dst 10.244.1.160 -vv -c 10

 

 

패킷 흐름 분석

도착지 MAC정보가 ARP Tables의 Pod의 정보로 변경된 것을 확인할 수 있음

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  ( Worker Node cni0 인터페이스 MAC주소)
도착지 : 16:34:aa:db:1f:25  ( ARP Table정보에서 확인했던 Pod Mac 주소)



[IP주소 정보]
출발지 : 10.244.1.1     (Worker Node의  cni0 인터페이스 ip주소)
도착지 : 10.244.1.160 (목적지 Pod의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (목적지 Pod의 Port 8080)

 

 

 

6. Worker Node : vethaa818364 (목적지 Pod)

 

 

[명령어]

tcpdump -i vethaa818364  -ne tcp  -vv -c 10

 

vethaa818364 인터페이스가 

목적지인 tomcat pod에 해당하는 인터페이스임.

Pod에 해당하는 veth 장비 찾기 참고 : Pod의 veth (Virtual Ethernet Interface) 장치 찾기

 

패킷 흐름 분석

cni0에서 확인했던 패킷을 Pod내부의 tomcat container가 받는 것을 확인할 수 있음.

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node cni0 인터페이스 MAC주소)
도착지 : 16:34:aa:db:1f:25  ( ARP Table정보에서 확인했던 Pod Mac 주소)


[IP주소 정보]
출발지 : 10.244.1.1 (Worker Node의  인터페이스 ip주소)
도착지 : 10.244.1.160 (목적지 Pod의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (목적지 Pod의 Port 8080)

 

 

 

분석 완료...

 

 

 

 

 


제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!


 

 

 

 

 

 

참고

 

[kube-proxy iptable netfilter]

https://rtfm.co.ua/en/kubernetes-service-load-balancing-kube-proxy-and-iptables/

https://kubernetes.io/docs/concepts/services-networking/service/

https://sktelecom-oslab.github.io/Virtualization-Software-Lab/ExposeService/

https://arthurchiao.art/blog/cracking-k8s-node-proxy/

 

[LoadBalancer Type의 경우 kubernetes Routing 참고]

https://www.stackrox.com/post/2020/01/kubernetes-networking-demystified/

 

[kubernetes routing]

https://medium.com/finda-tech/kubernetes-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%95%EB%A6%AC-fccd4fd0ae6

https://ronaknathani.com/blog/2020/07/kubernetes-nodeport-and-iptables-rules/

 

[kube-proxy mode 설명]

https://ssup2.github.io/theory_analysis/Kubernetes_Service_Proxy/

https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

 

[iptable 개념 : KUBE-MARK-MASQ 의 MASQUERADE 패킷]

http://blog.naver.com/PostView.nhn?blogId=alice_k106&logNo=221305928714&parentCategoryNo=&categoryNo=22&viewDate=&isShowPopularPosts=false&from=postView

 

반응형