킬 스위치는 IPv4만 차단한다: IPv6는 왜 여전히 빠져나가는가
IPv4용 방화벽 규칙 집합은 IPv6에 대해 아무 말도 하지 않는다. 이중 스택 기기에서는 앱이 우회 경로를 유지하는 동안 터널이 끊어질 수 있다.
터널이 끊어지고, 킬 스위치가 작동했다고 보고하며, 다운로드는 계속된다. IPv4 규칙에는 아무것도 깨진 것이 없다. 앱이 그 규칙이 애초에 작성되지 않은 경로로 옮겨 갔을 뿐이다.
이중 스택 기기에는 두 개의 독립적인 출구가 있다. 둘 중 하나를 막는 것은 절반의 차단이다.
두 개의 규칙 집합, 두 개의 기본값
Linux에서 iptables와 ip6tables는 별도의 정책을 가진 별도의 테이블이다. IPv4 테이블의 INPUT과 OUTPUT 체인을 DROP으로 설정해도 IPv6 테이블에서는 아무것도 바뀌지 않으며, 그곳의 기본값은 보통 ACCEPT다. 같은 분리가 nftables에도 있어서, 한 패밀리를 살펴보면 둘 중 하나만 당신의 규칙을 담고 있음을 알 수 있다. 그 비대칭은 정상이며, 문제 전체가 바로 그것이다.
그래서킬 스위치가 철저해 보이면서도 부분적으로 작동할 수 있다. IPv4 규칙을 작성한 스크립트는 실행되었고, 카운터는 올라가고, 로그 줄은 나타나며, IPv6 패킷은 물리 인터페이스를 통해 완전히 손대지 않은 채 빠져나간다.
클라이언트가 실제로 무엇을 설치하는지 읽어야지, 마케팅 페이지가 약속하는 것이 아니다. 일부 클라이언트는 IPv6를 터널로 보내고, 일부는 의도적으로 차단하며, 일부는 플랫폼 수준의 잠금이 다른 규칙을 적용하는 동안 그대로 둔다. 여기서 신뢰할 만한 유일한 출처는 공급업체 문서다.
인터페이스 구성이 알려주는 것
wg-quick의매뉴얼 페이지는 예시 구성에 IPv6 규칙을 포함하며, 그 이유가 바로 이 틈이다. 그 예시는 장식이 아니다. IPv4 규칙만으로는 두 번째 패밀리를 열어둔 채로 두기 때문에 존재한다.
예시의 절반을 복사하면 정책의 절반이 나온다. 이것이 이 누출의 가장 흔한 기원이다. 한 블로그 글이 길이 때문에 예시를 줄였다. 그런 다음 줄어든 버전이 모두가 다시 사용한 템플릿이 되었다.
# what is actually installed, per family?
sudo iptables -S OUTPUT | head -20
sudo ip6tables -S OUTPUT | head -20
# nftables: inspect each family separately
sudo nft list ruleset inet | head -40
# does this host even have a usable IPv6 path?
ip -6 route show default
두 번째 명령이 허용 정책을 출력하는데 첫 번째가 DROP을 출력한다면, 클라이언트를 건드리지 않고 답을 찾은 것이다. 그 비대칭이 버그다.
조회 한 번으로 전체 이야기가 아닌 이유
하나의 이름이 A 레코드와 AAAA 레코드를 모두 반환할 수 있고, 앱이 모두 같은 방식으로 선택하지는 않는다. 브라우저는 네트워크가 제공하면 IPv6를 선호하고 실패하면 물러날 수 있지만, 백그라운드 동기화나 업데이터는 자체 리졸버와 자체 선호를 사용한다. 따라서 IPv4에서의 한 번의 성공한 테스트는 하나의 경로를 증명할 뿐, 정책을 증명하지 않는다.
네트워크 전환도 답을 바꾼다. 다른 Wi-Fi 네트워크에 연결하거나, 절전에서 깨어나거나, 셀룰러로 전환하면 기기에 새 IPv6 접두사가 주어질 수 있고, 사무실 LAN에서 충분해 보였던 정책이 이를 덮지 못할 수 있다.
터널이 꺼진 상태를 테스트하라
연결된 상태에서 테스트하면 터널이 작동한다는 것을 알 수 있다. 흥미로운 테스트는 바로 그때 킬 스위치가 버텨야 하는, 사용할 수 없는 상태다. 터널을 일부러 중지한 다음, 보호 중인 앱에서 중요한 목적지를 시도하라.
# with the tunnel stopped, does anything still leave?
ip -6 route get 2606:4700:4700::1111
curl -6 -sS -o /dev/null -w '%{http_code}\n' --max-time 5 https://ipv6.example.net/
curl -4 -sS -o /dev/null -w '%{http_code}\n' --max-time 5 https://ipv4.example.net/
두 패밀리 모두 닫힌 채로 실패해야 한다. IPv4 요청이 시간 초과되고 IPv6 요청이 상태 코드를 반환한다면 정책은 불완전하며, 터널을 아무리 다시 테스트해도 바뀌지 않는다.
그 테스트에 문서화된 예외도 유지하라. 로컬 네트워크 검색, DHCP 갱신 또는 VPN 엔드포인트로의 부트스트랩 경로는 계속 접근 가능해야 할 수 있으며, 그러한 예외는 장애 중에 발견되는 대신 정책에 명시되어야 한다. 터널이 다시 올라오지 못해 실패하는 누출 테스트는 보안 발견이 아니라 연결성 발견이다.