클라이언트 창에는 Connected라고 나오고 터널 주소도 있는데 브라우저는 계속 돕니다. 이 조합은 정상입니다. 왜냐하면 핸드셰이크 성공은 터널이 만들어졌다는 것만 증명하기때문입니다. 서버가 패킷을 전달할지, 출발지를 다시 쓸지, DNS 질의에 답할지는 말해 주지 않습니다. 대부분이 하는 실수는 서버 설정을 먼저 여는 것입니다. 대신 클라이언트에서 바깥으로 나아가세요. 아래 각 단계가 다음 단계를 확인할 가치가 있는지 알려 줍니다.

클라이언트에서 시작해 바깥으로 나아가기

이 순서대로 세 가지 시험을 하고 첫 번째가 어디서 실패하는지 적어 두세요. 서버의 터널 주소로 ping을 보내면 터널 자체가 트래픽을 나른다는 것이 증명됩니다. 번호로 공인 주소에 ping을 보내면 이름 해석 없이 전달과 변환이 증명됩니다. 그다음 호스트 이름 해석을 시도하세요. DNS를 쓰는 유일한 시험입니다. 결과마다 의심에서 한 계층이 통째로 빠집니다.

브라우저가 더 빠르게 느껴진다고 세 번째 시험으로 건너뛰지 마세요. 페이지 로드 실패는 세 계층을 한꺼번에 덮기 때문에, 거기서 시작하면 잘 되던 터널을 다시 만드는 일이 생깁니다. 실패한 페이지는 셋 중 어디가 깨졌는지 알려 주지 않습니다.

캡처 두 개가 패킷이 멈추는 곳을 보여 줌

클라이언트에서 공인 주소로 ping이 흐르는 동안 터널 인터페이스에서 한 번, 인터넷으로 나가야 하는 상위 링크에서 한 번 캡처하세요. 시간 표시가 맞도록 두 개를 동시에 돌리세요.

sudo tcpdump -ni tun0 -c 20 icmp
sudo tcpdump -ni enp1s0 -c 20 icmp

요청이 터널에는 나타나고 상위 링크에는 전혀 나타나지 않으면 서버가 터널과 케이블 사이에서 버린 것이고, 원인은 전달이나 전달용 방화벽 정책이지 NAT가 아닙니다. 둘 다에 나타나면 서버는 제 몫을 했고 응답이 더 바깥에서 사라진 것이므로, 변환이나 상위 필터, 또는 제공자 쪽 제한을 가리킵니다.

상위 링크 이름은 자습서가 아니라 라우팅 테이블에서 가져오세요. 클라우드 이미지와 이름이 바뀐 어댑터는 안내서의 예시 인터페이스와 거의 일치하지 않고, 잘못된 장치에서 뜬 캡처는 전달하지 않는 서버와 똑같이 보입니다.

ip -4 route get 9.9.9.9

그 출력의 dev 필드가 나중에 NAT 규칙이 필요로 할 이름이니 남겨 두세요.

전달은 커널 스위치이자 방화벽 정책

커널이 애초에 전달에 동의해야 합니다. 현재 값을 읽고, 실행 중인 커널에만 쓰지 말고 설정을 영구로 만드세요.

sysctl net.ipv4.ip_forward
printf 'net.ipv4.ip_forward=1\n' > /etc/sysctl.d/70-vpn-forward.conf
sysctl --system

전달은 절반일 뿐입니다. UFW를 쓰는 호스트에서는 전달 정책이 /etc/default/ufw에서 정해지고, 다시 읽기가 효력을 내기 전에 ACCEPT여야 합니다. 전달 경로의 기본 버리기는 핸드셰이크를 지극히 건강하게 둔 채 터널로 나가려는 모든 패킷을 조용히 버립니다. 처음 마주친 증상이 바로 이것입니다.

