카테고리 없음

Istio side proxy 패킷 분석(In/Outbound)

hj_. 2025. 1. 30. 23:49

정말 오랜만에 해보는 패킷분석이다.

 

현업에서 Istio를 사용할 일이 없어 잘 알진 못하지만 ... 

언제까지 Ingress만 쓸거고, Rolling Update로 배포 할 것인가? 

 

Ingress Controller를 쓰면 아주 쉽지만,

그럼에도 Istio를 사용하는 이유를 간단하게 언급하자면 ... 

 

보안 강화

  • Pod 간 통신이 기본적으로 mTLS로 암호화
  • Peer Authentication을 통한 인증 제어가 용이

가시성 확보

  • Sidecar proxy를 통해 모든 Pod의 통신이 추적되어 분산 추적과 모니터링이 용이
  • 서비스 간 호출 관계와 성능을 상세하게 파악

고급 트래픽 제어

  • 비율 기반 라우팅, 트래픽 속도 제한, 정책 확인 등 고급 라우팅 규칙을 적용
  • Circuit Breaking과 같은 복잡한 트래픽 제어가 가능

 

환경 구성 

nginx-pod, ingress-gateway 배치 현황

 

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 에서 패킷 흐름도는 아래 그림과 같다.

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간 통신에서 중간다리 역할을 하기 때문이다. 

 

외부 Client PC에서 Nginx Pod로 요청을 보냈을때, istio ingress pod에서 캡쳐한 packet

 

 

 

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

ingress gateway pod가 동작중인 worker node 라우팅 룰

 

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을 살펴보자.

nginx-pod의 init-container 설정 값 확인(좌) 노드에서 crictl 명령어로 container의 iptable 확인

 

-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에서 패킷 캡쳐

 

 

istio-proxy에서 ip정보를 127.0.0.6으로 변경하고 전달하는 것을 확인할 수 있다. 

 

(XFF 정보에 Proxy 정보가 누적되어 있지 않고,

Source 포트 정보가 다른것을 보아 NAT에서 S-NAT, D-NAT로 목적지를 관리하고 있을 듯하다. )

istio-proxy의 ISTIO_OUTPUT iptable

 

1337 UID만 아니면 목적지로 routing 한다. 

별도의 POSTROUTING룰은 존재하지 않는다. 

 

이로써 istio-proxy -> nginx container까지 패킷이 도착했다. 

 

 

요약하자면, 

istio-init 컨테이너에서 iptable 라우팅 rule을 생성하고, istio-proxy에서 모든 패킷을 받아다가 확인 후 nginx pod에 전달한다. 

https://jimmysong.io/en/blog/sidecar-injection-iptables-and-traffic-routing/

 

[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 되었음을 확인할 수 있다. 

nginx-pod에서 outbount 요청시, istio-proxy 에서 패킷 정보

 

라우팅 테이블도 다시 확인해보자 

nsenter -t 3153 -n iptables -t nat -L -n -v

 

OUTOUT -> ISTIO_OUTPUT -> ISTIO_REDIRECT (redir ports 15001) 순서로 룰이 적용되었다. 

Istio-proxy Iptable

2. Istio-proxy -> 외부 서버 (ipinfo.io)

nsenter -t 3153 -n lsof -i TCP:80

Outbount 패킷의 PID 확인, Source Port 정보와 매핑

 

위에서 언급한 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