안드로이드의 단일 VPN 규칙은 호스팅된 SSH 서버를 막지 않는다
VPN 하나라는 제한은 VPN 서비스를 제한할 뿐 소켓을 제한하지 않는다. 휴대폰의 SSH 서버는 리스너, 피어 접근, 응답 경로 때문에 실패한다.
이 믿음은 사실처럼 받아들여질 만큼 흔하다. 안드로이드는 VPN을 하나만 허용하므로 휴대폰에서 도는 SSH 서버는 그 터널로 동작할 수 없다는 것이다. 앞부분은 맞지만 결론은 따라오지 않는다. 플랫폼의 제한은 몇 개의 앱이 VPN 서비스 슬롯을 차지할 수 있는지에 관한 것이지, 소켓이 몇 개 존재할 수 있는지나 누가 휴대폰에 접속할 수 있는지에 관한 것이 아니다. 수신 소켓을 가진 SSH 데몬은 평범한 포트를 쓰는 평범한 앱이고, 터널은 그 트래픽이 지날 수 있는 경로 중 하나일 뿐이다.
단일 VPN 규칙이 실제로 제한하는 것
VpnService 은 단일 슬롯 자원이다. 앱이 터널을 세우면 그 앱이 기기의 VPN이 되고, 터널을 세우려는 두 번째 앱은 첫 번째가 사라질 때까지 거부된다. 이는 터널 소유권에 관한 규칙이며, VPN 클라이언트 두 개가 동시에 실행될 수 없는 이유를 설명한다.
수신 소켓에 대해서는 아무 말도 하지 않는다. SSH 서버는 VpnService 을 호출하지 않고, 패킷을 가로채지 않으며, 그 슬롯을 두고 경쟁하지 않는다. 포트를 바인딩하고 연결을 받아들이는데, VPN이 전혀 설치되지 않았을 때와 같은 일이다. 그래서 이 속설은 터널 제공자에 대한 제한과 네트워크 연결에 대한 제한을 혼동하며, 둘은 아무 관계가 없다.
리스너는 목적지이며 터널의 일부가 아니다
터널이 생기면 휴대폰은 그 위에 주소를 갖고, 피어 기기는 그 주소로 가는 경로를 갖는다. 무언가 응답하는지는 리스너가 결정한다. loopback에 바인딩된 데몬은 휴대폰 자체에서만 닿을 수 있다. 예전 터널 주소에 바인딩된 데몬은 재접속 후에도 바인딩을 유지할 수 있지만, 바인딩했던 주소는 이미 바뀌었다.
바인딩 규칙은 평범한 SSH 설정이다. 수신 주소와 포트, 그리고 누가 인증할 수 있는지다. 안드로이드에서는 그 설정이 앱 자체의 구성에 있고, 앱 프로세스 소유자에게 특권 포트 사용이 허용되지 않을 수 있으므로 root로 22번 포트를 바인딩하던 데스크톱 습관은 그대로 옮겨지지 않는다.
ListenAddress 10.90.0.4
Port 2222
AllowUsers alice다음은 경로 자체에 대한 허가다. 나가는 웹 트래픽을 흔쾌히 실어 나르는 소비자용 VPN이라도 클라이언트로 들어오는 연결을 전혀 허용하지 않을 수 있다. 많은 서비스가 그렇게 하는데, 한 고객을 다른 고객에게 노출시키기 때문이다. 피어가 어디서도 휴대폰의 터널 주소에 닿지 못한다면 문제는 서비스 정책에 있지 휴대폰에 있지 않다.
응답 경로가 보통 끊기는 지점이다
피어가 휴대폰의 터널 주소에 도달하고 SYN이 도착했다고 하자. 이제 응답은 같은 경로로 나가야 하며, 그것은 휴대폰 내부의 라우팅 결정이다. 응답이 기본 네트워크라서 Wi-Fi로 나가면, 피어는 끝나지 않는 연결을 보게 되고 이는 방화벽과 똑같이 보인다.
안드로이드는 기본 네트워크가 프로세스별로 선택되고 VPN 앱의 정책으로 바뀔 수 있어서 이 문제를 데스크톱보다 복잡하게 만든다. VPN 클라이언트는 허용된 앱 목록만 라우팅할 수 있으므로, 그 목록 밖의 서버 앱은 피어가 터널로 기대하는 응답을 계속 물리 네트워크로 내보낸다.
앱은 자기 소켓을 특정 네트워크에 바인딩할 수도 있고, 그것이 기본값을 덮어쓴다. 어떤 서비스가 한 주소에서는 닿고 다른 주소에서는 닿지 않는다는 보고를 읽었다면, 보통 그 뒤에 있는 메커니즘이 이것이다.
VPN을 우회해도 인바운드 서비스는 고쳐지지 않는다
흔한 우회책, 즉 SSH 앱을 VPN에서 제외해 트래픽이 더 이상 리디렉션되지 않게 하는 것은 반대 문제에 대한 처방이다. 나가는 SSH 클라이언트의 전송이 터널 때문에 자꾸 끊길 때 도움이 된다. 호스팅된 서버에서는 피어가 쓰는 터널 밖으로 응답을 빼버릴 수 있고, 그러면 부분적 실패가 전면적 실패가 된다.
라우터에서 공용 포트를 여는 경우도 같다. 요구 사항이 승인된 기기 하나가 VPN으로 휴대폰에 닿는 것이라면, SSH를 인터넷에 노출하는 것은 다른 위협 모델이지 수리가 아니다.
먼저 서비스 주소를 정하라. 휴대폰의 LAN 주소, 터널 주소, 또는 의도적으로 포워딩한 엔드포인트다. 각각 도달성과 노출에 관한 계약이 다르고, 이것들을 섞는 것이 작은 접근 문제를 영구적인 구멍으로 만드는 방법이다.
한 번에 한 방향씩 진단하라
설정을 바꾸기 전에 네 가지 사실을 적어라. 피어가 접속하는 주소, 휴대폰의 리스너 상태, 그것을 소유한 앱과 사용자 맥락, 그리고 각 방향에서 관찰된 경로다. VPN이 SSH를 끊는다는 보고에는 이 중 어느 것도 없고, 클라이언트 전송 문제와 닿을 수 없는 서버를 구분할 수 없다.
adb shell ip -br addr
adb shell ip route get 10.90.0.9
nc -vz 10.90.0.4 2222포트 확인은 휴대폰이 아니라 피어에서 실행하고, SSH 클라이언트의 상세 출력을 읽어 TCP 핸드셰이크 전에 실패하는지 후에 실패하는지 보라. 끝내 연결되지 않는 소켓과 거부되는 로그인은 서로 다른 단계이고 책임자도 다르다.
디버깅하는 동안 호스트 키 검증과 계정 제한을 그대로 유지하라. 리스너와 허용 사용자를 제한하는 것이 닿을 수 있는 서버를 받아들일 만하게 만든다. 연결이 되게 하려고 그것들을 풀면 한 문제를 더 나쁜 문제와 맞바꾸는 것이다. OpenSSH 서버 참조는 안드로이드 구현이 같은 구성 이름을 따른다면 수신과 접근 제어를 다룬다.