OpenVPN 뒤의 Docker와 VM 트래픽: 경로인가, 재번호인가
OpenVPN 클라이언트가 도는 호스트의 컨테이너와 게스트는 반환 경로를 잃거나 터널 접두사와 충돌한다. 세 가지 질문이 해법을 고른다.
호스트는 터널로 브라우징하는데 옆의 컨테이너는 아무것도 닿지 않는다. 자체 VPN 클라이언트를 돌리는 컨테이너에서 포트를 게시하면 더 나쁘다. 요청은 도착하지만 응답은 결코 나가지 않는다. 둘 다 호스트의 라우팅 결정이며, Docker는 전달자일 뿐이다.
두 경우 중 어느 쪽인지는 세 가지 질문으로 정해지고, 오래 걸리지 않는다.
같은 모습으로 나타나는 두 가지 장애
첫째는 반환 경로 문제다. 게시한 포트가 컨테이너로 변환되고, 요청이 도착하며, 응답은 호스트의 기본 경로로 나간다. 그 경로를 OpenVPN 클라이언트가 tun0으로 돌려놓았다. 응답은 원격 피어가 예상하지 못한 주소에서 나가고, 버려진다. 둘째는 접두사 충돌이다. Docker의 기본 브리지가 172.17.0.0/16을 차지하는데 기업 터널이 같은 범위를 밀어 넣는다.
하나는 라우팅 테이블로, 다른 하나는 브리지 재번호로 고친다. 어느 쪽인지 확인하기 전에 손대는 것이 바로 멀쩡한 컨테이너를 망가뜨리는 길이다.
세 가지 질문으로 가른다
질문 하나: VPN을 멈추면 장애가 사라지는가. 터널을 내린 상태에서 모두 동작한다면 반환 경로를 의심하라. 질문 둘: ip route show table all 이 같은 접두사를 두 번, 브리지에서 한 번 tun0에서 한 번 보여주는가. 그것이 충돌이다. 질문 셋: 영향을 받은 주소가 컨테이너인가, NAT 게스트인가, 브리지 게스트인가.
셋째 질문이 중요한 이유는 각각 올바른 수정이 다르기 때문이다. 컨테이너용 수정을 libvirt 게스트에 적용하면 저녁 하나를 버린다.
ip route
ip route show table all | grep -E '172\.1[7-9]|10\.8'
docker network inspect bridge --format '{{.IPAM.Config}}'반환 경로: 원본 주소 하나를 위한 테이블
컨테이너 트래픽은 물리 인터페이스로 내보내야 하는데 호스트 자신은 터널을 쓰는 경우, 원본 기반 라우팅 이 가장 작은 변경이다. 컨테이너 주소와 일치하는 규칙을 추가하고 전용 테이블로 보낸다. 호스트의 다른 것은 움직이지 않는다.
ip rule add from 172.22.0.3 lookup 10
ip route add default via 172.22.0.1 table 10원본이 그 주소인 모든 패킷은 먼저 테이블 10을 보고, 테이블 10은 터널 대신 브리지 게이트웨이로 보낸다.
다음으로 확인한다: ip route get 1.1.1.1 from 172.22.0.3. 응답에는 브리지 인터페이스가 나와야 하고 tun0이 아니어야 한다. 이렇게 추가한 규칙은 재부팅하면 사라지므로, 컨테이너를 소유한 unit나 네트워크 훅에 넣어 둔다.
같은 패턴은 WireGuard 컨테이너에도 통한다. 거기서도 응답 경로에 명시적 규칙이 필요하다.
접두사 충돌: 브리지를 재번호한다
라우팅 테이블은 접두사 하나를 한 곳으로 보낸다. VPN이 172.17.0.0/16을 밀어 넣는데 Docker 브리지가 이미 그 자리에 있으면 메트릭이 낮은 쪽이 이긴다. Docker는 연결 경로를 메트릭 0으로 넣고, 밀려 들어온 경로는 50으로 도착한다. 그러면 Docker는 계속 동작하는데 터널만 끊긴 것처럼 보이거나 그 반대가 되며, 설치 순서에 따라 달라진다. 메트릭과 싸우는 대신 브리지를 재번호한다.
{
"bip": "10.200.0.1/24",
"default-address-pools": [{"base": "10.201.0.0/16", "size": 24}]
}변경 후 Docker를 재시작하고 옛 풀로 만든 네트워크를 다시 만든다. 대신 메트릭을 바꾸면 충돌이 다음 부팅으로 옮겨질 뿐이다.
VM에는 또 다른 답이 필요하다
기본 NAT 네트워크의 libvirt 게스트는 호스트의 라우팅을 물려받고, 호스트 터널이 기본 경로를 가지는 순간 바깥으로 나가지 못한다. 게스트를 브리지나 macvtap 장치로 옮기면 물리 네트워크에서 자기 경로를 얻는다. virt-manager에서 하드웨어를 추가하고 Network를 고르고 소스를 macvtap 호스트 장치로 두고 모델은 virtio로 유지한 다음, 옛 NAT 인터페이스를 비활성화한다.
macvtap 게스트는 보통 자기 호스트에 닿지 못한다. 그것은 고장이 아니라 예상된 동작이며, 남기는 메모에 들어가야 한다.
호스트의 LAN만 필요한 게스트라면 벤더 클라이언트에 빠진 스위치가 있을 수 있다. Mullvad라면 mullvad lan set allow 가 libvirt를 건드리지 않고 로컬 도달성을 되돌린다. 그리고 컨테이너를 터널로만 닿게 해야 한다면 게시 포트를 터널 주소에 묶는다. -p 10.8.0.5:8080:8080 처럼 쓰고 모든 주소에 게시하지는 않는다.