DNS 리다이렉트 후 dnsmasq 쿼리가 VPN을 빠져나가는 이유
라우터 쪽 REDIRECT는 LAN DNS 쿼리를 새 리졸버 소켓으로 바꾼다. 클라이언트 흐름의 표시는 결코 그곳에 닿지 않으므로 업스트림 이그레스를 직접 설정해야 한다.
LAN 클라이언트는 자기 VPN에 제대로 도달하는데도 DNS 쿼리는 엉뚱한 상향 링크에 나타난다. 이것은 보통 의미의 유출이 아니다. 라우터가 쿼리 전달을 멈추고 스스로 응답할 때 벌어지는 일이다. 가로채기는 목적지를 로컬 소켓으로 다시 쓰고, 그러면 리졸버는 자기 자신의 출발지 주소와 자기 자신의 라우팅 결정을 가진 두 번째의 무관한 대화를 연다. 클라이언트 흐름에 붙은 표시는 첫 번째 대화를 설명할 뿐 두 번째에 대해서는 아무것도 말하지 않는다.
표시는 쿼리와 함께 이동하지 않는다
패킷 표시는 conntrack 항목과 비행 중인 패킷 위에 존재한다. REDIRECT 규칙은 그 비행을 끝낸다. 페이로드는 로컬 소켓으로 넘어가고 원래 항목은 할 일을 마쳤다. 그 뒤에 라우터를 떠나는 것은 리졸버 프로세스가 만든 새 소켓이며, 출발지는 라우터 자신의 주소다.
그래서 LAN 클라이언트 주소를 키로 삼은 정책 경로가 업스트림 쿼리와 맞는 일은 결코 없다. 새 소켓은 클라이언트가 아니라 dnsmasq에 속하고, 커널은 라우터 자신의 트래픽 기준으로 경로를 평가한다. 출발지 규칙, 인터페이스 규칙, 표시 규칙은 모두 다른 튜플을 본다. 표시가 전파된다고 가정하는 것이 이 구성에서 가장 흔한 실수이고, 클라이언트 쪽 캡처에서는 보이지 않는다.
두 대화는 실제로 이름과 시점을 공유하며, 캡처를 대충 훑을 때 그것들이 하나의 흐름처럼 보이는 이유가 바로 이것이다.
전달이 로컬 확인으로 바뀌는 지점
먼저 포트 53이 리다이렉트되는지, 다른 호스트로 변환되는지, 아니면 그대로 두는지를 밝혀야 한다. 세 경우의 동작이 다르기 때문이다. 로컬 포트로의 리다이렉트는 전달을 끝내지만, 다른 기기로의 변환은 패킷을 계속 움직이게 하고 클라이언트의 튜플을 의미 있게 유지한다.
두 프로토콜을 모두 본다. UDP와 TCP로 도달 가능한 리졸버는 둘 다에서 시험되고, UDP만 덮는 규칙 집합은 큰 응답을 위한 조용한 구멍을 남긴다. 라우터 자체의 구성 관리자가 생성한 규칙도 셈에 들어가며, 생성된 파일을 편집하는 일은 보통 다음 재적재에 진다.
sudo nft list ruleset | grep -n -B2 -A4 'dport 53'
sudo iptables -t nat -L -n -v | grep -E ':53|dns'
sudo ss -lunp | grep ':53'
이 출력들은 세 가지 질문에 차례로 답한다. 무엇이 쿼리를 다시 쓰는지, 무엇이 그것을 받는지, 그리고 어느 프로세스가 대기 소켓을 소유하는지. 소유자가 밝혀지면 업스트림 정책이 머물 자리가 생긴다.
리졸버에 자기만의 이그레스 정책을 준다
일부 이름만 터널이 필요할 때 도메인별 업스트림이 가장 깔끔한 도구다. dnsmasq는 도메인으로 범위를 좁힌 server 줄을 받아들이고, 선택적으로 인터페이스나 출발지 주소를 붙여 쿼리를 지정한 장치 밖으로 내보낸다. 바로 그 유도가 두 번째 대화를 VPN으로 따라가게 한다.
server=/corp.example/10.30.0.53@tun_work
log-queries
위의 도메인, 주소, 인터페이스는 각자의 값으로 바꿔 넣을 자리표시자다. 도메인 선택과 출구 제어는 여전히 별개의 결정이다. 경로가 존재해야 하고, 출발지 주소가 업스트림 리졸버에서 허용되어야 하며, 방화벽이 응답을 허용해야 한다. 인터페이스 유도만으로는 fail-closed가 아니다. 터널이 사라지면 리졸버가 다른 경로로 떨어질 수 있기 때문이다. 공개 대체 경로는 사적으로 유지해야 할 네임스페이스에서 멀리 두어야 한다.
그 줄은 생성된 파일이 아니라 라우터가 지원하는 구성 소유자에 넣어야 한다. 그렇지 않으면 다음 재적재가 수정을 지운다.
공유 캐시가 지킬 수 없는 것
캐시 하나를 가진 포워더 하나가 모든 LAN 클라이언트를 상대하며, 그것은 일부 네트워크가 필요로 하는 구분을 무너뜨린다. 두 클라이언트가 같은 이름을 다르게 확인해야 한다면, 공유 소켓에는 누가 물었는지 되살릴 방법이 없다. 클라이언트별 정책은 별도의 리졸버 인스턴스나 신원을 유지하는 전달 기능 안에 있어야 한다.
캐시된 응답도 새 경로에 대한 증거가 아니다. 업스트림 경로가 깨진 채로도 응답은 메모리에서 나올 수 있다.
포트 53 규칙은 DNS를 HTTPS나 TLS 위에서 말하는 애플리케이션에 대해 아무것도 말하지 않는다. 그런 쿼리는 리다이렉트에 결코 닿지 않으므로 포트 53만으로 세운 정책은 트래픽의 일부만 기술한다 는 점을 문서에 밝혀야 한다.
확인해야 할 것은 두 번째 대화이지 첫 번째가 아니다
자신이 관리하는 이름 하나로 시험하고 양쪽을 살핀다. 클라이언트 쪽 캡처는 쿼리가 LAN을 떠났음을 증명할 뿐이고, 그다음 어디로 갔는지는 라우터 쪽만이 증명한다.
sudo tcpdump -ni tun_work udp port 53
ip route get 10.30.0.53
dig +short corp.example @10.30.0.53
시도할 때마다 캐시를 비운다. 따뜻한 응답은 깨진 업스트림을 감추기 때문이다. TCP로 반복하고, 그다음 터널을 내린 상태로 반복해 선호 경로가 사라졌을 때 리졸버가 어떻게 하는지 본다. 그 마지막 동작이 설계가 fail-closed인지 아니면 그저 운이 좋은 것인지를 가른다.