nftables를 직접 관리하는 호스트는 같은 질문을 다른 문법으로 마주합니다. 클라이언트 시험이 도는 동안 전달 체인의 카운터를 보세요. 참조되는 체인과 버리는 체인은 설정 파일에서는 똑같아 보입니다.

NAT 규칙에는 실제 출구 인터페이스를 적기

출발지 변환은 실제로 호스트를 떠나는 인터페이스에, 서버가 나눠 주는 풀과 함께 두어야 합니다. 그 규칙의 양쪽 절반이 현실을 서술해야 하고, 어느 쪽도 선택 사항이 아닙니다.

iptables -t nat -A POSTROUTING -s 10.44.0.0/24 -o enp1s0 -j MASQUERADE

고전적인 실패는 안내서에서 복사한 규칙이 -o eth0이라고 말하는데 그 기계의 어댑터 이름이 다른 경우입니다. 규칙은 경고 없이 설치되고 아무것에도 맞지 않으며 클라이언트는 여전히 인터넷이 없습니다. 풀도 같은 실수의 나머지 절반입니다. 서버가 10.44.0.0/24를 나눠 주는데 규칙이 10.8.0.0/24를 덮으면 결코 맞지 않습니다.

규칙 문구를 믿는 대신 카운터를 보세요. 목록은 규칙마다 패킷 수를 알려 주고, 결코 맞지 않는 규칙은 아무리 올바르게 보여도 영에 머뭅니다. 그 숫자에서 무엇을 읽기 전에 실제 클라이언트 트래픽을 일 분간 흘려 주세요.

iptables -t nat -L POSTROUTING -v -n

실제 클라이언트 시험 뒤 패킷 수가 영이면 일치 조건이 틀린 것이지 터널이 깨진 것이 아닙니다. 일치하기 시작하면 규칙 집합을 저장해 재부팅을 견디게 하세요. 셸 기록에만 사는 수정은 첫 재시작에서 사라집니다.

서버가 클라우드 NAT 인스턴스이기도 하면 플랫폼에도 발언권이 있습니다. 주소를 변환하는 인스턴스는 보통 제공자가 그 설정을 마련한 곳에서 출발지·목적지 검사를 꺼 두어야 하고, VPN 포트 자체도 외부에서 닿을 수 있어야 합니다.

DNS는 첫 번째 뒤에 오는 두 번째 실패

이름은 실패하는데 주소로 ping이 통하는 것은 리졸버 문제이며, 그 나름의 원인이 있습니다. 로컬 리졸버를 그대로 두는 클라이언트는 그 질의를 완전한 터널로 보내고 결코 오지 않을 응답을 기다립니다. 로컬 리졸버가 이제 경로의 반대편에 있기 때문입니다.

서버에서 리졸버를 밀어 주어 클라이언트가 어디에 물을지 알게 하세요. 두 항목이면 첫 번째에 닿지 않을 때 대안이 되고, 둘 다 터널이 실제로 허용하는 리졸버여야 합니다.

push "dhcp-option DNS 9.9.9.9"
push "dhcp-option DNS 149.112.112.112"

그래도 Windows 클라이언트는 로컬 리졸버로 되돌아갈 수 있고, 바로 그것을 막으려고 block-outside-dns가 있습니다. 다만 로컬 네트워크만 해석할 수 있는 이름을 잃는 대가가 따릅니다. systemd-resolved를 쓰는 Linux 클라이언트는 스크립트가 설치하지 않으면 밀어 준 리졸버를 무시하는 일이 많으니, 밀어 주기가 무시됐다고 결론짓기 전에 터널 장치의 resolvectl status를 확인하세요.

사용자가 겪는 순서대로 이 여정을 끝내세요. 클라이언트에서 실제 페이지를 열고, 어느 주소로 나가는지 확인하고, 서버를 재부팅한 뒤 다시 연결해 세 시험을 반복하세요. 이 두 번째 확인만이 저장한 규칙과 방화벽 정책, 밀어 준 리졸버가 모두 함께 살아남았음을 증명합니다.

관련 글