title: “[#24] enovy는 어떻게 트래픽을 intercept하는가?” categories:

Overview


지난 포스팅에서 보안의 주도권을 앱이 아닌 인프라(Sidecar)로 옮기기로 결정되었다. 이제 개발자는 평소처럼 80 포트로 호출하면 된다. (어떻게 설정했는지 본문에서는 다루지 않는다.)

그런데 여기서 의문이 생겼다.

“내 앱 컨테이너는 그냥 외부로 패킷을 던졌을 뿐인데, 옆에 있는 Sidecar(Envoy)가 이걸 어떻게 가로채는 거지?”

본문에서는 그 ‘가로채기(Intercept)’의 비밀인 Transparent Proxy 매커니즘을 간략히 살펴본다.

같은 집(Pod), 다른 방: Sidecar 구조


쿠버네티스에서 Pod는 하나의 가상 호스트와 같다. 하나의 Pod 안에 우리 앱 컨테이너와 Envoy 컨테이너가 함께 담기면 다음과 같은 특징을 갖는다.

Transparent Proxy: iptables 주입


앱이 모르게 트래픽을 가로채는 핵심은 리눅스 커널의 네트워크 관리 도구인 iptables다.

사이드카가 주입(Injection)될 때, Istio는 istio-init이라는 컨테이너를 통해 대상 Pod의 커널에 iptables 규칙을 함께 주입한다고 한다. 이후 앱의 모든 인바운드/아웃바운드 트래픽은 이 규칙에 따라 인터셉트된다.

  1. 아웃바운드 가로채기: 앱이 밖으로 보내는 80 트래픽을 가로채 사이드카(Envoy)의 15001번 포트로 강제 포워딩된다. 여기서 Envoy는 목적지를 확인하고 mTLS 암호화 후에 포워딩된다.

  2. 인바운드 가로채기: 외부에서 들어오는 트래픽을 사이드카의 15006번 포트로 먼저 꺾여 여기서 Envoy는 암호를 풀고(복호화) 정상적인 요청인지 확인한 뒤, 내부 앱의 80 포트로 최종 전달한다.

이것을 Transparent Proxy(투명 프록시)라고 부른다. 앱 입장에서는 평소처럼 통신했을 뿐인데, 패킷이 커널 계층에서 Envoy라는 ‘검문소’로 배달되는 매커니즘이다.

서비스 간 통신 흐름 (Service A to Service B)


앱 A가 앱 B를 호출할 때, 각 사이드카가 어떻게 협력하는지 시퀀스로 나타내면 다음과 같다.

  1. App A: http://service-b:80으로 요청 전송
  2. Sidecar A (Intercept): Outbound 규칙에 의해 요청을 가로챔
  3. Sidecar A (Upgrade): HTTP(80) 요청을 mTLS(443)로 감싸서 전송
  4. Sidecar B (Intercept): Inbound 규칙에 의해 들어오는 443 요청을 가로챔
  5. Sidecar B (Downgrade): mTLS(443)를 복호화하여 원래의 HTTP(80)로 변환
  6. App B: Sidecar B로부터 순수한 HTTP(80) 요청을 수신

Envoy Role (Intercept 이후)


일단 트래픽을 가로챈 Envoy는 다음과 같은 스마트한 작업을 수행한다.

Conclusion


우리가 소스 코드를 단 한 줄도 고칠 필요가 없었던 이유는, 이 모든 과정이 애플리케이션 계층이 아닌 리눅스 커널과 네트워크 인터페이스 계층에서 일어나기 때문.

결국 보안 주도권의 전도는 이 iptables라는 보이지 않는 조력자 덕분에 완성된다.

Next


다음 포스트에서는 내 앱의 로그에는 분명 http://...:80으로 찍히는데, 실제 네트워크 패킷은 왜 443(mTLS)으로 바뀌어 있는지 그 ‘변신’의 과정을 포스팅한다.