VPN이 연결되는 순간 이름 해석이 멈추지만, 공용 주소로 보내는 ping은 여전히 응답하고 방화벽 상태에는 잘못된 것처럼 보이는 것이 없습니다. 정책은 아무것도 바뀌지 않았고, 바뀐 것은 새 인터페이스가 나타난 것이며, 기본 거부가 첫 패킷부터 그것을 덮습니다. UFW는 방향과 인터페이스로 판단하므로 무선 링크용으로 작성한 규칙은 터널을 떠나는 트래픽에 대해 아무 말도 하지 않습니다. 수리는 특정 순서로 적용하는 몇 개의 좁은 규칙입니다.

증상은 터널이 올라온 뒤에만 나타난다

기본 거부 정책은 시킨 대로 정확히 하여, 일치하는 규칙이 없는 것은 존재하는 모든 인터페이스에서 거부됩니다. 규칙을 쓸 때 터널은 존재하지 않았으므로 어느 규칙도 그것을 잡을 수 없고, 늘 있던 거부가 이제 이름 해석에 적용됩니다. 리터럴 주소로 가는 직접 트래픽은 더 넓은 허용이 덮으면 여전히 동작할 수 있으며, 그래서 공용 리졸버로의 ping은 성공하는데 이름 조회는 실패합니다. 빠진 것은 정책이 한 번도 본 적 없는 인터페이스에 대한 규칙입니다.

이것은 질의가 규칙을 가진 인터페이스로 돌아가기 때문에 터널을 내리면 이름이 다시 해석되는 이유이기도 합니다. 터널이 올라와 있을 때만 실패하는 정책은 DNS 문제가 아니며, 리졸버 자체를 조정하는 것이 여기서 가장 많은 시간을 낭비합니다. 리졸버 구성을 읽기 전에 인터페이스 목록을 읽으세요.

세 방향, 세 가지 다른 규칙 집합

하나의 세션은 한 방향 이상에서 규칙을 건드리고, 터널은 그 거의 모두에 두 번째 인터페이스를 더합니다. 트래픽을 먼저 방향과 인터페이스로 정렬하면 아래의 각 행이 제자리에서 자기 규칙을 필요로 하기 때문에 나머지는 기계적이 됩니다. 예시는 tun0과 10.8.0.0/24 풀을 사용하므로, 이름과 범위는 클라이언트 자체 출력에 있는 것으로 바꾸세요.

DirectionExample packetRule that admits itWritten on
Client to the pushed resolverA DNS query to 10.8.0.1Destination port 53 over the tunnelThe client's tun0
Client to the VPN serverA handshake to the server's public addressServer address and port on the physical linkThe client's physical interface
Server to a serviceSSH from the pool to port 22Source pool, destination port, tunnel interfaceThe server's tun0
Anyone to the listenerThe VPN listener itself1194/udp on the public sideThe server's public interface

두 행이 자주 혼동되는데, 둘 다 터널을 언급하기 때문입니다. 리졸버용 규칙은 클라이언트가 터널로 보내는 트래픽을 허용하고, SSH용 규칙은 서버가 그곳에서 받는 트래픽을 허용하며, 둘은 다른 머신에 살고 독립적으로 실패합니다. 가장 자주 잊히는 행은 두 번째인데, 서버의 공용 주소로 가는 클라이언트 트래픽이 허용되기 전에는 클라이언트가 터널을 아예 만들 수 없기 때문입니다. 위에서 아래로 작업하면 모든 실패에는 후보 원인이 하나씩 있습니다.

푸시된 리졸버는 자기 규칙이 필요하다

터널이 DNS를 나르는지는 서버가 무엇을 보내는지에 달려 있습니다. 리졸버 옵션을 푸시하는 서버는 클라이언트 구성에 사설 주소를 넣고, 그 주소는 터널을 통해서만 라우팅할 수 있습니다. 서버가 아무것도 푸시하지 않으면 클라이언트는 원래 있던 리졸버를 유지하고, 아래 규칙에는 허용할 것이 없습니다. 무언가를 쓰기 전에 푸시된 상태와 현재 질의 결과를 확인하세요:

