VirtualBox NAT와 브리지: VM의 VPN은 어디서 끝나는가
NAT는 게스트를 호스트 뒤에 숨기므로 호스트의 터널이 게스트를 덮는다. 브리지는 게스트에게 자체 주소를 주므로 터널이 VM 안에 있어야 한다.
가상 머신이 VPN 클라이언트를 실행할 때마다 두 가지 다른 질문이 함께 생기고, 답도 각각 다르다. 첫째는 게스트 안의 터널이 호스트를 보호하는가이고, 둘째는 호스트의 터널이 게스트를 덮는가이다. 어댑터 모드가 둘 다를 결정하며, VPN 소프트웨어 자체보다 영향이 크다. NAT 어댑터는 호스트를 보이는 종단으로 만들고, 브리지 어댑터는 게스트를 자체 주소로 네트워크에 올린다.
암호화된 세션을 실제로 끝내는 주체는 누구인가
암호화된 세션은 그것을 끝내는 기계에 속하고, 클라이언트가 VM 안에서 실행될 때 그 기계는 게스트이다. 그 터널로는 게스트의 가상 어댑터로 들어오는 트래픽만 지나가며, 하이퍼바이저는 스스로 다른 기계를 게스트를 거쳐 보내지 않는다. 그래서 게스트가 터널을 유지하는 동안 호스트는 평문 네트워크를 그대로 쓴다. 반대 방향은 대칭이 아니고, 대부분의 안내서가 틀리는 지점이 바로 이 비대칭이다.
바깥 네트워크가 누구의 주소를 보는가는 어댑터 모드가 답하는 질문이다. 그것이 정해지면 적용 범위는 정책 문제가 아니라 기계적인 문제가 된다. 아래의 모든 내용은 이 두 사실에서 나온다.
NAT는 게스트를 호스트처럼 보일 때까지 다시 쓴다
NAT에서는 하이퍼바이저가 게스트의 모든 패킷을 첫 스위치에 도달하기 전에 호스트 주소로 다시 쓰고, 그 트래픽은 호스트 자체의 네트워크 스택으로 넘어가 호스트의 라우팅 표를 따른다. 호스트의 터널이 소유한 기본 경로도 그 표의 일부다. 따라서 NAT 게스트는 아무 설정 없이 호스트의 터널로 덮이며, 이는 게스트에서 호스트로 가는 경우가 시사하는 것과 반대다.
# on the host: adapter mode and NAT DNS behaviour
VBoxManage showvminfo "lab-vm" | grep -i -E "nic|nat"
VBoxManage modifyvm "lab-vm" --natdnshostresolver1 on
자체 클라이언트를 실행하는 게스트는 그 터널을 게스트 안에서 끝내고, 암호화된 트래픽은 같은 재작성을 거쳐 나가므로 호스트 애플리케이션은 자신의 경로와 자신의 출구 주소를 유지한다. VirtualBox는 이 모드에서 이름 해석도 담당하며, --natdnshostresolver1이 켜져 있지 않으면 게스트의 질의를 자체 엔진으로 중계한다. 이를 켜면 게스트의 조회가 호스트의 해석기로 넘어가고, 호스트 자신의 시야가 옳을 때 해석이 바로잡히며, 동시에 터널 안에만 존재할 수 있는 해석기를 그대로 넘기게 된다. 설정을 바꾸기 전에 VM을 꺼야 하고, 그 설정은 게스트 소프트웨어가 아니라 어댑터에 속한다.
브리지는 게스트에게 자체 임대와 자체 노출을 준다
브리지는 게스트의 프레임을 물리 세그먼트로 보내므로 DHCP가 임대를 주고 네트워크는 게스트를 별도 호스트로 취급한다. 브리지된 게스트는 각자 자체 기본 경로를 가진 독립 터널을 유지할 수 있고, 호스트의 표는 바뀌지 않는다. 대가는 노출이며, 게스트는 브로드캐스트에 응답하고 이웃 표에 나타나며 스위치 포트 보안과 무선 클라이언트 격리의 대상이 된다. 호스트의 터널도 이 구성은 덮지 못하는데, 게스트의 프레임이 호스트 스택을 지나지 않기 때문이다.
NAT 경우와의 이 차이는 함축으로 남기지 말고 분명히 말할 가치가 있다. 브리지된 게스트는 트래픽이 터널을 써야 할 때마다 자체 클라이언트와 자체 자격 증명이 필요하다. 게스트가 로컬 네트워크에 보이지 않아야 하는 곳에서는 NAT나 라우팅을 통제한 내부 전용 네트워크가 더 나은 설계다.
게스트가 호스트의 게이트웨이가 될 수 없는 이유
게스트의 터널을 호스트에게 빌려주는 일은 두 대형 하이퍼바이저 어디에도 지원되는 스위치가 없다. 호스트에는 게스트 터널로 들어가는 경로도, 그것을 가리키는 변환 규칙도 없으므로 게스트와 호스트의 트래픽은 서로 다른 주인을 가진 별개 흐름으로 남는다. 이 방향은 어댑터 모드와 무관하게 실패한다.
쓸 만한 대안은 전달과 프록시다. SSH 로컬 전달을 쓰면 게스트가 흐름을 터널로 옮기는 동안 호스트는 자신의 루프백에 접속할 수 있고, 두 번째 클라이언트를 설치하지 않고 TCP 서비스를 덮을 수 있다. 호스트 자신의 IP 트래픽이 대신 게스트의 터널을 쓰게 하려면 게스트 안의 라우터와 호스트의 명시적 경로, 그리고 호스트의 변환이 필요하다. 그것은 체크 상자가 아니라 작은 네트워크 설계이고, 돌아오는 경로에 대한 계획이 필요하다. 게스트가 클라이언트를 실행하기 위해 존재한다면 클라이언트를 호스트에 두고 불필요한 홉을 없애라.
ssh -N -f -L 8080:internal-host.example:80 user@lab-vm
해석기와 경로는 경계를 넘지 않는다
DNS는 그 옵션을 받은 어댑터를 따르므로 게스트가 호스트 터널의 해석기를 물려받지 않고, 호스트는 게스트에게 밀어 넣은 서버를 알지 못한다. 밀어 넣은 dhcp-option DNS 값은 그것을 받아들인 인터페이스에 적용되므로, 클라이언트가 없는 브리지 게스트는 내부 이름을 해석할 수 없다. NAT 게스트는 보통 대신 하이퍼바이저의 DNS 처리를 쓰고, 그 차이는 환경마다 명령 하나로 드러난다.
# on the host
VBoxManage showvminfo "lab-vm" | grep -i -E "nic|nat"
# inside a Linux guest
ip -4 route
resolvectl status
# inside a Windows guest
Get-DnsClientServerAddress
둘 중 하나가 호스트를 따른다고 가정하기 전에 게스트 안에서 해석기와 경로를 읽어라. 내부 이름을 거기서 해석해야 한다면 터널 해석기를 게스트 자체 어댑터에 추가하고, 그다음 터널 뒤에만 존재하는 이름으로 확인하라. VM마다 어댑터 모드와 터널 소유자, 해석기 출처를 기록하라, 이 세 사실이 이후의 거의 모든 질문에 답하기 때문이다. 다른 하이퍼바이저는 모드 이름을 바꾸지만 원칙은 그대로 둔다.