한 고객 네트워크에 접속하려고 두 번째 VPN 클라이언트를 설치하는 것이 이 일이 시작되는 흔한 방식이며, 두 클라이언트가 모두 성공을 보고하는데 첫 번째 터널은 어두워집니다. 각 클라이언트가 기본 경로를 설치했고, 두 번째가 더 구체적이었다, 이제 커널은 첫 번째 터널 자체의 외부 연결을 제외한 모든 것에 그 경로를 따릅니다. 아무것도 죽지 않았고 어떤 프로필도 잘못되지 않았습니다. 한 경로가 다른 경로를 대체했고, 해결책은 어느 터널이 기본 경로를 가져야 하는지 정하는 것입니다.

증상: 두 번째 클라이언트가 조용히 이긴다

OpenVPN의 redirect-gateway def1 옵션은 0.0.0.0/0 을 덮어쓰지 않습니다. 0.0.0.0/1 와 128.0.0.0/1 을 추가하며, 둘을 합치면 모든 IPv4 주소를 덮고 각 프리픽스는 길이에서 단순한 기본 경로를 이깁니다. 원래 기본 경로는 테이블에 남아 일반 트래픽에 쓰이지 않게 되고, VPN 서버로 가는 호스트 경로는 터널이 자체 외부 패킷을 잡지 않게 합니다. 두 클라이언트 모두 이렇게 하며, 두 번째는 자기 쌍을 나중에 설치할 뿐입니다.

그래서 첫 번째 터널은 끊긴 것이 아니라 죽은 것처럼 보입니다. 인터페이스는 올라가 있고 제어 채널은 살아 있으며 어떤 애플리케이션 트래픽도 더 이상 닿지 않습니다. 반대 불만도 같은 푸시 부재에서 나오는데, 연결은 되지만 아무것도 보내지 않는 휴대폰은 보통 redirect-gateway 를 한 번도 알리지 않은 서버와 대화하므로 기기는 자기 기본 경로를 유지하고 풀에만 닿습니다.

두 번째 클라이언트가 연결되기 전과 후에 라우팅 스냅숏을 찍으세요. 테이블에 나타나는 두 개의 반쪽 경로가 전부 변화이며, 두 불만을 모두 설명합니다. 증상이 나타나려고 테이블의 다른 무엇이 움직일 필요는 없습니다. 그래서 두 번째 클라이언트를 제거하면 첫 번째가 즉시 복구됩니다.

어느 터널이 실제로 이기는지 읽으세요

클라이언트가 시작된 순서나 트레이 아이콘으로 승자를 추론하지 마세요. 두 사설 네트워크 밖의 목적지에 커널이 어느 경로를 쓸지 물어보고, 돌아온 인터페이스를 읽으세요.

ip route show
ip route get 198.51.100.24
route print -4
Find-NetRoute -RemoteIPAddress 198.51.100.24

앞의 두 명령은 Linux의 답이고 뒤의 두 명령은 Windows의 대응입니다. 두 터널이 모두 올라간 상태에서 실행하고, 그다음 하나를 끊고 다시 실행하세요. 두 출력의 차이가 해결책을 고르기 전에 필요한 사실이며, 어느 클라이언트의 로그를 읽는 것보다 시간이 덜 걸립니다.

결과를 인터페이스 이름과 일치한 프리픽스도 포함해 기록하세요. 두 번째 VPN이 첫 번째를 망가뜨렸다고 적힌 티켓은 증상 설명이지 누구나 행동할 수 있는 라우팅 진술이 아닙니다. 이 조회가 그것을 다음 엔지니어가 한 명령으로 재현할 수 있는 사실로 바꿉니다.

Windows: 프로필 하나가 기본 경로를 갖지 않게 하기

Windows는 저장된 프로필을 유지하며, 내장 클라이언트는 하나가 다른 하나에 의존하게 해 두 번째가 연결되면 첫 번째가 자동으로 올라오게 합니다. 가장 최근에 연결된 프로필이 기본 경로를 유지하는 경향이 있으므로 실질적 소유자는 첫 번째가 아니라 마지막 연결입니다. 각 프로필에 자체 푸시 경로를 주고 기본 경로를 가져서는 안 되는 프로필에는 분할 터널링을 켜세요.

