IPTables 참조
IPTables 참조 (IPTables Reference)
파드 안에서 프록시로 TCP 트래픽을 오가게 라우팅하기 위해, init container가 주입된 파드의 수명 주기가 시작될 때 iptables 규칙을 설정해요.
본문
처음에 linkerd-init은 nat 테이블에 두 개의 체인을 만들어요: PROXY_INIT_REDIRECT와 PROXY_INIT_OUTPUT. 이 체인들은 인바운드·아웃바운드 패킷을 프록시를 통해 라우팅하는 데 사용돼요. 각 체인에는 일련의 규칙이 붙어 있고, 패킷은 이 규칙들을 순서대로 통과해요.
인바운드 연결
파드에 패킷이 도착하면 보통 nat 테이블에 붙어 있는 기본 체인인 PREROUTING 체인이 패킷을 처리해요. 사이드카 컨테이너는 인바운드 패킷을 처리할 PROXY_INIT_REDIRECT라는 새 체인을 만들어요. 사이드카 컨테이너는 PREROUTING 체인의 패킷을 리다이렉트 체인으로 보내는 규칙(install-proxy-init-prerouting)을 만들어요. 이 규칙이 인바운드 패킷이 통과하는 첫 번째 규칙이에요.
리다이렉트 체인에는 규칙이 두 개 더 구성돼요:
- ignore-port: 목적지 포트가 skip-inbound-ports 설치 옵션에 포함된 패킷 처리를 건너뜁니다.
- proxy-init-redirect-all: 들어오는 모든 TCP 패킷을 프록시를 통해 포트 4143으로 리다이렉트합니다.
이 두 규칙에 따라 인바운드 패킷이 취할 수 있는 경로가 두 가지 있는데, 둘 다 아래에 정리돼 있어요.
인바운드 iptables 체인 순회: 패킷은 PREROUTING 체인에 도착해 즉시 리다이렉트 체인으로 라우팅돼요. 목적지 포트가 건너뛸 인바운드 포트 중 하나와 일치하면 프록시를 우회해서 애플리케이션 프로세스로 직접 전달돼요. 확인할 목적지 포트 목록은 Linkerd를 설치할 때 구성할 수 있어요. 패킷이 목록의 어떤 포트와도 일치하지 않으면 프록시를 통해 리다이렉트돼요. 리다이렉션은 들어오는 패킷의 목적지 헤더를 바꾸는 방식으로 이뤄지며, 대상 포트가 프록시의 인바운드 포트인 4143으로 교체돼요. 프록시가 패킷을 처리해 서비스로 전달할 새 패킷을 만들고, 특수 소켓 옵션 SO_ORIGINAL_DST를 사용해 인바운드 패킷의 원래 대상(IP:PORT)을 알아낼 수 있어요. 새 패킷은 OUTPUT 체인을 통해 라우팅된 뒤 애플리케이션으로 전송돼요. OUTPUT 체인 규칙은 아래에서 더 자세히 다룰게요.
아웃바운드 연결
패킷이 파드를 떠날 때 먼저 OUTPUT 체인을 통과해요. 이 체인은 nat 테이블에서 나가는 패킷이 처음 통과하는 첫 기본 체인이에요. 아웃바운드 패킷을 프록시의 아웃바운드 쪽으로 리다이렉트하기 위해 사이드카 컨테이너가 다시 새 체인을 만들어요. 첫 아웃바운드 규칙은 인바운드 쪽과 비슷해요: OUTPUT 체인을 통과하는 패킷은 처리되도록 우리의 PROXY_INIT_OUTPUT 체인으로 전달돼야 해요.
출력 리다이렉트 체인은 이해하기 조금 더 어렵지만 인바운드 리다이렉트 체인과 같은 논리적 흐름을 따라요. 총 4개의 규칙이 구성돼요:
- ignore-proxy-uid: 프록시(사용자 ID 2102)가 소유한 패킷은 처리를 건너뛰고 이전(OUTPUT) 체인으로 돌아갑니다. 거기서 아웃바운드 네트워크 인터페이스로 보내져요 (인바운드 패킷이라면 애플리케이션으로, 아웃바운드 패킷이라면 파드 밖으로).
- ignore-loopback: 패킷이 루프백 인터페이스(lo)로 전송되면 처리를 건너뛰고 이전 체인으로 돌아갑니다. 여기서 패킷은 체인의 첫 규칙과 마찬가지로 목적지로 전송돼요.
- ignore-port: 목적지 포트가 skip-outbound-ports 설치 옵션에 포함된 패킷 처리를 건너뜁니다.
- redirect-all-outgoing: 체인의 마지막 규칙으로, 나가는 모든 TCP 패킷을 프록시의 아웃바운드 포트인 4140으로 리다이렉트합니다. 패킷이 여기까지 왔다면 그 목적지가 로컬(즉 lo)이 아니고 프록시가 만든 것도 아님을 보장할 수 있어요. 즉 이 패킷은 서비스가 만든 것이므로, 프록시가 목적지로 전달해야 해요.
아웃바운드 iptables 체인 순회: 서비스가 만든 패킷은 먼저 OUTPUT 체인을 거쳐 우리의 출력 체인으로 보내져 처리돼요. PROXY_INIT_OUTPUT에서 만나는 첫 규칙은 ignore-proxy-uid예요. 패킷이 서비스에 의해 생성됐으므로 이 규칙은 건너뛰어져요. 패킷의 목적지가 로컬호스트에 바인딩된 포트(예: 127.0.0.1:80)가 아니면 두 번째 규칙도 건너뛰어요. 세 번째 규칙 ignore-port는 패킷의 목적지 포트가 건너뛸 아웃바운드 포트 목록에 있으면 매칭되는데, 이 경우 패킷은 프록시를 우회해 네트워크 인터페이스로 나가요. 규칙이 매칭되지 않으면 패킷은 체인의 마지막 규칙 redirect-all-outgoing에 도달해요. 이름이 뜻하는 대로, 프록시의 아웃바운드 포트 4140으로 보내져 처리돼요. 인바운드 경우와 마찬가지로 라우팅은 nat 레벨에서 일어나며, 패킷의 헤더가 아웃바운드 포트를 대상으로 다시 쓰여요. 프록시가 패킷을 처리한 뒤 목적지로 전달해요. 새 패킷은 OUTPUT 체인을 통해 같은 경로를 지나지만, 프록시가 만든 것이므로 첫 규칙에서 멈춰요.
이 설명은 목적지가 파드 밖의 다른 서비스인 패킷에 적용돼요. 실제로 애플리케이션은 로컬로 트래픽을 보낼 수도 있어요. 그래서 두 가지 다른 시나리오를 살펴볼게요: 서비스가 자기 자신과 통신하는 경우(로컬호스트로 트래픽을 보내거나 자기 엔드포인트 주소를 사용)와 서비스가 clusterIP 대상을 통해 자기 자신과 통신하는 경우. 두 시나리오는 어느 정도 연관돼 있지만, 패킷이 취하는 경로는 달라요.
서비스가 자기 자신에게 요청을 보낼 수 있어요. 파드 안의 다른 컨테이너를 대상으로 삼을 수도 있어요. 이 시나리오는 보통 다음의 경우에 적용돼요:
- 목적지가 파드(또는 엔드포인트) IP 주소인 경우.
- 목적지가 로컬호스트에 바인딩된 포트인 경우(어느 컨테이너가 소유하든 상관없음).
아웃바운드 iptables 체인 순회: 애플리케이션이 자기 파드의 IP(또는 루프백 주소)로 자기 자신을 대상으로 삼으면, 패킷은 두 출력 체인을 통과해요. 소유자가 프록시가 아니라 애플리케이션이므로 첫 규칙은 건너뛰어져요. 두 번째 규칙이 매칭되면 패킷은 첫 출력 체인으로 돌아가고, 여기서 서비스로 직접 전송돼요.
참고
보통 패킷은 아웃바운드 쪽에서 POSTROUTING이라는 다른 체인도 통과해요. 이 체인은 OUTPUT 체인 다음에 거치는데, 설명을 단순하게 유지하기 위해 언급하지 않았어요. 마찬가지로 루프백 인터페이스로 전송되는 아웃바운드 패킷은 다시 처리해야 하므로 인바운드 패킷이 돼요. 이 경우 커널은 단축 경로를 취해서, 외부 세계에서 처음 도착한 인바운드 패킷이 거치는 PREROUTING 체인을 우회해요. 그래서 로컬로 전송되는 아웃바운드 패킷을 처리하기 위한 특별한 인바운드 규칙은 필요 없어요.
서비스가 자기 clusterIP로 자기 자신에게 요청을 보낼 수 있어요. 이런 경우 목적지가 로컬이라는 보장은 없어요. 패킷은 아래 다이어그램에 나온 대로 특이한 경로를 따라요.
아웃바운드 iptables 체인 순회: 패킷이 출력 체인을 처음 통과할 때는 아웃바운드 패킷이 보통 취하는 것과 같은 경로를 따라요. 이런 시나리오에서 패킷의 목적지는 커널이 로컬로 간주하지 않는 주소예요. 어쨌든 가상 IP니까요. 프록시는 패킷을 처리하면서, 연결 레벨에서 clusterIP로의 연결은 엔드포인트 간에 로드 밸런싱돼요. 선택된 엔드포인트가 그 파드 자신일 가능성이 높아서, 패킷은 파드를 결코 떠나지 않아요. 목적지는 podIP로 해석돼요. 프록시가 만든 패킷은 출력 체인을 통과하다 첫 규칙에서 멈추고, 이어서 서비스로 전달돼요. 이것은 엣지 케이스인데, 패킷이 프록시로 처리됐기 때문이에요. 앞서 논의한 시나리오(프록시를 완전히 건너뜀)와는 달라요. 따라서 연결 레벨에서 프록시는 엔드포인트가 파드에 로컬일 때 연결을 mTLS 처리하거나 HTTP/2로 기회적으로 업그레이드하지 않아요. 실제로는 목적지가 루프백인 것처럼 취급되지만, 패킷이 서비스에서 자기 자신에게 직접 전달되는 대신 프록시를 통해 전달된다는 점이 달라요.
규칙 테이블
참고로, 규칙을 만들 때 실제로 쓰이는 명령어는 아래에서 찾을 수 있어요. 또는 파드에 생성된 iptables 규칙을 직접 검사하고 싶다면 다음 명령어로 가져올 수 있어요:
$ kubectl -n <namespace> logs <pod-name> linkerd-init
# where <pod-name> is the name of the pod
# you want to see the iptables rules for
인바운드
| # | name | iptables rule | description |
|---|---|---|---|
| 1 | redirect-common-chain | iptables -t nat -N PROXY_INIT_REDIRECT | creates a new iptables chain to add inbound redirect rules to; the chain is attached to the nat table |
| 2 | ignore-port | iptables -t nat -A PROXY_INIT_REDIRECT -p tcp --match multiport --dports -j RETURN | configures iptables to ignore the redirect chain for packets whose dst ports are included in the --skip-inbound-ports config option |
| 3 | proxy-init-redirect-all | iptables -t nat -A PROXY_INIT_REDIRECT -p tcp -j REDIRECT --to-port 4143 | configures iptables to redirect all incoming TCP packets to port 4143, the proxy's inbound port |
| 4 | install-proxy-init-prerouting | iptables -t nat -A PREROUTING -j PROXY_INIT_REDIRECT | the last inbound rule configures the PREROUTING chain (first chain a packet traverses inbound) to send packets to the redirect chain for processing |
아웃바운드
| # | name | iptables rule | description |
|---|---|---|---|
| 1 | redirect-common-chain | iptables -t nat -N PROXY_INIT_OUTPUT | creates a new iptables chain to add outbound redirect rules to, also attached to the nat table |
| 2 | ignore-proxy-uid | iptables -t nat -A PROXY_INIT_OUTPUT -m owner --uid-owner 2102 -j RETURN | when a packet is owned by the proxy (--uid-owner 2102), skip processing and return to the previous (OUTPUT) chain |
| 3 | ignore-loopback | iptables -t nat -A PROXY_INIT_OUTPUT -o lo -j RETURN | when a packet is sent over the loopback interface (lo), skip processing and return to the previous chain |
| 4 | ignore-port | iptables -t nat -A PROXY_INIT_OUTPUT -p tcp --match multiport --dports -j RETURN | configures iptables to ignore the redirect output chain for packets whose dst ports are included in the --skip-outbound-ports config option |
| 5 | redirect-all-outgoing | iptables -t nat -A PROXY_INIT_OUTPUT -p tcp -j REDIRECT --to-port 4140 | configures iptables to redirect all outgoing TCP packets to port 4140, the proxy's outbound port |
| 6 | install-proxy-init-output | iptables -t nat -A OUTPUT -j PROXY_INIT_OUTPUT | the last outbound rule configures the OUTPUT chain (second before last chain a packet traverses outbound) to send packets to the redirect output chain for processing |