Ztunnel 트래픽 리디렉션
Ztunnel 트래픽 리디렉션 (Ztunnel traffic redirection)
앰비언트 모드의 맥락에서 *트래픽 리디렉션(traffic redirection)*은 앰비언트 활성화 워크로드로 들어오고 나가는 트래픽을 가로채어, 핵심 데이터 경로를 처리하는 ztunnel 노드 프록시를 거치게 하는 데이터 플레인 기능을 뜻해요. 때로는 *트래픽 캡처(traffic capture)*라는 용어도 사용돼요.
출처: Istio 문서
본문
ztunnel은 애플리케이션 트래픽을 투명하게 암호화하고 라우팅하는 것을 목표로 하기 때문에, "메시 안에 있는" 파드로 들어오고 나가는 모든 트래픽을 캡처하는 메커니즘이 필요해요. 이는 보안상 매우 중요한 작업이에요. ztunnel이 우회될 수 있다면 인가 정책도 우회될 수 있기 때문이에요.
Istio의 파드 내 트래픽 리디렉션 모델 (Istio's in-pod traffic redirection model)
앰비언트 모드의 파드 내 트래픽 리디렉션의 핵심 설계 원칙은, ztunnel 프록시가 워크로드 파드의 Linux 네트워크 네임스페이스 안에서 데이터 경로 캡처를 수행할 수 있는 능력을 가진다는 것이에요. 이는 istio-cni 노드 에이전트와 ztunnel 노드 프록시 사이의 기능 협력을 통해 이루어져요. 이 모델의 핵심 이점은 Istio의 앰비언트 모드가 어떤 쿠버네티스 CNI 플러그인과도 투명하게 함께 동작하면서, 쿠버네티스 네트워킹 기능에 영향을 주지 않는다는 것이에요.
아래 그림은 앰비언트가 활성화된 네임스페이스에서 새 워크로드 파드가 시작될 때(또는 추가될 때) 일어나는 일련의 사건들을 보여줘요.
istio-cni 노드 에이전트는 파드 생성·삭제 같은 CNI 이벤트에 응답하고, 파드나 네임스페이스에 앰비언트 레이블이 추가되는 것 같은 이벤트를 위해 기본 쿠버네티스 API 서버도 감시해요.
istio-cni 노드 에이전트는 또한 체인형 CNI 플러그인을 설치해요. 이 플러그인은 해당 쿠버네티스 클러스터 안에서 기본 CNI 플러그인이 실행된 후, 컨테이너 런타임에 의해 실행돼요. 그 목적은 오직, 컨테이너 런타임이 이미 앰비언트 모드에 등록된 네임스페이스에서 새 파드를 만들었을 때 istio-cni 노드 에이전트에게 알리고, 새 파드 컨텍스트를 istio-cni로 전달하는 것이에요.
istio-cni 노드 에이전트가 파드를 메시에 추가해야 한다는 알림을 받으면(파드가 아예 새 것이라면 CNI 플러그인에서, 이미 실행 중이지만 추가가 필요하다면 쿠버네티스 API 서버에서), 다음과 같은 일련의 작업이 수행돼요:
istio-cni는 파드의 네트워크 네임스페이스에 들어가서 네트워크 리디렉션 규칙을 만들고, 파드로 들어오고 나가는 패킷이 가로채어져 잘 알려진 포트(15008, 15006, 15001)에서 리스닝하는 노드 로컬 ztunnel 프록시 인스턴스로 투명하게 리디렉션되도록 해요.- 그다음
istio-cni노드 에이전트는 Unix 도메인 소켓을 통해 ztunnel 프록시에게, 파드의 네트워크 네임스페이스 안(포트 15008, 15006, 15001)에 로컬 프록시 리스닝 포트를 만들어야 한다고 알리고, 파드의 네트워크 네임스페이스를 나타내는 저수준 Linux 파일 디스크립터를 ztunnel에 제공해요.
일반적으로 소켓은 실제로 그 네트워크 네임스페이스 안에서 실행되는 프로세스에 의해 Linux 네트워크 네임스페이스 안에 만들어지지만, Linux의 저수준 소켓 API를 활용하면 한 네트워크 네임스페이스에서 실행 중인 프로세스가 다른 네트워크 네임스페이스 안에 리스닝 소켓을 만드는 것도 가능해요. 단, 대상 네트워크 네임스페이스가 생성 시점에 알려져 있어야 해요.
- 노드 로컬 ztunnel은 내부적으로 새로 추가된 파드 전용의 새 논리적 프록시 인스턴스와 리스닝 포트 세트를 띄워요. 이는 여전히 같은 프로세스 안에서 실행되며, 그저 그 파드 전용의 전용 태스크일 뿐이에요.
- 파드 내 리디렉션 규칙이 자리 잡고 ztunnel이 리스닝 포트를 만들면, 파드가 메시에 추가되고 트래픽이 노드 로컬 ztunnel을 통해 흐르기 시작해요.
메시 안의 파드로/파드에서 나가는 트래픽은 기본적으로 mTLS로 완전히 암호화돼요.
이제 데이터는 파드 네트워크 네임스페이스에 암호화된 채로 들어오고 나가요. 메시 안의 모든 파드는 메시 정책을 적용하고 트래픽을 안전하게 암호화할 능력을 가져요. 파드 안에서 실행되는 사용자 애플리케이션이 그중 어느 것도 인지하지 못하더라도 말이죠.
이 다이어그램은 새 모델에서 앰비언트 메시 안의 파드들 사이로 암호화된 트래픽이 어떻게 흐르는지 보여줘요:
앰비언트 모드에서 트래픽 리디렉션 관찰·디버깅하기 (Observing and debugging traffic redirection in ambient mode)
앰비언트 모드에서 트래픽 리디렉션이 제대로 동작하지 않는다면, 문제를 좁히는 데 도움이 되는 몇 가지 빠른 확인 방법이 있어요. ztunnel 디버깅 가이드에 설명된 단계부터 트러블슈팅을 시작하길 권장해요.
ztunnel 프록시 로그 확인하기 (Check the ztunnel proxy logs)
애플리케이션 파드가 앰비언트 메시의 일부일 때, 메시가 트래픽을 리디렉션하고 있는지 확인하려면 ztunnel 프록시 로그를 확인할 수 있어요. 아래 예제에서 보듯, inpod와 관련된 ztunnel 로그는 파드 내 리디렉션 모드가 활성화되어 있고, 프록시가 앰비언트 애플리케이션 파드에 대한 네트워크 네임스페이스(netns) 정보를 받았으며, 그 파드에 대한 프록싱을 시작했다는 것을 나타내요.
$ kubectl logs ds/ztunnel -n istio-system | grep inpod
Found 3 pods, using pod/ztunnel-hl94n
inpod_enabled: true
inpod_uds: /var/run/ztunnel/ztunnel.sock
inpod_port_reuse: true
inpod_mark: 1337
2024-02-21T22:01:49.916037Z INFO ztunnel::inpod::workloadmanager: handling new stream
2024-02-21T22:01:49.919944Z INFO ztunnel::inpod::statemanager: pod WorkloadUid("1e054806-e667-4109-a5af-08b3e6ba0c42") received netns, starting proxy
2024-02-21T22:01:49.925997Z INFO ztunnel::inpod::statemanager: pod received snapshot sent
2024-02-21T22:03:49.074281Z INFO ztunnel::inpod::statemanager: pod delete request, draining proxy
2024-02-21T22:04:58.446444Z INFO ztunnel::inpod::statemanager: pod WorkloadUid("1e054806-e667-4109-a5af-08b3e6ba0c42") received netns, starting proxy
소켓 상태 확인하기 (Confirm the state of sockets)
포트 15001, 15006, 15008의 소켓이 열려 있고 리스닝 상태인지 확인하려면 아래 단계를 따라 하세요.
$ kubectl debug $(kubectl get pod -l app=curl -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image nicolaka/netshoot -- ss -ntlp
Defaulting debug container name to debugger-nhd4d.
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 127.0.0.1:15080 0.0.0.0:*
LISTEN 0 128 *:15006 *:*
LISTEN 0 128 *:15001 *:*
LISTEN 0 128 *:15008 *:*
iptables 규칙 설정 확인하기 (Check the iptables rules setup)
애플리케이션 파드 중 하나 안의 iptables 규칙 설정을 보려면 다음 명령을 실행하세요:
$ kubectl debug $(kubectl get pod -l app=curl -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it --image docker.io/istio/base --profile=netadmin -n ambient-demo -- iptables-save
Defaulting debug container name to debugger-m44qc.
# Generated by iptables-save
*mangle
:PREROUTING ACCEPT [320:53261]
:INPUT ACCEPT [23753:267657744]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [23352:134432712]
:POSTROUTING ACCEPT [23352:134432712]
:ISTIO_OUTPUT - [0:0]
:ISTIO_PRERT - [0:0]
-A PREROUTING -j ISTIO_PRERT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -m connmark --mark 0x111/0xfff -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
-A ISTIO_PRERT -m mark --mark 0x539/0xfff -j CONNMARK --set-xmark 0x111/0xfff
-A ISTIO_PRERT -s 169.254.7.127/32 -p tcp -m tcp -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -i lo -p tcp -j ACCEPT
-A ISTIO_PRERT -p tcp -m tcp --dport 15008 -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15008 --on-ip 0.0.0.0 --tproxy-mark 0x111/0xfff
-A ISTIO_PRERT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15006 --on-ip 0.0.0.0 --tproxy-mark 0x111/0xfff
COMMIT
# Completed
# Generated by iptables-save
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [175:13694]
:POSTROUTING ACCEPT [205:15494]
:ISTIO_OUTPUT - [0:0]
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -d 169.254.7.127/32 -p tcp -m tcp -j ACCEPT
-A ISTIO_OUTPUT -p tcp -m mark --mark 0x111/0xfff -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j REDIRECT --to-ports 15001
COMMIT
명령 출력은 애플리케이션 파드의 네트워크 네임스페이스 안 netfilter/iptables의 NAT와 Mangle 테이블에 추가적인 Istio 전용 체인이 추가되었음을 보여줘요. 파드로 들어오는 모든 TCP 트래픽은 인그레스 처리를 위해 ztunnel 프록시로 리디렉션돼요. 트래픽이 평문(목적지 포트 != 15008)이면 파드 내 ztunnel의 평문 리스닝 포트 15006으로 리디렉션되고, HBONE(목적지 포트 == 15008)이면 파드 내 ztunnel의 HBONE 리스닝 포트 15008로 리디렉션돼요. 파드에서 나가는 모든 TCP 트래픽은 이그레스 처리를 위해 ztunnel의 포트 15001로 리디렉션된 뒤, ztunnel이 HBONE 캡슐화를 사용해 내보내요.