Pon un cliente VPN nun contedor e obtés un espazo de nomes cun túnel dentro, non un servidor que encamiña todo o seu tráfico por unha VPN. Son proxectos separados, e tratalos como un só é a forma en que os contedores acaban privilexiados por accidente. Este manual só cobre o primeiro: unha imaxe revisada, un perfil protexido e un proceso de cliente confinado ao seu propio espazo de nomes de rede.

Comeza por un artefacto que alguén xa aprobou. Os pasos seguintes asumen unha imaxe que contén o executable openvpn e un perfil de cliente completo co seu certificado e material de chave xa resoltos dentro da montaxe. Non constrúen software, non emiten credenciais nin cobren contornos sen root nin contedores nativos de Windows, e cada un deles cambia a resposta.

Que recibe o contedor e que non

Mantén o perfil e todo o que referencia nun directorio local con permisos restrinxidos, montado en só lectura. As rutas relativas dentro do perfil deben resolverse dentro desa montaxe; é un detalle que a xente pasa por alto cando un certificado está un directorio por riba do perfil e o contedor nunca o ve.

services:
  vpn:
    image: ${VPN_CLIENT_IMAGE:?set the reviewed image reference}
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./vpn:/vpn:ro
    working_dir: /vpn
    entrypoint: ["openvpn"]
    command: ["--config", "/vpn/client.ovpn"]

Ese ficheiro concede tres cousas e non pide nada máis. A referencia da imaxe debe fixarse por digest se a túa política de despregamento o di, e o perfil debe ser o revisado, non unha copia que alguén editou no servidor.

Só lectura aquí significa que o contedor non pode modificar os teus ficheiros do servidor; non significa que non poida ler a chave privada que montaches. Ese é todo o sentido da montaxe, e tamén por iso os permisos de ficheiro, a propiedade e como chegou o segredo ao servidor seguen importando. Nunca pases un contrasinal pola liña de ordes nin o incrustes nunha capa de imaxe, porque ambos sobreviven en lugares que non poderás limpar despois.

Capacidades, entrypoint e a trampa entre elas

Fan falta dous axustes e fan traballos non relacionados. NET_ADMIN permite manipular interfaces e rutas dentro do espazo de nomes do contedor. A entrada de dispositivo /dev/net/tun permite ao proceso abrir o dispositivo de túnel sequera. Conceder un sen o outro produce un fallo claro, o cal é cómodo: un dispositivo TUN ausente falla ao abrir, mentres que unha capacidade ausente falla cando o cliente tenta configurar a interface.

Ningunha das dúas xustifica privileged: true. Un contedor privilexiado recibe todas as capacidades máis acceso a dispositivos, e é unha concesión moito maior do que necesita esta carga de traballo. A referencia de privilexios e capacidades do contorno de execución explica ata onde chega esa concesión. Se un chart ou unha entrada de blog o pide, comproba primeiro se o requisito era en realidade unha ruta de dispositivo ausente ou unha capacidade ausente.

A substitución de entrypoint é a parte que morde. Invocar openvpn directamente é correcto para unha imaxe corrente e incorrecto para calquera imaxe de produto cuxo script de arranque prepara regras de devasa, ficheiros de resolución ou credenciais antes de executar o daemon. Substituír ese script omite a preparación e déixache depurando un túnel que arrancou nun espazo de nomes a medio configurar.

Executa o cliente en primeiro plano e deixa que sexa o proceso principal do contedor. Un proceso que se demoniza sae de inmediato, o contedor reiníciase en bucle, e os rexistros que necesitas quedan repartidos entre reinicios que parecen idénticos.

Verifica desde dentro do espazo de nomes, non desde o servidor

Levanta a pila, confirma no rexistro que o cliente acadou un estado conectado e despois inspecciona o espazo de nomes que realmente contén o túnel.

docker compose up -d
docker compose logs --tail 40 vpn
docker compose exec vpn ip -br addr
docker compose exec vpn ip route

A lista de enderezos debería mostrar unha interface de túnel cun enderezo do grupo, e a táboa de rutas debería mostrar os destinos empurrados apuntando a ela. Se falta a interface, o cliente nunca chegou a creala. Se existe pero ningunha ruta a usa, o lugar onde mirar son as opcións de push ou de ruta.

Despois proba o destino que che importa, desde o espazo de nomes que se supón que usa o túnel, coa aplicación e non só cun ping. A resolución de nomes é un fallo aparte que merece probarse explicitamente: un enderezo DNS empurrado non actualiza por si só o resolvedor dun contedor, e un cliente autónomo que ignora o push resolverá nomes a través do que configurase a imaxe.

O estado desconectado forma parte do contrato

O comportamento de reconexión necesita a súa propia comprobación, porque unha política de reinicio demostra que o proceso se reinicia e non di nada sobre o que fai o tráfico mentres tanto. Detén o túnel, proba o destino previsto e observa se a conexión se rexeita, se colga ou ten éxito silenciosamente pola ruta ordinaria do contedor. O último resultado é o que importa, e é invisible nun rexistro san.

Se a carga de traballo non debe usar nunca a ruta ordinaria, deseña iso explicitamente con regras de devasa e un sinal de preparación dentro do espazo de nomes. Un kill switch é unha política que escribes e probas, non unha propiedade que herdas por executar un cliente nun contedor.

Por último, lembra a fronteira: estas rutas viven no espazo de nomes do contedor e non substitúen a táboa de encamiñamento do servidor. As regras de reenvío, tradución e devasa de Docker seguen formando parte do camiño exterior, e un porto publicado non é unha interface VPN.

Artigos relacionados