이미 설정된 Linux VPN을 쓰는 브라우저는 /dev/net/tun을 열지 않는다. 소켓을 열고, 그 소켓의 패킷이 어느 인터페이스로 나가는지는 커널이 결정한다. 터널을 만든 프로세스는 그 트래픽을 전달하는 프로세스와 다른 일을 한다. 이 두 역할을 분리해 두면 대부분의 권한 질문은 질문되기 전에 답이 나온다.

소켓에서 터널까지 패킷 하나를 따라가 보자

평범한 앱은 connect나 send를 호출하고, 커널은 목적지의 경로를 찾는다. 선택된 경로가 터널 인터페이스를 가리키면 패킷은 VPN 데몬이 읽는 장치의 대기열에 들어간다. 데몬은 그것을 암호화해 물리 인터페이스로 내보낸다. 그 설명의 화살표 하나하나가 권한 질문에 답할 수 있는 지점이다.

그 경로 어디에도 앱이 장치를 알 것을 요구하지 않는다. 데몬은 파일 디스크립터를 보유하고 인터페이스 작업을 수행하며, 앱은 평범한 네트워크 API만 본다.

두 반쪽을 한 번 그려 보면 권한 질문을 놓기가 더 쉬워진다. 위쪽 반쪽은 앱의 세계이고, 아래쪽 반쪽은 데몬의 것이다.

누가 장치를 필요로 하고 누가 필요로 하지 않는지 결정한다

TUN 인터페이스를 만들거나 연결하려면 장치 접근과 함께 그것을 설정할 권한이 필요하다. 그래서 지원되는 VPN 서비스는 네트워크 관리 권한으로 시작하거나 특권 도우미를 사용한다. 그 요구 사항은 데몬의 것이다. 브라우저, 데이터베이스 클라이언트, 파일 전송 도구에 대해서는 아무 말도 하지 않는다. 그 권한이 필요한 서비스는 자신의 설정 단계를 위해 필요로 하고, 클라이언트를 위해서가 아니다.

경로와 정책이 자리 잡으면 평범한 프로세스는 평소의 소켓 호출로 만들어진 경로를 사용한다. 장치 노드도 필요 없고 root도 필요 없다.

장치 권한을 바꾸는 것은 앱에 목적지 접근을 부여하는 방법이 아니다. 장치 경계를 다른 곳으로 옮길 뿐이다.

앱이 실제로 실행되는 네임스페이스를 확인한다

컨테이너나 두 번째 네트워크 네임스페이스는 그 프로세스에 별도의 인터페이스 집합과 라우팅 테이블을 준다. 호스트의 터널은 그곳에 자동으로 나타나지 않으며, 장치 권한을 아무리 바꿔도 그것은 달라지지 않는다. 앱은 그 네임스페이스가 제공하는 것을 통해 라우팅할 뿐이다. 그래서 컨테이너 이미지가 실수로 호스트 터널을 상속할 수 없다.

확인하려면 ip netns list와 ip -n NAME route를 쓰거나 프로세스의 네임스페이스 링크를 살펴본다. 답은 보통 명령 하나로 보인다.

같은 호스트의 두 사용자에게 정책으로 서로 다른 경로를 줄 수도 있다. 그것은 네트워크 결정이며 장치 소유의 증거가 아니다.

권한과 네트워크 정책을 분리한다

일부 도구는 소켓보다 더 많은 것을 필요로 한다. 패킷을 캡처하려면 원시 소켓이 필요하고, 경로를 바꾸려면 관리 권한이 필요하다. 이것들은 별도의 허가가 따르는 별도의 작업이며, 하나를 준 것을 VPN 사용의 포괄적 요구로 읽어서는 안 된다.

자체 터널을 구현하는 프로그램은 세 번째 경우다. 데몬의 역할을 맡기 때문에 정말로 TUN을 연다. 그래서 데몬의 권한이 필요한 것이다.

권한의 이름을 붙이기 전에 작업의 이름을 붙여라. 둘은 보통 보이는 것보다 분리하기 쉽다.

두 계정으로 분리를 검증한다

터널을 서비스로 연결한 다음, 앱을 일반 사용자로 실행해 사설 리소스에 도달하는지 확인한다. 그 사용자의 셸에서 목적지에 대해 ip route get을 확인해 어느 인터페이스가 선택되었는지 본다. 경로가 맞는데도 연결이 실패하면 문제는 특권이 아니라 정책이다.

결과로 나오는 설계는 명시적 경계를 둔 최소 권한이다. 한 구성 요소가 설정과 터널 자원을 소유하고, 워크로드는 평소의 계정을 유지하며, 네트워크 정책이 무엇이 통과할 수 있는지 결정한다. 모두를 root로 실행하면 아무것도 설명하지 않은 채 그 역할들이 무너진다.

관련 글