정말 오랜만에 해보는 패킷분석이다.
현업에서 Istio를 사용할 일이 없어 잘 알진 못하지만 ...
언제까지 Ingress만 쓸거고, Rolling Update로 배포 할 것인가?
Ingress Controller를 쓰면 아주 쉽지만,
그럼에도 Istio를 사용하는 이유를 간단하게 언급하자면 ...
보안 강화
- Pod 간 통신이 기본적으로 mTLS로 암호화
- Peer Authentication을 통한 인증 제어가 용이
가시성 확보
- Sidecar proxy를 통해 모든 Pod의 통신이 추적되어 분산 추적과 모니터링이 용이
- 서비스 간 호출 관계와 성능을 상세하게 파악
고급 트래픽 제어
- 비율 기반 라우팅, 트래픽 속도 제한, 정책 확인 등 고급 라우팅 규칙을 적용
- Circuit Breaking과 같은 복잡한 트래픽 제어가 가능
환경 구성
- istio version 1.24.2
- 설치 방법 : https://istio.io/latest/docs/setup/install/istioctl/
Prerequsite
테스트에 필요한 Pod, Service, Gateway, VirtualService 정보 (일단 클러스터에 배포해두자)
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx-app
spec:
nodeName: k8s-worker-1 # istio-ingress와 다른 node에 배포하자.
terminationGracePeriodSeconds: 0
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
spec:
ports:
- name: svc-nginx
port: 80
targetPort: 80
selector:
app: nginx-app
type: ClusterIP
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: test-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: nginx-service
spec:
hosts:
- "hj-test.com"
gateways:
- test-gateway
http:
- route:
- destination:
host: svc-nginx
port:
number: 80
Overview.
Istio 에서 패킷 흐름도는 아래 그림과 같다.
[Inbound] 외부 Client -> nginx pod 패킷 흐름 분석
1. 외부 Client (Ubuntu) -> Istio Ingress Gateway 구간
Pod 패킷 캡쳐 sniff 사용
kubectl krew install sniff
kubectl sniff {istio-ingress-gateway pod-name} -n istio-system -p -o capture.pcap
패킷을 살펴보자.
아래 Packet은 Istio Ingress gateway에서 캡쳐했다.
외부 Client <-> Nginx Pod간 통신에서 중간다리 역할을 하기 때문이다.
192.168.56.1 외부 Client <-> 192.168.140.6 Istio Ingress Gateway 로 요청이 전달되고,
192.168.140.6 <-> 192.168.230.2 nginx pod로 패킷이 전달된다.
이 구간에서 Istio Ingress gateway는 외부 Client IP를 X-forwarded-for 헤더에 담아서 전달한다.
X-Forwarded-For(XFF) 헤더
HTTP 프록시나 로드 밸런서를 통해 웹 서버에 접속하는 클라이언트의 원래 IP 주소를 식별하기 위한 표준 헤더
예시. X-Forwarded-For: <client>, <proxy1>, <proxy2>
* 가장 왼쪽이 최초 클라이언트 IP이고, 오른쪽으로 갈수록 중간 프록시 서버들의 IP가 순차적으로 추가
2. Istio Ingress는 worker-2에서 Running 중이고,
Nginx pod는 worker-1에서 Running 중인데 어떻게 전달될수 있을까?
k8s-worker-2에 접속해서 라우팅 룰을 살펴보면된다.
설치 시점에 Calico CNI Iptable 방식으로 설정해두었기 때문에 ip -c route 명령어로 라우팅 조건을 확인할 수 있다.
1. worker-2 -> worker-1
cali0fb22ae215b (Istio Ingress Gateway의 Pause Container 일 것이고) 인터페이스(Pod의 veth pair)를 통해 나온다.
라우팅 테이블에서 목적지 192.168.230.0/26 네트워크는 "via 192.168.56.12 dev enp0s8" 규칙을 따른다.
- 192.168.56.12는 k8s-worker-1 node의 ip를 의미
즉, enp0s8 인터페이스를 통해 nginx-pod를 가진 k8s-worker-1 노드(192.168.56.12)로 전달
tcpdump 결과를 보면 k8s-worker-2의 룰에 따라 패킷이 라우팅되는 것을 확인할 수 있다.
2. worker-1 -> nginx-pod
k8s-worker-1로 도착한 패킷은 라우팅 규칙에 따라
192.168.230.2로 요청온 패킷에 대해 cali8b56182ddcf 인터페이스로 라우팅 된다.
자 여기서 부터 본론이다.
라우팅 룰에 맞춰서 nginx-pod까지 여차여차 왔는데,
3. (sidecar) istio-proxy container에서 nginx container로 어떻게 전달되는걸까?
namespace 설정에 istio-inject=enabled 라벨을 붙이면,
생성되는 모든 Pod에 istio-init 이라는 init-container와 istio-proxy라는 envoy container가 추가된다.
istio-init을 살펴보자.
-p 15001 : 모든 아웃바운드 트래픽은 15001 포트로 REDIRECT
-z 15006: 모든 인바운드 트래픽은 15006 포트로 REDIRECT
-u 1337: UID 1337번은 프록시로 라우팅하지 않는다
- 무한루프 방지, istio-proxy는 1337 권한으로 동작됨, 자기가 보낸 패킷을 자기가 다시 받지 않도록 하기 위함
-i *: 모든 IP Range에 해당되는 트래픽 라우팅
-b * -d 15090, 15021, 15020: 3개의 포트를 제외하고 모든 포트에 해당되는 트래픽을 라우팅 한다.
Istio-proxy container의 Iptable Inbound 룰을 살펴보면
- PREROUTING -> ISTIO_INBOUND -> ISTIO_IN_REDIRECT (redir ports 15006) 순서로 정리된다
15006 포트는 envoy 프로세스가 Listen 하고 있다.
다시 nginx-pod로 curl 요청을 보내고,
istio-proxy 로그를 살펴보자
$ kubectl logs -f nginx-pod -c istio-proxy
[2025-01-30T13:29:22.017Z] "GET / HTTP/1.1" 200 - via_upstream - "-" 0 615 0 0 "192.168.56.1" "curl/8.5.0" "1a049686-177e-97a2-83e4-dbc47d396ed4" "hj-test.com:32548" "192.168.230.2:80" inbound|80|| 127.0.0.6:55717 192.168.230.2:80 192.168.56.1:0 - default
127.0.0.6 으로 Source IP가 변경되어 찍혀있는 것을 확인할수 있는데,
이럴땐 Pakcet 찍어서 Wireshark으로 보는게 가장 깔끔하다
kubectl sniff nginx-pod -c istio-proxy -p -o proxy.pcap
istio-proxy에서 ip정보를 127.0.0.6으로 변경하고 전달하는 것을 확인할 수 있다.
(XFF 정보에 Proxy 정보가 누적되어 있지 않고,
Source 포트 정보가 다른것을 보아 NAT에서 S-NAT, D-NAT로 목적지를 관리하고 있을 듯하다. )
1337 UID만 아니면 목적지로 routing 한다.
별도의 POSTROUTING룰은 존재하지 않는다.
이로써 istio-proxy -> nginx container까지 패킷이 도착했다.
요약하자면,
istio-init 컨테이너에서 iptable 라우팅 rule을 생성하고, istio-proxy에서 모든 패킷을 받아다가 확인 후 nginx pod에 전달한다.
[Outbound] nginx-pod -> 외부 서버 패킷 흐름 분석
istio-proxy container의 패킷 캡쳐 준비 먼저하고
kubectl sniff nginx-pod -c istio-proxy -p -o proxy-outbound.pcap
특정 외부 도메인으로 요청을 보내보자
kubectl exec -it nginx-pod -c nginx-container -- curl -s http://ipinfo.io/city
확인차 ipinfo.io 도메인 DNS Query를 해보면 IP 주소는 34.117.59.81로 확인된다.
nslookup ipinfo.io
Name: ipinfo.io
Address: 34.117.59.81
1. nginx container -> istio-proxy container
Istio-proxy container에서 NAT 트래픽 정보를 확인해보면
#3153은 istio-proxy의 container PID (crictl inspect {cotainer-id} | grep pid)
nsenter -t 3153 -n conntrack -L --dst-nat
# tcp 6 112 TIME_WAIT src=192.168.230.2 dst=34.117.59.81 sport=53734 dport=80 src=127.0.0.1 dst=192.168.230.2 sport=15001 dport=53734 [ASSURED] mark=0 use=1
# src=192.168.230.2 dst=34.117.59.81 sport=53734 dport=80 : 최초 outboud 요청 트래픽
# src=127.0.0.1 dst=192.168.230.2 sport=15001 dport=53734 : 15001으로 redirect 됨,
istio-proxy의 패킷을 확인해보면 15001포트로 redirect 되었음을 확인할 수 있다.
라우팅 테이블도 다시 확인해보자
nsenter -t 3153 -n iptables -t nat -L -n -v
OUTOUT -> ISTIO_OUTPUT -> ISTIO_REDIRECT (redir ports 15001) 순서로 룰이 적용되었다.
2. Istio-proxy -> 외부 서버 (ipinfo.io)
nsenter -t 3153 -n lsof -i TCP:80
위에서 언급한 1337 권한으로 ipinfo.io(34.117.59.81)으로 정상적으로 요청되었음을 확인할 수 있다.
OUTPUT -> ISTIO_OUTPUT 순서로 Routing 룰이 적용되었고, 1337 권한으로 확인되어
다시 istio-proxy로 패킷이 들어가지 않고 외부로 나갈수 있었다.
참고자료
https://gasidaseo.notion.site/Istio-Life-of-a-packet-6ad9808e14594296bf854dcc203cab71
Istio 🌶️ 트래픽 흐름 Life of a packet | Notion
참고 링크
gasidaseo.notion.site
https://jimmysong.io/en/blog/sidecar-injection-iptables-and-traffic-routing/
Understanding the Sidecar Injection, Traffic Intercepting & Routing Process in Istio | Jimmy Song
Learn the sidecar pattern, transparent traffic intercepting and routing in Istio.
jimmysong.io
https://brunch.co.kr/@growthminder/84
istio proxy의 작동원리
istio sidecar proxy의 화려한 패킷 가로채기 | istio는 쿠버네티스 클러스터 내에서 네트워크 흐름을 통제하는 오픈소스이다. istio는 Envoy 프록시를 사이드카(sidecar) 형태로 배포하여 트래픽을 관리한
brunch.co.kr