터널은 올라가 있고 클라이언트는 VPN 호스트에 도달하지만, 호스트는 아무것도 확인하지 못한다. 호스트 자신의 아웃바운드 쿼리는 인터페이스를 깨끗하게 떠나므로, 실패는 돌아오는 경로에 있다. 상위 에지가 호스트가 방금 보낸 것을 기억하지 않은 채 들어오는 트래픽을 필터링하는 것이다. 터널 포트를 허용하는 것은 그 교환을 전혀 설명하지 못한다. DNS 쿼리에 대한 응답은 별개의 흐름이고, 에지가 상태 비저장이면 돌아올 수 있도록 허용한 것이 아무것도 없다.

두 교환은 같은 교환이 아니다

튜플을 적어 보면 실수가 분명해진다. 요청의 목적지 포트가 응답의 출발 포트가 되는데, 이는 53 포트에 대한 인바운드 규칙을 추가할 때 대부분이 떠올리는 것과 정반대다.

query:  203.0.113.10:53018 -> 198.51.100.53:53
reply:  198.51.100.53:53 -> 203.0.113.10:53018

목적지 포트 53을 요구하는 인바운드 규칙은 호스트에서 실행 중인 DNS 수신자로 향하는 트래픽을 설명하며, 그것은 전혀 다른 서비스다. 그 주소들은 문서용 예시이고, 출발 포트는 쿼리마다 클라이언트가 고르므로 규칙에 복사해서 계속 동작할 것이라고 기대할 수 있는 값이 아니다.

상태 비저장 에지와 상태 저장 호스트 방화벽

상태 비저장 에지 필터는 뒤에 연결 테이블 없이 각 인바운드 패킷을 규칙 목록과 대조해 평가한다. 성공적으로 나간 아웃바운드 쿼리는 자신의 인바운드 응답에 대한 허가를 만들지 않는다. 장치에 쿼리가 있었다는 기록이 없기 때문이다. 상태 저장 호스트 방화벽은 반대다. 연결 추적이 허용된 교환의 반환 트래픽을 인식하고 응답용 규칙 없이 통과시킨다.두 경계 모두 일반적인 경로에 존재한다, 그리고 둘 다 의도한 패킷을 허용해야 한다.

비교가 중요한 이유는 두 장치가 서로 다르게 실패하기 때문이다. 응답을 거부하는 상태 저장 방화벽은 드롭을 기록한다. 상태 비저장 에지는 조용히 거부하므로 호스트는 그저 시간 초과에 이른다.

허용은 포트 번호가 아니라 프로토콜을 중심으로 설계한다

규칙은 실제 리졸버 주소, 목적지 호스트, 제품이 지원하는 수신 포트 정책으로 한정한다. DNS의 TCP 경로도 염두에 두어야 한다. 큰 응답이나 잘림으로 인한 폴백은 교환을 TCP로 옮기며 UDP만 다루는 규칙으로는 감당할 수 없다. 에지가 정의된 흐름에 대해 상태 저장 검사를 지원한다면 손으로 쓴 응답 규칙보다 그것을 우선한다. 상태 저장 경로는 클라이언트가 고른 포트를 자동으로 따라가기 때문이다.

그다음 경로상의 어떤 제품이 실제로 트래픽을 필터링하는지 확인한다. 문서화된 IPv4 에지 범위가 IPv6 정책을 세우지는 않으며, 공급자 내부 트래픽은 같은 필터를 아예 지나지 않을 수도 있다.

아무 출발 포트 53이나 허용하는 것이 해결책이 아닌 이유

출발 포트 53이라고 주장하는 모든 것을 받아들이는 규칙은 송신자가 헤더 필드에 적은 것을 그대로 받아들이는 규칙이다. 포트 번호는 송신자를 인증하지 않고 페이로드가 유효한 응답임을 증명하지도 않으므로, 그 규칙은 확인을 더 신뢰할 수 있게 만들지 않으면서 호스트의 노출만 넓힌다. 액세스 목록을 단순화하려고 끄는 대신 출발지 제한과 클라이언트 자체의 응답 검사(출발 포트 무작위화 포함)를 유지한다.

리졸버가 직접 관리하는 서비스라면 그 주소로 제한하는 것으로 충분하다. 공용 리졸버라면 승인한 주소로 제한하고 규칙 주석에 적어 둔다.

올바른 경계에서, 올바른 방향으로 검증한다

같은 쿼리를 호스트와 에지에서 각각 캡처하고, 패킷 수가 아니라 튜플을 비교한다. 양쪽에서 보이는데 호스트 쪽에서 응답이 보이지 않는 쿼리는 인바운드 필터를 가리킨다. 호스트 쪽에서만 보이는 쿼리는 라우팅이나 리졸버 자체를 가리킨다. UDP만 허용했다면 TCP 쿼리로 반복한다. 첫 번째 시험이 볼 수 없는 틈이 바로 거기에 있다.

이를 고치면 호스트가 실제로 필요로 하는 DNS 교환이 완성될 뿐이다. 호스트를 공용 리졸버로 만들지도, 전달된 VPN 흐름을 허용하지도, 무관한 터널 문제의 원인이 DNS였다고 증명하지도 않는다.

관련 글