OpenVPN + SSH Proxy — Implementação Passo a Passo
OpenVPN + SSH Proxy — Implementação Passo a Passo
No post anterior expliquei o problema que me levou a criar o openvpn-ssh-proxy: a necessidade de acessar múltiplas redes VPN simultaneamente, sem conflito de rotas, sem instalar clientes pesados na máquina host. Agora vamos ao que interessa — colocar tudo pra funcionar.
Pré-requisitos
Antes de começar, você vai precisar de:
- Docker instalado na máquina host (versão 20.10 ou superior)
- Um ou mais arquivos
.ovpnválidos (configuração de cliente OpenVPN fornecida pelo seu provedor de VPN ou administrador de rede) - Conhecimento básico de redes — saber o que é uma rota, uma interface TUN e um proxy SOCKS já basta
Não há nada para compilar ou instalar além do Docker. A imagem mauricioj/openvpn-ssh-proxy:latest já vem com OpenVPN e o servidor SSH embutidos.
Quick Start com docker run
O jeito mais rápido de testar é com um único docker run. Vou dissecar cada flag:
docker run -d \
--device /dev/net/tun \
--cap-add NET_ADMIN \
-v /caminho/para/config.ovpn:/vpn/config.ovpn:ro \
-e OVPN_FILE_PATH=/vpn/config.ovpn \
-e OVPN_AUTH_USER=meu_usuario \
-e OVPN_AUTH_PASS=minha_senha \
-e SSH_TUNNEL_PORT=2222 \
-e SSH_TUNNEL_USER=tunnel \
-e SSH_TUNNEL_PASS=tunnelpass \
-p 2222:2222 \
--name vpn-trabalho \
mauricioj/openvpn-ssh-proxy:latest
O que cada flag faz:
--device /dev/net/tun— expõe o dispositivo TUN do host para o container. O OpenVPN precisa criar uma interfacetun0dentro do container, e sem acesso a esse device, o processo falha imediatamente.--cap-add NET_ADMIN— concede ao container a capability de Linux necessária para manipular interfaces de rede e tabelas de roteamento. Sem ela, o OpenVPN não consegue configurar as rotas.-v /caminho/para/config.ovpn:/vpn/config.ovpn:ro— monta o arquivo de configuração dentro do container em modo somente leitura. Ajuste o caminho da esquerda para o local real do seu.ovpn.-e OVPN_FILE_PATH— diz ao entrypoint onde encontrar o arquivo de configuração dentro do container.-e OVPN_AUTH_USERe-e OVPN_AUTH_PASS— credenciais de autenticação da VPN, caso o servidor exija usuário e senha além do certificado.-e SSH_TUNNEL_PORT— porta em que o servidor SSH vai escutar dentro do container (e que você mapeia com-p).-e SSH_TUNNEL_USERe-e SSH_TUNNEL_PASS— usuário e senha para autenticar no servidor SSH do container.-p 2222:2222— publica a porta SSH para que você possa conectar a partir do host.
Se o seu .ovpn usa certificado com passphrase, adicione também -e OVPN_CERT_PASSPHRASE=sua_passphrase.
Rodando Duas VPNs Simultaneamente com Docker Compose
Aqui está onde a proposta do projeto brilha de verdade. Preciso acessar a VPN do trabalho e a VPN de um cliente ao mesmo tempo? Sem problema — cada container é isolado, cada um tem seu próprio tun0 e suas próprias rotas.
services:
vpn-trabalho:
image: mauricioj/openvpn-ssh-proxy:latest
container_name: vpn-trabalho
restart: unless-stopped
devices:
- /dev/net/tun
cap_add:
- NET_ADMIN
volumes:
- ./configs/trabalho.ovpn:/vpn/config.ovpn:ro
environment:
OVPN_FILE_PATH: /vpn/config.ovpn
OVPN_AUTH_USER: meu_usuario_trabalho
OVPN_AUTH_PASS: minha_senha_trabalho
SSH_TUNNEL_PORT: 2222
SSH_TUNNEL_USER: tunnel
SSH_TUNNEL_PASS: tunnelpass
ports:
- "2222:2222"
vpn-cliente:
image: mauricioj/openvpn-ssh-proxy:latest
container_name: vpn-cliente
restart: unless-stopped
devices:
- /dev/net/tun
cap_add:
- NET_ADMIN
volumes:
- ./configs/cliente.ovpn:/vpn/config.ovpn:ro
environment:
OVPN_FILE_PATH: /vpn/config.ovpn
OVPN_AUTH_USER: meu_usuario_cliente
OVPN_AUTH_PASS: minha_senha_cliente
SSH_TUNNEL_PORT: 2222
SSH_TUNNEL_USER: tunnel
SSH_TUNNEL_PASS: tunnelpass
ports:
- "2223:2222"
Note que as portas do host são diferentes (2222 e 2223), mas dentro de cada container o SSH escuta em 2222. Isso é justamente a vantagem do isolamento — cada container vive em seu próprio namespace de rede.
Suba tudo com:
docker compose up -d
Referência das Variáveis de Ambiente
| Variável | Obrigatória | Descrição |
|---|---|---|
OVPN_FILE_PATH | Sim | Caminho do arquivo .ovpn dentro do container |
OVPN_CERT_PASSPHRASE | Não | Passphrase do certificado cliente, se houver |
OVPN_AUTH_USER | Não | Usuário para autenticação via auth-user-pass |
OVPN_AUTH_PASS | Não | Senha para autenticação via auth-user-pass |
OVPN_OPTS | Não | Flags adicionais passadas diretamente ao OpenVPN (ex: --verb 4) |
SSH_TUNNEL_PORT | Sim | Porta em que o servidor SSH vai escutar |
SSH_TUNNEL_USER | Sim | Usuário SSH para autenticação no container |
SSH_TUNNEL_PASS | Sim | Senha SSH para autenticação no container |
Testando os Túneis
Com os containers rodando, há dois casos de uso principais.
Proxy SOCKS5 para navegação e acesso geral
O SSH suporta criar um proxy SOCKS5 com a flag -D. Todo tráfego roteado por esse proxy vai sair pelo IP da VPN:
ssh -D 1080 \
-p 2222 \
-o StrictHostKeyChecking=no \
tunnel@localhost \
-N
A flag -N diz ao SSH para não executar nenhum comando remoto — só abrir o túnel. Com o proxy SOCKS5 ativo na porta 1080 local, você pode testar com curl:
curl --socks5 localhost:1080 https://ifconfig.me
O IP retornado deve ser o IP de saída da VPN, não o IP real da sua máquina.
Para a segunda VPN, basta mudar a porta de conexão SSH e a porta local do SOCKS:
ssh -D 1081 \
-p 2223 \
-o StrictHostKeyChecking=no \
tunnel@localhost \
-N
Port Forwarding para acessar um serviço interno
Se preciso acessar um serviço específico dentro da rede VPN — digamos, um servidor de banco de dados em 10.8.0.50:5432 — uso redirecionamento de porta com -L:
ssh -L 5432:10.8.0.50:5432 \
-p 2222 \
-o StrictHostKeyChecking=no \
tunnel@localhost \
-N
Agora localhost:5432 no meu host aponta direto para o Postgres dentro da rede VPN. Posso conectar qualquer cliente de banco de dados normalmente.
No Windows, o mesmo comando funciona no PowerShell ou no terminal do VS Code com OpenSSH instalado:
ssh -L 5432:10.8.0.50:5432 `
-p 2222 `
-o StrictHostKeyChecking=no `
tunnel@localhost `
-N
Debugging
Coisas não funcionando? Aqui está o meu processo de diagnóstico.
Ver os logs do container em tempo real:
docker logs -f vpn-trabalho
Procure por linhas com Initialization Sequence Completed — isso confirma que o OpenVPN conectou com sucesso. Erros de autenticação ou de certificado aparecerão aqui claramente.
Entrar no container para inspecionar o estado:
docker exec -it vpn-trabalho sh
De dentro do container, verifico se a interface TUN foi criada e se as rotas estão corretas:
ip addr show tun0
ip route show
Se tun0 não aparece, o OpenVPN não subiu — os logs vão indicar o motivo. Se tun0 existe mas o tráfego não flui, verifique se as rotas da rede interna da VPN foram adicionadas corretamente.
Ativar logs verbosos do OpenVPN sem recriar o container, basta usar a variável OVPN_OPTS:
environment:
OVPN_OPTS: "--verb 4"
Com --verb 4 você vê o handshake TLS completo e os pacotes de controle, o que ajuda a identificar problemas de certificado ou de compatibilidade de cifras.
Proximo Passo
A implementação está funcionando, mas antes de usar isso em produção ou em um ambiente de trabalho real, há alguns pontos importantes sobre segurança que vale considerar — credenciais em variáveis de ambiente, superfície de ataque do SSH exposto, e como minimizar os riscos.
Cubro tudo isso no terceiro post da série, junto com as lições aprendidas durante o desenvolvimento do projeto.