NET_ADMIN 은 프로세스가 네트워크 관리를 수행하도록 허용하는 Linux 기능이며, 컨테이너에서는 프로세스가 실행되는 네트워크 네임스페이스 안에서 정확히 그 일을 합니다. 소켓을 여는 라이선스가 아니고 장치나 라우트를 대신하지도 않습니다. 흥미로운 질문은 기능이 부여되었는지가 아니라 어느 네임스페이스가 변경을 받는가입니다.

이 기능이 포괄하는 범위

자기 네임스페이스 안에서 NET_ADMIN 을 가진 프로세스는 인터페이스를 올리고 내리고, 주소를 할당하고, 라우트를 추가하고 제거하며, 방화벽 규칙을 작성할 수 있습니다. 이것이 capabilities(7) 이 기능 아래에 모으는 작업 그룹입니다. 또한 커널이 트래픽을 어떻게 대기시키고 필터링하는지 제어하는 인터페이스 수준 설정도 조정할 수 있습니다. VPN 클라이언트는 보통 시작할 때 몇 가지가 필요하고 그 뒤로는 아무것도 필요하지 않습니다. 그래서 대부분의 이미지는 사용하는 것보다 더 넓은 권한을 요청합니다.

그 네임스페이스 밖의 작업은 포괄되지 않으며 인접 권한은 별개입니다. 원시 패킷 읽기와 쓰기는 NET_RAW 에 속하고, tun 장치를 만들려면 기능과 장치 노드 접근이 모두 필요합니다. 장치가 없는 프로세스는 네트워크 문제처럼 보이지만 그렇지 않은 권한 오류를 받습니다.

네임스페이스가 피해 범위를 결정한다

자체 브리지 네트워크에 있는 컨테이너는 자체 loopback, 자체 인터페이스, 자체 라우팅 테이블에 도달합니다. 거기서 NET_ADMIN 을 부여하면 VPN 클라이언트가 호스트의 라우팅 결정을 건드리지 않고 터널을 만들 수 있으며, 대부분의 컨테이너 이미지가 전제하는 구성입니다.

그 컨테이너를 호스트 네트워킹으로 바꾸면 같은 부여가 이제 호스트의 라우트와 호스트의 방화벽을 편집합니다. 별도 네임스페이스가 남아 있지 않기 때문입니다. 하나의 터널을 관리해야 했던 클라이언트가 전체 머신의 트래픽을 우회시킬 수 있고, 해제 스크립트의 실수는 아무도 쓰지 않은 규칙을 호스트에 남깁니다. 기능 자체는 아무것도 바뀌지 않았고 문맥이 바뀌었습니다. 같은 compose 파일이 두 호스트에서 다르게 동작하는 이유입니다.

하나로 합쳐지는 세 가지 전제 조건

VPN 컨테이너의 시작 실패는 보통 세 가지 별개 사항 중 하나와 관련됩니다. 기능, 터널 장치, 프로토콜에 대한 커널 지원입니다. 세 가지를 한꺼번에 주면 동작하지만 다음 실패를 귀속시킬 수 없게 만들기도 합니다. 최소 compose 조각은 기능을 부여하고 장치 노드를 명시적으로 전달합니다.

services:
  vpn:
    image: example/vpn-client:latest
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1

어떤 sysctl이 필요한지는 이미지 자체 문서를 읽으십시오. 컨테이너의 sysctl은 추가 권한이나 호스트 수준 설정을 요구할 수 있기 때문입니다. 이미지가 사용자 공간 전용 모드만 지원하는 곳에서는 장치가 전혀 필요 없고 기능 목록도 그에 따라 줄어듭니다.

모드가 아니라 좁은 작업을 부여하라

특권 모드는 모든 기능을 넘기고 동시에 장치 접근을 넓히므로 세 가지 전제 조건 중 무엇이 빠졌는지 감춥니다. 컨테이너의 실패 방식을 호스트 변경과 구별할 수 없게 만들기도 합니다. 작성이 길어지더라도 명시적 목록을 선호하십시오.

루트리스 및 사용자 네임스페이스 배포는 기능의 실제 의미를 바꿉니다. 프로세스가 매핑된 네임스페이스 안에서만 기능을 보유할 수 있기 때문입니다. 루트가 있는 예제의 기능 목록을 루트리스 런타임에 복사하고 같은 권한을 기대하는 것은 오후를 잃는 흔한 방법입니다.

비밀은 별개의 결정에 속합니다. NET_ADMIN 을 보유한다고 해서 클라이언트가 어떤 피어 신원을 제시하는지, 어떤 자격 증명을 불러오는지, 그것이 어떻게 교체되는지는 알 수 없습니다. 네트워크 관리는 신뢰를 제공하지 않습니다.

변경을 검증하고 부여가 왜 존재하는지 기록하라

터널을 디버그하기 전에 컨테이너가 실제로 무엇을 받았는지 확인하십시오. 아래 검사는 추가된 기능과 네트워크 모드를 보고한 다음 내부에서 인터페이스 목록이 어떻게 보이는지 보여 줍니다.

docker inspect --format '{{.HostConfig.CapAdd}} {{.HostConfig.NetworkMode}} {{.HostConfig.Privileged}}' vpn
docker exec vpn sh -c 'grep CapEff /proc/self/status'
docker exec vpn ip -br addr
docker exec vpn ip route

기능이 있는데 인터페이스 목록이 여전히 비어 있다면 클라이언트가 무언가를 만들기 전에 실패한 것이며, 다음으로 볼 곳은 컨테이너 로그입니다. 터널은 있는데 트래픽이 흐르지 않는다면 컨테이너 내부의 라우팅 테이블이 다음 질문에 답합니다.

해제 경로도 테스트하십시오. 방화벽 규칙을 쓰는 클라이언트는 그것을 남길 수 있습니다. 컨테이너를 멈추고 호스트 네트워킹을 쓰는 경우 호스트의 규칙 집합을 다시 확인하십시오. 시작할 때 두 초만 필요한 부여를 그 설명이 없는 메모 없이 compose 파일에 영구 요구 사항으로 적어서는 안 됩니다.

각 기능 옆에 이유를 적으십시오. 어떤 작업이 그것을 필요로 했고 그것 없이 무엇이 실패하는지. 그 메모는 이미지 업데이트를 넘어 남으며, 검토된 권한 목록과 복사·붙여넣기로 늘어난 목록의 차이입니다.

관련 글