OpenVPN 터널을 통한 IPv6: 가정용 라우터를 따라가는 점검
터널이 라우터가 경로를 잡을 수 없는 프리픽스를 내주면 IPv6는 죽거나 새어 나갑니다. 클라이언트, 풀, 포워딩, 프록시 NDP, 귀환 경로를 차례로 살펴봅니다.
노트북에는 IPv6 주소가 있고 터널은 IPv4 위에서 올라와 있는데도 모든 IPv6 테스트가 실패합니다. 더 나쁜 경우에는 테스트가 성공하고 트래픽이 터널에 들어가지 않은 채 ISP 경로로 빠져나갑니다. 두 결과 모두 라우터에 클라이언트 쪽으로 라우팅할 수 있는 프리픽스를 준 사람이 없다는 하나의 빠진 결정으로 돌아갑니다. 클라이언트에서 바깥으로 경로를 따라가면 어느 조각이 빠졌는지 알 수 있습니다.
클라이언트가 있는 곳에서 시작합니다
서버를 건드리기 전에 클라이언트를 봅니다. ip -6 addr은 풀에서 나온 터널 주소를 보여 주어야 하고, ip -6 route은 터널 장치를 가리키는 푸시된 프리픽스를 보여 주어야 합니다. 주소는 있는데 경로가 없으면 푸시가 빠졌거나 클라이언트가 무시한 것입니다. 둘 다 없으면 서버가 아무것도 할당하지 않은 것입니다.
다음으로 클라이언트가 바깥세상을 어떻게 보는지 확인합니다. 터널을 지나는 기본 경로는 모든 IPv6 트래픽을 주장하고, 2000::/3에 대한 경로는 전역 유니캐스트만 주장합니다. 둘 다 맞을 수 있으며 누출에 대해 서로 다른 질문에 답합니다.
IPv6 기본 경로가 아예 없는 클라이언트는 터널 자체가 완전히 건강해도 모든 공개 테스트에서 실패합니다. 그것은 정책의 빈틈이지 고장 난 터널이 아닙니다.
서버가 실제로 라우팅할 수 있는 풀을 확인합니다
서버는 자신이 실제로 제어하는 프리픽스에서만 할당할 수 있습니다. ISP가 /56을 위임한다면 터널용으로 /64 하나를 남기고 나머지는 LAN에 둡니다. ISP가 WAN에서 /64 하나만 넘기고 더는 라우팅하지 않는다면 LAN 프리픽스는 그대로 두고 터널에 고유한 로컬 범위를 주며, 클라이언트에 전역 출구가 없다는 점을 받아들입니다.
풀과 푸시는 함께 갑니다. server-ipv6 2001:db8:80:10::/64은 주소를 할당하고, push "route-ipv6 2000::/3"는 클라이언트에 터널로 무엇을 보낼지 알려 줍니다. 2000::/3을 푸시하면 전역 유니캐스트를 덮고 링크 로컬과 멀티캐스트 범위는 터널 밖에 남습니다.
고른 프리픽스를 배포 기록에 적어 둡니다. 다음에 ISP 인계를 바꾸는 사람은 터널이 이미 어떤 /64를 차지하는지 알아야 합니다.
두 번째 패밀리를 위한 포워딩을 켭니다
IPv4 포워딩이 IPv6 포워딩을 뜻하지는 않습니다. net.ipv6.conf.all.forwarding=1을 설정하고 IPv4 FORWARD 정책이 이미 허용하는 것을 그대로 옮깁니다. FORWARD 체인이 기본적으로 버리는 라우터에서는 규칙 집합이 빠진 모습이 죽은 터널과 똑같이 보입니다.
핸드셰이크는 성공하고 클라이언트는 주소를 받지만 돌아오는 것은 없습니다. LAN에는 닿지만 인터넷에는 닿지 않는 터널은 세 번째 테스트에서 실패하므로 양방향과 WAN 구간에 규칙을 추가합니다. 실패한 시도 뒤에는 체인 카운터를 보되, 한 번도 일치하지 않은 규칙은 일치해서 버린 규칙과 다른 문제입니다.
ip6tables -A FORWARD -i br0 -o tun0 -j ACCEPT
ip6tables -A FORWARD -i tun0 -o br0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -i tun0 -o wan0 -j ACCEPT
부작용 하나가 결국 터널을 탓하는 사람을 잡습니다. 포워딩을 켜면 커널이 따로 말하지 않는 한 라우터 광고를 통한 주소 구성을 건너뛰고, 그렇게 배운 기본 경로도 함께 사라집니다. WAN 인터페이스에 명시적 기본 경로를 고정하거나, 거기서 accept_ra을 의도적으로 2로 둡니다. 어느 쪽이든 바꾼 뒤에는 클라이언트를 다시 시험하기 전에 라우터에 여전히 쓸 만한 IPv6 출구 경로가 있는지 확인합니다.
상류가 라우팅하지 않을 때 이웃 탐색에 답합니다
상류 장비는 때때로 위임된 /64를 자기 링크의 것으로 취급하고 어떤 하위 프리픽스도 VPN 상자로 라우팅하기를 거부합니다. 그럴 때는 무언가가 각 클라이언트 주소마다 이웃 요청에 개별적으로 답해야 합니다.
OpenVPN은 할당된 주소를 client-connect 훅에 노출하므로 항목이 손으로 관리하는 목록 대신 실제 풀을 따를 수 있습니다. 훅은 ip -6 neigh add proxy $ifconfig_pool_remote_ip6 dev eth0을 추가하고, 짝을 이루는 client-disconnect 훅이 그것을 제거합니다. 제거하지 않으면 프록시 표가 낡고 나중에 온 클라이언트가 받은 적 없는 주소의 항목을 물려받을 수 있습니다. 상태는 ip -6 neigh show proxy으로 확인합니다.
프록시는 임시방편으로 봅니다. 터널의 /64를 라우터로 향하게 한 정적 경로는 클라이언트 교체를 견디고 권한 있는 셸 훅이 전혀 필요 없습니다.
바깥으로 경로를 한 홉씩 따라갑니다
바깥으로 세 단계로 시험합니다, 먼저 라우터의 터널 주소, 다음 LAN 호스트, 세 번째로 traceroute6을 통한 공개 목적지입니다. 풀은 첫 단계가, 포워딩과 LAN 방화벽은 두 번째가, 상류를 거치는 귀환 경로는 세 번째가 증명합니다. 실패할 때마다 설계 전체를 다시 여는 대신 찾을 범위를 장치 하나로 좁힙니다.
터널 MTU를 지켜봅니다, 경로의 IPv6 라우터가 대신 단편화해 주지 않기 때문입니다. 너무 큰 MTU는 깔끔한 실패가 아니라 packet-too-big 오류와 멈춘 TLS로 나타납니다. 명시적으로 설정하고 한계에 가까운 페이로드로 시험합니다.