Set-VpnConnection -Name "Contoso" -SplitTunneling $true
rasdial Contoso

분할 터널링은 이를 바꿔, 서버가 푸시한 프리픽스만 테이블에 들어가므로 기본 경로는 첫 번째 터널에 남습니다. 두 서버가 모두 기본 경로를 푸시하면 설계는 여전히 실패하는데, 그때는 마지막 연결이 이기고 클라이언트 쪽 설정으로는 바꿀 수 없습니다. 서버 쪽 푸시를 고치거나 소유자 하나를 받아들이세요.

앱으로 트리거되는 프로필은 다른 기능이며 알아둘 만한데, Windows 프로필은 지정된 실행 파일이 시작될 때 시작될 수 있고 트래픽 필터가 무엇을 실을지 결정합니다. 이것은 방화벽 규칙이 아니라 프로필 설정이며, 기본 경로 충돌과는 다른 문제를 풉니다.

Linux: 두 번째 터널을 메인 테이블 밖에 두기

한 머신에서 openvpn --config 인스턴스 둘을 돌리면 두 클라이언트가 같은 테이블에 들어가고, 세 가지 선택지가 격리를 높이며 이들을 나눕니다. 가장 가벼운 방법은 두 번째 클라이언트가 테이블을 아예 편집하지 못하게 하는 것으로, 명시적 경로와 함께 route-nopull 을 쓰거나 의도한 것만 설치하는 route-up 스크립트를 씁니다. 그러면 두 번째 터널은 지정한 프리픽스만 싣고 그 밖의 것은 싣지 않습니다. 그러면 나중에 발견하는 대신 어느 프리픽스를 어디로 보낼지 직접 정합니다.

정책 라우팅은 중간 선택지입니다. 규칙이 하나의 소스 주소나 방화벽 마크에 별도 테이블을 선택하고, 그 테이블이 두 번째 터널의 경로를 싣습니다. 여기서 함정은 역경로 필터링인데, 테이블이 고를 인터페이스와 다른 인터페이스로 도착한 응답은 버려지므로 터널이 망가졌다고 결론 내리기 전에 rp_filter 설정을 확인하세요.

ip rule add from 10.44.7.0/24 table 100
ip route add default dev tun1 table 100
sysctl net.ipv4.conf.all.rp_filter

네트워크 네임스페이스는 가장 무겁고 가장 깨끗한 선택지입니다. 두 번째 클라이언트를 ip netns 안에서 시작하고 veth 쌍을 주고, 사용하려면 그 네임스페이스로 들어가세요. 각 네임스페이스가 테이블 하나를 공유하는 대신 자체 테이블을 가지므로 간섭이 사라지고, 두 터널을 몇 주씩 올려 둬야 한다면 그 추가 설정이 값어치 있습니다.

애플리케이션별로, 그리고 연결은 별도 문제로

때로는 프로그램 하나만 두 번째 터널을 써야 합니다. Linux에서는 사용자, cgroup, 포트별 정책 라우팅으로 그 결과를 얻고, 기본 경로를 전혀 건드리지 않고 한 목적지를 인터페이스에 고정할 수 있습니다. 소켓을 한 인터페이스에 고정한 애플리케이션은 라우팅 테이블을 아예 참조하지 않으므로, 런처는 자체 설정에서 주소를 고를 때까지 물리 어댑터를 계속 쓸 수 있습니다.

연결은 두 터널을 나란히 돌리는 것과 다른 작업입니다. A를 B 위로 연결한다는 것은 안쪽 서버 주소가 바깥 터널 안에서 도달 가능해야 한다는 뜻입니다. 바깥 클라이언트도 안쪽 클라이언트의 암호화된 패킷을 유출로 오해하면 안 됩니다. 가상 머신 안의 클라이언트 하나는 투박하지만 믿을 만한 판이고, 한 호스트에 둘 다 두려면 위의 라우팅 작업에 더해 안쪽 터널로 절대 들어가지 않는 바깥 경로가 필요합니다.

기본 경로의 소유자, 각 터널이 싣는 프리픽스 목록, DNS 경로를 적어 두세요. 그 세 가지 사실이 다음 사건을 조사 대신 조회로 바꿉니다.

관련 글