다운 스크립트냐 방화벽 규칙이냐: 무엇이 유출을 막는가
연결이 끊길 때 인터페이스를 내리는 방식은 거칠고 흔히 권한 없이 실행된다. 끊긴 뒤에도 살아남아 틈을 메우는 방화벽 규칙과 비교해 보자.
두 설계는 같은 것을 약속한다. 터널이 끊기면 아무것도 평문으로 밖에 나가지 않는다. 하나는 해제 시 스크립트를 실행해 인터페이스를 자른다. 다른 하나는 방화벽 규칙을 그대로 두고 VPN 클라이언트에 아무것도 요구하지 않는다. 둘은 누구에게 행동할 권한이 있는지, 틈이 벌어진 동안 무슨 일이 일어나는지, 그 뒤에 기계가 어떻게 다시 온라인으로 돌아오는지에서 다르다. 이 세 지점에서 비교하는 편이 어느 쪽이 더 엄격한지 논쟁하는 것보다 유용하다.
같은 일을 두 가지 방식으로
프로필이 스크립트 실행을 허용한다면 클라이언트는 터널이 올라올 때와 해제될 때 훅을 실행할 수 있다. 그 훅은 클라이언트의 수명 주기 안에서, 연결이 닫히는 바로 그 순간에 돈다. 반면 방화벽 규칙은 시스템 소유이며 클라이언트가 실행 중인지, 죽었는지, 강제 종료되었는지에 관심이 없다.
| Property | Teardown script | Firewall unit |
|---|---|---|
| Runs when the client is killed | No | Yes |
| Needs privilege at teardown time | Usually yes | Configured once, at boot |
| Acts on the whole machine | Often, including recovery paths | Only on the flows it matches |
그 표가 논증의 전부다. 정상 종료에서만 도는 스크립트는 가장 걱정하지 않던 경우를 덮는다. 중요한 경우는 충돌이나 네트워크의 소멸이며, 둘 다 깨끗한 해제를 만들어 내지 않는다.
해제 훅이 할 수 있는 일과 없는 일
문서화된 패턴은 네트워크 인터페이스를 순회하며 loopback을 제외한 모두를 내린다. 물리 장치가 내려가 있으면 터널 밖으로 빠져나갈 수 있는 것이 없으며, 그것이 바로 의도다.
#!/bin/sh
for dev in /sys/class/net/*; do
name=$(basename "$dev")
[ "$name" = lo ] || ip link set dev "$name" down
done
또한 로컬 네트워크, 기계가 호스팅하는 모든 서비스, 그리고 문제를 고치는 데 쓸 경로까지 내린다. 노트북에서는 이후에 무선 장치를 손으로 다시 켜야 할 때가 있어 짧은 장애가 수리 세션이 된다. 더 나쁜 것은 훅에 그런 일을 할 권한이 아예 없을 수 있다는 점이다. 클라이언트가 권한을 내려놓으면 해제 스크립트는 비특권 계정으로 돌고, 인터페이스 변경은 모두 권한 오류로 실패한다. 그 오류는 아무도 로그를 읽지 않는 바로 그 순간에 기록된다.
훅을 믿기 전에 클라이언트가 어떻게 시작되는지 확인하라. root로 손수 실행하면 동작하는 스크립트는 부팅 시 시작되는 서비스에 대해 아무것도 증명하지 못한다.
방화벽 규칙이 다른 점
터널 트래픽만 허용하는 규칙은 클라이언트 프로세스가 사라져도 계속 동작한다. 커널이 그 프로세스에 의존하지 않기 때문이다. 규칙은 더 좁기도 하다. 일치하는 흐름만 차단하고 loopback, 로컬 서브넷, 복구 경로는 건드리지 않으며, 그래서 기계를 고립시키지 않는다.
대가는 규칙을 호스트 방화벽을 소유한 쪽이 작성하고 유지해야 한다는 것이며, 규칙은 클라이언트가 실제로 만드는 터널 장치를 지목해야 한다. 즉 규칙과 프로필을 함께 유지해야 한다. 이름이 바뀐 인터페이스는 모든 것을 차단하면서 아무것도 설명하지 않는 방화벽을 남긴다.
어느 방식도 다른 쪽을 불필요하게 만들지 않는다. 수정한 뒤 한 번도 다시 읽지 않는 방화벽 규칙은 한 번도 실행되지 않는 스크립트만큼이나 허구다.
틈이 생기는지는 persist-tun이 정한다
터널 장치가 연결 끊김을 견디면 그쪽을 향하는 경로도 견딘다. 그러면 트래픽은 재라우팅되지 않고 버려지므로 기본 경로가 물리 게이트웨이로 되돌아가지 않는다. 어떤 방화벽 규칙이 개입하기 전에도 유출을 일어나기 어렵게 만드는 것이 이 동작이다.
이 설정이 없으면 장치가 경로와 함께 제거되고 기본 경로가 다시 물리 게이트웨이를 가리킬 수 있다. 그럴지는 플랫폼과 클라이언트에 달려 있으며, 바로 그래서 상태를 가정하지 말고 관찰해야 한다. 이 설정을 성능 옵션이 아니라 kill switch의 일부로 다루어라.
재연결 경로를 살려 둔 다음 네 가지 상태를 시험하라
엄격한 차단은 클라이언트의 재연결도 막을 수 있다. 나머지가 모두 거부되는 동안에도 클라이언트는 물리 게이트웨이를 거쳐 서버로 가는 경로가 필요하기 때문이다. 터널이 올라오기 전에 그 한 주소에 대한 호스트 경로를 넣고, 이름을 먼저 풀어 두어라. 필요할 때쯤 DNS가 차단되어 있을 수 있다.
ip route add 198.51.100.20/32 via 192.168.0.1 dev wlan0
그다음에는 라벨이 아니라 상태를 검증하라. 네 순간을 의도적으로 겪어 보라. 터널 없는 콜드 스타트, 건강한 세션, 아무도 요청하지 않은 끊김, 그리고 복구된 뒤의 세션이다. 각 순간에 기본 경로를 읽고 출구 주소가 보이는 요청을 하나 보내라. 터널이 내려가 있는 동안 포기하는 애플리케이션은 메일 클라이언트나 업데이터에 대해 아무것도 말해 주지 않는다. 그래서 마지막 확인은 백그라운드 애플리케이션 하나로 반복하라. 규칙과 인터페이스 이름과 복구 절차를 적어 두어라. 아무도 검증할 수 없는 kill switch은 소문일 뿐이다.