Wi-Fi에서 셀룰러로 전환한 뒤 킬 스위치: 무엇이 깨지는가
킬 스위치는 네트워크 변경이 교체하는 인터페이스 이름에 맞춰 작성된다. 신뢰하기 전에 바깥 경로와 주소 체계, 리졸버를 확인하라.
어느 순간 노트북은 사무실 Wi-Fi에 있고, 다음 순간에는 휴대폰에 연결되어 있으며, 창 전환을 마치기도 전에 터널 표시등은 다시 초록색이다. 초록불은 터널이 다시 연결되었다는 것만 알려 준다. 그 틈에 라우팅과 차단 정책이 무엇을 했는지는 다른 문제다. 그 정책은 마지막 설치 때 컴파일된 일치 조건들의 집합이고, 핸드오버가 방금 제거한 인터페이스와 주소, 주소 체계를 이름으로 지정한다. 그래서 같은 전환이 똑같아 보이는 세 가지 결과를 낳는다. 차단이 계속되거나, 정책이 자기 복구를 막거나, 아무것도 덮지 않는 경로가 열린다.
화면에서는 그 세 상태를 구분할 수 없으니 표시등이 무슨 색인지가 아니라 규칙이 어느 식별자에 고정되어 있었는지 물어야 한다. Linux에서는 규칙이 인터페이스 이름이나 그로부터 채운 ipset을 지정할 수 있다. Android에서는 setUnderlyingNetworks를 통해 어떤 하위 네트워크를 따를지 VpnService에 알려 줄 수 있다. 모든 결합은 곧 교체될 링크를 가리키고, 각각은 나중에 다시 평가되어야 한다.
핸드오버가 실제로 다시 쓰는 것
옛 링크가 사라지면 커널은 그 주소를 제거하고, 그를 지나던 경로를 회수하며, 남은 것으로 출발지 주소 선택을 다시 세운다. 기본 경로는 새 게이트웨이로 옮겨 가고, 옛 규칙이 참조하던 인터페이스 인덱스는 사라졌거나 다른 장치용으로 재활용된다. 링크별 리졸버도 갱신되지만 경로와 같은 순간은 아니다. 이 모든 일은 VPN 클라이언트 아래에서 일어나므로 자기 터널 상태만 지켜보는 클라이언트는 아무것도 보지 못한다.
문제는 규칙이 그 배후의 의도가 아니라 특정 식별자를 부호화한다는 점이다. 터널 인터페이스로 나가지 않는 모든 것을 버리는 규칙은 인터페이스 이름을 부호화한다. 엔드포인트를 허용하는 규칙은 새 네트워크가 다른 곳으로 보낼 수 있는 주소를 부호화한다. 식별자가 사라지면 규칙이 눈에 띄게 틀리는 것이 아니라, 아무것과도 일치하지 않게 되거나 잘못된 것과 일치하기 시작한다.
구멍을 뚫지 않고 바깥 터널 다시 세우기
바깥 전송은 옛 출발지 주소에 묶여 있었으므로 새 주소에서 새 소켓을 열어야 한다. 엔드포인트가 문자 그대로의 주소가 아니라 호스트 이름이면, 차단이 아직 유효한 동안 조회가 성공해야 한다. 이것이 순서의 함정이다. 엔드포인트 말고 모두를 버릴 만큼 엄격한 정책은 그것을 찾는 데 필요한 조회도 버린다. 차단을 올리기 전에 해석이 일어나거나 아직 살아 있는 터널로 실려 가지 않는 한. 두 순서 모두 될 수 있지만, 클라이언트가 자기가 어느 순서인지 알아야 한다.
여기서 클라이언트의 차이가 진단할 때 중요해진다. 어떤 것은 먼저 해석하고 규칙을 나중에 설치하므로 핸드오버마다 눈에 보이는 단절이 생겼다가 스스로 회복된다. 다른 것은 차단을 올려 둔 채 살아남은 터널이 조회를 실어 나르기를 기대하는데, 터널이 링크와 함께 죽으면 완전히 실패한다. 연결성을 되찾으려고 넓은 직접 기본 경로를 더하면 시험은 늘 통과하고 막으려던 누출은 늘 남는다.
옛 게이트웨이를 아직 가리키는 경로도 다른 원인으로 같은 막힌 회복을 만든다. 그러니 클라이언트를 탓하기 전에 표를 읽어라. 아래 명령은 커널이 실제로 쓸 인터페이스와 출발지 주소를 알려 주므로 옛 경로가 아직 설치되어 있는지 한 줄로 판정된다.
ip route get 1.1.1.1
ip -6 route get 2606:4700:4700::1111새 링크가 IPv6를 가져오고 규칙은 가져오지 않을 때
셀룰러와 게스트 네트워크는 사무실 유선 LAN이 한 번도 하지 않았더라도 전역 IPv6 프리픽스를 주는 일이 많다. IPv4 규칙으로만 작성된 정책, 곧 손으로 만든 iptables와 nftables 레시피 대부분의 기본 형태는 그것을 전혀 덮지 않는다. 터널은 연결된 채로 있고, IPv6 도달성이 있는 응용 프로그램은 그 밖으로 나가며, 터널이 정말 올라와 있으므로 표시등은 차이를 보여 줄 수 없다. 반대 경우도 흔하다. IPv6 전용 규칙 집합은 v6를 제공하지 않는 네트워크에서 아무것도 하지 않는다.
핸드오버 양쪽에서 링크가 제공하는 것과 정책이 언급하는 것을 비교하라. 이 명령들은 클라이언트가 다루는 것과 같은 사실을 출력한다.
ip -4 route show default
ip -6 route show default
sudo nft list ruleset | grep -c 'ip6 '둘째 명령이 기본 경로를 출력하는데 셋째가 영을 돌려주면, 새 링크는 어떤 규칙도 지켜보지 않는 채 기계를 IPv6로 밖에 내보낼 수 있다. 전환 뒤에 셋을 다시 실행하라. 흥미로운 숫자는 두 번의 실행 사이 차이다. 둘째가 아무것도 출력하지 않으면 반대 경우가 적용되고, 거기서 v6에 무거운 정책은 그저 장식이다.
리졸버가 세션 중간에 주인을 바꾼다
현대 시스템에서 리졸버는 링크에 붙어 있으므로 새 링크는 새 리졸버 집합과 함께, 어쩌면 다른 split-DNS 시야와 함께 온다. systemd-resolved는 링크마다 범위를 두고 경로에 따라 그 사이에서 고르며, macOS는 동적 저장소에서 범위가 있는 리졸버를 읽고 인터페이스 순서를 존중한다. 자기 리졸버를 설치하는 클라이언트는 링크의 것과 경쟁하고, 아무도 보지 않는 우선순위 규칙이 승자를 정한다. 그때 이름 해석은 깨질 수 있는데, 패킷 필터링은 쓰인 대로 정확히 동작한다.
설정된 리졸버가 아니라 실제로 활성인 리졸버를 읽어라. systemd 호스트에서는 status 하위 명령이 각 범위가 어느 링크에서 왔는지, private-DNS 이름이 유효한지를 보여 준다.
resolvectl status --no-pager
resolvectl query intranet.example.com질의가 터널이 아니라 물리 링크로 나가면 클라이언트는 우선순위 경쟁에서 진 것이고, 어떤 방화벽 조정도 이름을 고치지 못한다. 핸드오버 양쪽에서 질의 경로를 비교해 어느 쪽이 움직였는지 보라. 새 네트워크가 경로를 잡을 수 없는 사설 주소로 해석되는 이름은 세 번째 경우이고, 오류가 아니라 시간 초과로 나타난다.
핸드오버를 연습하고 기록을 남겨라
기다리는 대신 일부러 변화를 재현하라. 첫 링크에서 터널을 올리고 오래 걸리는 전송과 반복 조회를 시작한 다음 링크를 바꾸어 어느 쪽이 살아남는지 보라. 옛 링크가 사라진 때와 터널이 연결되었다고 보고한 때 사이의 간격을 적어라. 그 창에서 정책이 버텼는지 아니지가 갈린다. 결과가 한쪽으로 치우치지 않도록 같은 순서를 반대 방향으로도 돌려라.
예외에는 따로 점검이 필요하다. 옛 네트워크에서 정당했던 우회가 새 네트워크에서 자동으로 정당해지지는 않는다. 집 LAN의 프린터나 NAS, 캡티브 포털은 셀룰러에 대응물이 없고, 그곳에 닿던 예외는 대응물 없는 규칙이 된다.
방향마다 인터페이스 이름, 제공되는 주소 체계, 유효한 리졸버, 단절의 길이를 적어라. 재연결은 전송이 회복되었다는 증거이지 틈 동안 차단이 버텼다는 증거가 아니며, 티켓에 넣을 별개의 주장이다. 그 기록이 있으면 다음 핸드오버는 기억이 아니라 알려진 상태와 비교된다.