클라이언트는 연결됐다고 하고, 터널 주소는 ping에 응답하며, 사무실 LAN에서는 아무것도 응답하지 않는다. 나는 거의 모든 클라이언트 업그레이드 후에 이 상황을 보고, 첫 직감으로 방화벽을 열지만 그건 대개 잘못된 조치다. 터널은 정상이고 끊김은 패킷이 지나야 하는 세 홉 중 하나에 있으므로, 패킷이 지나는 순서대로 클라이언트 경로, 서버의 포워딩, 돌아오는 길을 따라가면 몇 분 안에 결함의 주인을 찾게 된다.

첫 단계: 클라이언트의 라우팅 결정을 살펴본다

서버를 건드리기 전에 클라이언트가 패킷을 터널로 보내는지 증명하라. 전체 테이블을 읽는 대신 특정 LAN 호스트 하나에 대해 커널이 어느 인터페이스를 쓸지 물어보라:

ip route get 192.168.50.10

답이 물리 어댑터와 사무실 게이트웨이를 지목한다면 서버 쪽 변경으로는 도울 수 없다. OpenVPN는 서버가 밀어줄 때만 경로를 배포하므로 서버 설정에서 push "route 192.168.50.0 255.255.255.0"를 찾고, 그다음 클라이언트를 다시 연결하라. 대부분의 클라이언트는 밀어준 경로를 연결 시점에 적용하지, 프로필을 다시 로드할 때 적용하지 않는다.

Windows에서는 같은 질문에 다른 명령이 있다. Find-NetRoute -RemoteIPAddress 192.168.50.10는 스택이 고를 인터페이스와 다음 홉을 출력하고, route print -4는 밀어준 프리픽스가 아예 있는지 보여준다.

둘째 단계: 서버가 패킷을 포워딩하는지 확인한다

클라이언트 경로가 맞다면 패킷은 서버에 도달하고, 서버는 포워딩에 동의해야 한다. 커널 스위치가 첫 관문이며 sysctl net.ipv4.ip_forward는 1을 반환해야 한다. echo 1 > /proc/sys/net/ipv4/ip_forward로 설정하면 다음 재부팅까지만 유지되고, 바로 그 때문에 같은 불만이 몇 주 뒤에 돌아온다.

재시작을 견디는 곳에 적어 두라:

echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-openvpn-forward.conf
sysctl --system

둘째 관문은 필터 테이블이다. Docker가 설치된 호스트는 FORWARD 정책이 DROP인 경우가 많고, 터널은 그 결정을 물려받는다. iptables -L FORWARD -v -n는 정책과 패킷 카운터를 모두 보여주며, 정책이 DROP이면 포워딩에는 터널에서 LAN 방향으로 명시적 허용 하나와 응답을 위한 허용 하나가 필요하다.

셋째 단계: 패킷을 LAN까지 따라가 응답을 찾는다

서버 양쪽을 동시에 지켜보라. 터널과 LAN 인터페이스에서 캡처하되, 출력이 읽히도록 클라이언트 하나와 호스트 하나로 필터링하라:

tcpdump -ni tun0 host 192.168.50.10
tcpdump -ni eth0 host 192.168.50.10

tun0에서 나타나고 다시 eth0에서 10.8.0.x를 출발지로 나타난 뒤 침묵이 이어진다면, 패킷은 도착했고 응답은 다른 곳으로 갔다는 뜻이다. LAN 호스트는 기본 게이트웨이로 응답하고, 그 게이트웨이에는 VPN 풀에 대한 경로가 없으며, 응답은 LAN 라우터에서 죽는다. 서버에서 LAN 호스트로 ping해도, 그 트래픽은 터널을 절대 지나지 않으므로 이 경로에 대해서는 아무것도 증명하지 못한다.

넷째 단계: 변환과 반환 경로 중에서 고른다

두 가지 수리가 구멍을 막고 그 선택은 ping이 되는지 여부를 넘어선 결과를 낳는다. 출발지 변환은 서버를 겉보기 송신자로 만든다:

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

규칙을 풀과 LAN 인터페이스로 한정한 뒤 영구화하지 않으면 다음 방화벽 재로드가 조용히 수리를 지운다. 대가는 정체성으로, LAN 로그와 접근 규칙에서 모든 클라이언트가 VPN 서버처럼 보이고 per-client policy becomes guesswork.

LAN 라우터에 VPN 서버를 가리키는 10.8.0.0/24 정적 경로를 두면 클라이언트 주소가 보이고, 그 라우터가 당신 것이라면 장기적으로 더 나은 답이다. 많은 사이트가 한동안 즉시 수리에는 NAT를, 방화벽 정책을 쓴 뒤에는 경로를 함께 운영한다. 서버가 풀이 아니라 사이트 간 피어라면 서버 쪽에도 원격 서브넷용 iroute가 필요하며, 그렇지 않으면 피어 경로는 있어도 클라이언트 트래픽이 그것과 연결되지 않는다.

다섯째 단계: 클라이언트에서 검증하고 무엇이 바뀌었는지 기록한다

서버가 아니라 클라이언트에서 고리를 닫아라. LAN 호스트의 실제 서비스에 연결한 뒤 iptables -t nat -L POSTROUTING -v -n로 변환 카운터가 움직였는지 확인하라. 패킷이 영인 규칙은 한 번도 일치한 적이 없고, 반환 방향이 추적된다는 증거가 필요할 때 conntrack -L -s 10.8.0.5가 흐름과 그 상태를 보여준다.

서버 설정에 어느 서브넷, 어느 인터페이스, 어느 규칙, 그리고 LAN 라우터가 반환 경로를 갖는지 메모를 남겨라. 다음에 클라이언트 업그레이드가 밀어준 경로를 지우면, 그 메모가 불 끄기 작업을 한 줄짜리 수리로 바꾼다.

관련 글