터널은 돌아오고 클라이언트는 연결됐다고 표시하며 브라우저는 끊기기 전에 불러오던 페이지에서 계속 돌고 있다. VPN 재연결은 경로를 복구하고, 앱과 원격 측이 예전 경로 위에 쌓아 둔 연결 상태는 복구하지 않는다. 고쳐야 할 것은 서로 다른 두 가지이며, 이를 혼동하면 이유 없이 VPN 설정으로 되돌아가게 된다.

테스트 하나면 둘을 깔끔하게 분리할 수 있고, 십 초면 된다. 이전 요청이 멈춰 있는 동안 같은 목적지로 새 요청을 보낸다.

재연결에서 살아남는 것과 그렇지 않은 것

터널은 경로와 키와 주소의 집합이며, 재연결은 세 가지를 모두 복구할 수 있지만 이미 성립한 흐름은 모두 죽은 채로 남는다. 운영체제는 앱이 요청했기 때문에 소켓을 열어 두고, 그 소켓의 타임아웃은 몇 분이나 몇 시간 뒤일 수 있다. 그 아래에서 상대는 흐름을 잊었고, 변환 항목이 만료됐으며, 경로가 핸드셰이크를 본 적 없는 종단을 지나게 됐을 수도 있다. 재연결 절차 어디에도 앱의 소켓을 훑어 고치는 단계는 없다.

일부 클라이언트는 재연결 때 오래된 연결을 버리는 훅을 넣지만 대부분은 넣지 않는다. 여기서의 동작은 클라이언트, 플랫폼, 버전에 따라 다르다.

원격 측도 당신을 잊었다

먼 쪽 끝은 TLS 세션, TCP 제어 블록, 그리고 어쩌면 주소와 포트를 흐름에 묶는 매핑 항목 같은 자기 상태를 들고 있다. 경로 위의 변환 장치는 터널이 빠르게 다시 서더라도 그 빈틈 동안 매핑을 만료시킬 수 있다. 매핑이 사라지면 서버가 더는 알아보지 못하는 흐름의 패킷이 도착하고, 응답은 클라이언트가 끝내 보여 주지 않는 리셋일 수 있다. 앱은 이것을 오류가 아니라 멈춤으로 보고하는데, 영영 응답하지 않을 소켓을 아직 기다리고 있기 때문이다.

서버 측 유휴 타임아웃은 자기 일정대로 같은 일을 하므로, 네트워크 사건이 전혀 없어도 세션이 죽을 수 있다. 서버가 기대하는 것보다 오래 유휴 상태였던 연결은 터널이 움직인 것을 알아차리기 전에 이미 사라졌다.

새 요청이야말로 둘을 가르는 테스트다

이전 요청이 아직 멈춰 있는 동안 같은 호스트로 새 요청을 보내고 둘 다 지켜본다. 새 요청이 성공하면 터널과 경로와 이름 해석이 모두 동작하는 것이고, 결함은 오래된 흐름에 국한된다. 새 요청도 실패하면 경로 문제가 있고 앱의 세션 상태는 잘못된 단서다.

테스트하는 동안 직접 트래픽 차단은 그대로 두어라. 우연한 우회 경로로 성공한 요청은 터널에 대해 아무것도 증명하지 못한다.

오래된 연결이 아니라 새 연결로 확인한다

아래 명령은 아직 열려 있는 소켓을 보여 주고, 새 요청을 하나 시작하며, 세션 표에 그 흐름의 항목이 남아 있는지 확인한다. 차이가 보이도록 첫 명령은 재연결 전과 후에 실행한다. 호스트와 포트는 자신의 서비스로 바꾼다.

ss -tanp | grep -E ':(443|8443)'
curl -sS -o /dev/null -w '%{http_code} %{time_connect}\n' --max-time 8 https://intranet.example.com/
conntrack -L 2>/dev/null | grep 8443
dig +short intranet.example.com

트래픽이 흐르지 않는 성립된 소켓이 찾아야 할 특징이고, 상태 코드를 돌려주는 새 요청은 경로가 살아 있음을 보여 준다. 연결 표는 무언가가 아직 그 흐름을 매핑하는지 답하며, 바로 그것이 클라이언트가 볼 수 없는 부분이다. 리졸버 질의는 페이지 로드의 나머지 절반을 덮는데, 이름 실패와 멈춘 연결이 브라우저에서는 똑같이 보이기 때문이다. 함께 보면 이 네 가지 출력이 오래된 세션과 고장 난 터널을 갈라낸다.

중단 시각, 재연결 시각, 이전 요청과 새 요청의 차이를 기록한다. 그 기록이 모호한 불만을 누군가 행동할 수 있는 보고로 바꾼다.

관련 글