공개 Linux 서비스가 VPN으로 응답하는 이유
로컬 서비스는 eth0에서 요청을 받아 완전 터널 VPN으로 응답할 수 있습니다. 전체 WAN 테이블을 갖춘 소스 규칙은 그 응답을 원래 경로에 유지합니다.
소켓에는 어느 인터페이스가 요청을 실어 날랐는지 기억하는 것이 없습니다. 그래서 완전 터널 VPN의 기본 경로가 WAN에 속해야 할 응답을 가로챌 수 있습니다. 서비스가 인바운드 인터페이스를 무시하는 것이 아닙니다. Linux는 로컬에서 생성된 응답을 목적지와 소스 정책에 따라 라우팅합니다. 소스 기반 규칙은 대신 서비스의 응답만 전체 WAN 테이블을 통해 보내고 일반 출구는 터널에 남길 수 있습니다. 응답이 잘못된 경로로 나가는 동안에도 포트는 열려 있으므로 증상은 한쪽만 보입니다.
오해: 응답은 도착한 인터페이스를 따라간다
eth0로 도착한 패킷이 eth0로 나가야 한다는 것은 논리적으로 보입니다. 상태 없는 패킷 필터도 그렇게 작성할 수 있고, 인바운드 쪽의 tcpdump는 요청을 기대한 바로 그 자리에서 보여 줍니다. 호스트 스스로 만들어 내는 트래픽에서는 라우팅이 그렇게 동작하지 않습니다.
서비스가 연결을 수락하면 그 응답은 새로 로컬에서 생성된 패킷입니다. 커널은 응답의 목적지와 소스에 대한 라우팅 규칙을 조회하고, VPN의 기본 경로는 충분히 유효한 답입니다. 소켓에는 어느 인터페이스가 요청을 실어 날랐는지 기억하는 것이 없습니다.
그래서 증상은 한쪽만 보입니다. 요청은 들어오는데 응답은 사라집니다. 그래서 인바운드 포트에 방화벽 규칙을 추가해도 아무것도 바뀌지 않습니다. 패킷은 들어오는 길에서 막히는 것이 아니라 나가는 길에서 다른 곳으로 라우팅됩니다. 소스를 지정한 조회로 응답을 따라가면 우회로가 드러납니다.
커널이 실제로 라우팅하는 것: 소스 주소
필요한 실마리는 서비스가 응답에 사용하는 로컬 주소입니다. eth0 주소가 예시로 192.0.2.10/24이고 게이트웨이가 192.0.2.1인 호스트라면 그 주소는 인터페이스에 존재합니다. 클라우드 VM에서는 공인 주소가 상위에서 변환되어 게스트에 없을 수 있으므로 호스트가 실제로 가진 주소를 사용하세요.
테이블 번호나 규칙 우선순위를 예약하기 전에 현재 상태를 확인하세요. 기존 정책 규칙이 새 규칙을 가릴 수 있고, 같은 번호의 오래된 테이블에 이미 무관한 경로가 들어 있을 수 있습니다:
ip -4 addr show dev eth0
ip -4 rule show
ip -4 route show table all
그다음 소스로 테이블을 선택하세요. 규칙은 이미 192.0.2.10을 가진 응답과 일치하며, 그것이 바로 면제하려는 로컬 종료 트래픽입니다. 전달되는 트래픽과 다른 로컬 주소의 패킷은 일치하지 않습니다.
절반의 기본이 아니라 완전한 반환 테이블을 만드세요
기본 경로만 있는 테이블로는 충분하지 않습니다. 커널은 여전히 게이트웨이를 해석해야 하고, 그 해석에는 같은 테이블의 연결된 프리픽스가 필요합니다. 그 프리픽스가 없는 테이블은 기본 경로가 올바르게 보여도 게이트웨이 해석에서 실패합니다. 둘 다 추가한 다음 소스 규칙을 테이블로 향하게 하세요:
ip -4 route add table 100 192.0.2.0/24 dev eth0 src 192.0.2.10
ip -4 route add table 100 default via 192.0.2.1 dev eth0
ip -4 rule add priority 1000 from 192.0.2.10/32 lookup 100
예시를 복사하지 말고 실제 프리픽스와 게이트웨이를 사용하세요. 온링크 게이트웨이가 있는 /32로 구성된 클라우드 인터페이스에는 다른 연결 경로가 필요하고, 잘못된 프리픽스로 만든 테이블은 첫 홉에서 실패합니다. src 속성은 그 경로의 선호 소스를 밝힐 뿐 소스 규칙을 대체하지 않습니다.
순서도 중요합니다. 우선순위 번호가 낮은 넓은 규칙이 먼저 평가되므로, 이 트래픽을 메인 테이블로 돌려보낼 수 있는 어떤 규칙보다도 소스 규칙에 먼저 도달해야 합니다. 추가한 뒤 ip rule show로 우선순위를 확인하세요. 부팅 때만 확인해서는 안 됩니다.
소스를 지정한 조회로 검증하세요
커널에 서비스 주소에서 시험 목적지로 라우팅하도록 요청하세요. 답은 터널이 아니라 eth0와 WAN 게이트웨이를 지목해야 합니다:
ip -4 route get 198.51.100.25 from 192.0.2.10
그다음 새로 승인된 연결을 만들고 양방향을 관찰하세요. 올바른 테이블을 선택하는데 실제 연결이 여전히 실패한다면 그것은 테이블이 아니라 역경로 필터링이나 방화벽 규칙을 가리킵니다. 무엇이든 전역으로 비활성화하기 전에 그 제어를 확인하세요.
최종 경로와 규칙은 인터페이스를 소유한 네트워크 관리자 아래에 두세요. 주소 변경, DHCP 갱신, VPN 재연결은 모두 커널 상태를 다시 쓰며, 손으로 추가한 규칙은 다음 이벤트 후에 사라지거나 오래된 게이트웨이를 가리킵니다.
이 예외가 멈추는 지점
이 규칙은 한 소스 주소에서 로컬로 종료된 트래픽을 면제합니다. Docker가 게시한 포트, DNAT, 호스트를 통해 전달되는 서비스는 포함하지 않습니다. 그 패킷은 전달 트래픽으로 라우팅되며 호스트의 로컬 소스를 같은 방식으로 갖지 않습니다. 그런 설계에는 보통 인그레스에서 설정한 마크와 그 마크에 맞는 규칙이 필요합니다.
또한 호스트의 공인 주소가 인터페이스에 나타나게 하지도 않습니다. 상위 NAT가 그 주소를 소유할 때 응답은 그 NAT가 기대하는 경로로 나가야 합니다. 그래서 WAN 테이블은 추측한 공인 범위가 아니라 원래 게이트웨이와 프리픽스를 반영해야 합니다.
예외는 테이블 번호, 규칙 우선순위, 정확한 소스 주소와 함께 문서화해 두세요. 다음 VPN이나 인터페이스 변경이 그것을 조용히 깰 수 있고, 세 값을 적은 메모가 고객 보고서에서 증상을 다시 발견하는 것보다 빠르게 확인됩니다.
소스를 지정한 조회가 통과한 뒤에도 서비스가 잘못된 경로로 응답한다면 실제 소켓의 소스 주소를 규칙에 있는 주소와 비교하세요. 와일드카드 주소에 바인딩된 서비스는 시험한 주소와 다른 소스를 고를 수 있으며, 그 불일치가 남은 간극입니다.