패키지를 제외하는 것은 차단이 아니라 라우팅 지시이므로, VPN 연결 후 로드되는 페이지는 예상된 결과이며 장애의 증거가 아닙니다. 제외는 앱을 차단하는 대신 터널에서 빼내고, 일반 네트워크가 그 트래픽을 나릅니다. 여기에서 두 가지 별개의 정책이 만납니다: 하나는 프로세스를 고르고, 다른 하나는 목적지를 고릅니다. 이 순서로 읽으면 연결 아이콘과 동작하는 앱이 모순이 아닌 이유가 설명됩니다.

제외가 네트워크를 열어 두는 이유

VpnService.Builder는 허용 목록이나 차단 목록 중 하나만 받고 둘 다는 받지 않습니다. 터널을 지나는 것은 허용 목록에 있는 패키지뿐입니다. 차단 목록은 이를 뒤집습니다. 목록에 있는 것을 제외한 모든 패키지가 터널을 쓰고, 목록에 있는 것은 물리적 경로를 유지합니다. 허용 목록에서 목록에 없는 패키지도 터널 밖에 있으므로, 이 모드에서 대부분의 앱에는 제외가 정상 상태입니다.

그래서 로컬 차단 항목은 방화벽 규칙이 아니라 라우팅 결정입니다. 요구 사항이 앱이 호스트에 도달하지 못하게 하는 것이라면, 터널이 그 앱을 덮어야 하고 서버나 로컬 정책이 트래픽을 거부해야 합니다. 앱을 터널에서 빼는 것은 어떤 인터페이스가 그 패킷을 나르는지만 바꿉니다.

Play Store 사례는 흔한 의도적 예입니다. 스토어 패키지를 제외하면 VPN 출구가 전송을 방해할 때 다운로드가 하위 연결을 쓸 수 있고, 제외가 차단이 아니라 우회를 뜻한다는 것을 보여 줍니다.

프로필 하나, 목록 하나

두 호출을 모두 쓰면 빌더가 예외를 던지므로, 앞에 있는 프로필은 허용 목록이거나 차단 목록입니다. 이 사실 하나가 이후의 모든 관찰을 읽는 방법을 정합니다. 라우팅 테이블보다 먼저 코드나 관리형 구성을 확인하십시오. 목록이 나머지 출력의 의미를 결정하기 때문입니다. 하이브리드 프로필은 관찰할 수 있는 상태가 아닙니다.

이 구분은 새로 설치한 앱의 동작도 예측합니다. 허용 목록에서는 누군가 추가할 때까지 밖에 남고, 차단 목록에서는 기본적으로 터널에 들어갑니다.

관리형 구성은 같은 선택을 다른 키 이름으로 노출하며, 일부 콘솔은 이를 목록이 아니라 앱별 VPN 모드라고 부릅니다. 동작은 결국 그 두 호출로 줄어들므로, 라벨을 믿는 대신 설정을 패키지 목록까지 거슬러 추적하십시오. 라벨과 목록이 어긋나면 목록이 이깁니다.

UID 규칙을 따라 터널 테이블로 이동

Android는 앱마다 고유한 Linux 사용자 ID를 주고, VPN 서비스는 UID 범위와 일치하는 정책 규칙을 설치합니다. 일치하는 트래픽은 기본 경로가 tun 장치를 가리키는 라우팅 테이블로 보내집니다. 테이블 번호는 VPN 인스턴스마다 할당되므로, 다시 연결하거나 두 번째 VPN 프로필을 만든 뒤에 바뀝니다.

규칙 출력이 곧 지도입니다. 각 줄은 조회 테이블을 지목하고, 그 테이블이 일치하는 패킷이 터널로 들어가는지를 정하는 기본 경로를 담고 있습니다.

adb shell ip rule show
adb shell ip route show table all
adb shell cmd package list packages -U | grep <package>

UID를 얻으려면 먼저 세 번째 명령을 실행하고, 그 범위에 UID가 들어 있는 규칙을 찾으십시오. 그 규칙은 테이블 번호를 가리키며, ip route show table <n> 그 번호를 붙이면 tun 장치를 지나는 기본 경로가 있는지 보여 줍니다. 테이블에 기본 경로가 없으면 앱은 선택되었지만 경로가 없는 것이고, 이는 다른 장애입니다.

이 명령에는 디버그 가능한 빌드나 adb shell이 네트워크 상태를 읽을 수 있는 기기가 필요합니다. 생산 기기에서는 패키지 관리자가 여전히 응답하지만 라우팅 점검은 거부될 수 있으므로, 할 수 있는 만큼 기록하고 나머지는 미확인으로 다루십시오.

경로는 앱이 아니라 프리픽스를 요구합니다

Builder.addRoute는 터널에 목적지 프리픽스를 요구합니다. 10.0.0.0/8 만 추가하는 프로필은 그 프리픽스를 터널로 보내고 나머지 모든 목적지는 물리적 경로에 둡니다. 이는 일반적인 분할 터널링이며, 규칙이 어떤 앱을 골랐는지와는 무관합니다.

서버가 푸시한 구성도 같은 모델을 따릅니다. 개별 경로 푸시는 프리픽스를 더하고, redirect-gateway 지시문은 기본 경로를 대체하여 터널이 덮는 앱의 모든 트래픽을 잡습니다. 그 지시문을 지우면 기기에서 아무것도 바꾸지 않고도 동작하던 터널이 하나의 서브넷으로 줄어들 수 있습니다.

특이한 게이트웨이나 메트릭 한정자가 붙은 푸시 경로도 설치에 실패할 수 있습니다. 단순한 프리픽스 형태를 선호하고 전달과 NAT는 서버에 맡기십시오.

설정을 바꾸기 전에 교집합을 읽으십시오

터널은 덮인 프로세스와 요구된 목적지의 교집합을 나릅니다. 덮인 앱이 게시된 프리픽스 밖의 주소를 요구하면 물리 네트워크로 나갑니다. 차단된 패키지는 그 프리픽스 안의 주소를 요구해도 밖에 남습니다. 둘 다 버그가 아니며, 둘 다 두 정책이 제 일을 하는 것입니다.

앱별 VPN은 연결된 앱을 연결 없이 남길 수도 있습니다. on-demand가 꺼져 있고 터널이 내려가 있으면, 앱은 앱별 VPN에 묶인 채 대체 경로가 없어 정책 상태가 아니라 고장 난 앱처럼 보입니다. on-demand를 켜거나 관리로 연결을 해제하면 달라집니다.

LayerWhat it selectsWhere to lookTypical mismatch
Package listProcessesBuilder calls or managed configurationApp excluded but expected to be blocked
UID policy ruleWhich table a UID usesip rule showTunnel icon on, app still on Wi-Fi
Routing tableDestinations for matched trafficip route show table <n>Covered app reaches only one subnet
Server pushPrefixes offered to the clientServer route configurationTunnel shrinks after a server edit

한 번에 한 축씩 진단하십시오. 먼저 허용인지 차단인지 확인하고, 다음으로 설치된 UID 규칙과 테이블을 읽고, 마지막으로 같은 목적지에 두 번째 앱을 시험하십시오. 두 번째 앱은 한 요청 안에서 프로세스 선택과 목적지 선택을 분리합니다.

결과를 앱, 목적지, 테이블 번호로 적어 두십시오. 다음 변경이 지켜야 할 것은 바로 이 세 가지입니다.

관련 글