dig +short @10.8.0.1 example.com
resolvectl status tun0
sudo ufw status numbered

상태 출력은 사용 중인 리졸버와 그것이 묶인 링크를 알려 주고, 그 뒤의 경로 조회는 어느 인터페이스가 그 주소로 가는 질의를 나를지 보여 줍니다. 리졸버가 다른 링크에 있으면 문제는 방화벽보다 앞선 단계이고 어떤 규칙도 도움이 되지 않습니다. 둘 다 터널에 동의하면 대부분의 질의는 UDP가 나르고 응답이 잘리면 TCP가 이어받기 때문에 규칙 두 개가 필요합니다. 규칙이 지목할 값이 바로 그것이므로 두 값을 모두 기록하세요:

sudo ufw allow out on tun0 to 10.8.0.1 port 53 proto udp comment 'pushed resolver'
sudo ufw allow out on tun0 to 10.8.0.1 port 53 proto tcp comment 'truncated answers'

안쪽 경로보다 바깥 경로를 먼저 연다

클라이언트는 터널이 존재하기 전에 서버의 공용 주소에 도달해야 하므로, 그 규칙은 물리 링크에 놓이고 가장 먼저 제자리에 있어야 합니다. 리졸버는 허용하지만 서버를 잊는 정책은 클라이언트가 터널을 전혀 만들 수 없게 남겨 두고, 그 증상은 보통 kill switch 탓으로 돌려집니다. 그다음 실제로 터널을 통해 원했던 서비스를, 인터페이스를 통째로 여는 대신 좁은 규칙 하나씩 허용하세요. 좁은 규칙이 거부를 제자리에 남겨 둘 수 있는 이유입니다.

sudo ufw allow out to 198.51.100.10 port 1194 proto udp comment 'vpn server'
sudo ufw allow out on tun0 to any port 443 proto tcp comment 'web through tunnel'

시험할 때는 터널이 애초에 올라오게 하는 것이 첫 규칙이기 때문에 순서가 중요합니다. 그것을 추가하고 연결한 뒤, 둘을 한꺼번에 하면 실패해도 어느 절반이 잘못인지 알 수 없으므로 서비스 규칙을 추가하기 전에 터널이 올라와 있는지 확인하세요. 위의 문서용 주소는 클라이언트가 사용하도록 구성된 엔드포인트로 바꾸세요.

일부러 연결을 끊어 클라이언트 쪽을 마무리합니다. 터널이 올라와 있을 때 응답하는 리졸버는 의심스러울 때 거부하는 정책의 절반만 덮기 때문에 거부는 의도적으로 제외한 트래픽을 여전히 거부해야 합니다.

연결한 클라이언트가 아직 허용된 것은 아니다

서버에는 고려할 자기 방향이 있고, 핸드셰이크를 마친 클라이언트도 서버가 받아들이기 전에는 서버에서 실행되는 무엇에도 닿지 못합니다. 공용 리스너 규칙은 터널이 존재하도록 할 뿐이며, 터널의 트래픽을 들여보내지 않습니다. 서버에서는 규칙 두 개에 더해 서비스가 실제로 수신 중인지 확인하는 검사 하나가 필요합니다:

sudo ufw allow 1194/udp comment 'openvpn listener'
sudo ufw limit in on tun0 from 10.8.0.0/24 to any port 22 proto tcp comment 'admin over tunnel'
ss -ltn | grep ':22'

다른 인터페이스용 규칙은 규칙이 없는 것과 구별되지 않기 때문에 규칙의 풀은 서버가 할당하는 범위와 같아야 하고, 터널 번호도 서버가 만든 것과 일치해야 합니다. limit는 관리 포트에 의도된 것으로, 무제한으로 받아들이는 대신 반복 연결을 제한합니다. 클라이언트 쪽에서는 똑같이 보이기 때문에 리스너 검사는 닫힌 포트와 버려진 패킷을 구분합니다. 번호가 붙은 규칙도 전체 그림은 아니며, UFW의 before 규칙과 손으로 쓴 nftables 줄은 더 먼저 일치하기 때문에 목록이 허용하는 것처럼 보이는 패킷도 여전히 거부할 수 있습니다.

관련 글