[DOCKER] Subindo OpenVPN no seu Docker

terça-feira, 9 de abril de 2019

[DOCKER] Subindo OpenVPN no seu Docker


Fala Jovens Padawans, Como estão ?

No post de hoje vamos abordar um esqueminha hands-on pra colocar aquela VPN simples pra rodar em Docker usando o famigerado OpenVPN.

Essa semana eu estava com uns probleminhas para os dev's da empresa em que presto serviço fecharem conexão com as bases de dados do nosso ambiente de testes.
Até proporcionei um PGAdmin, mas o pessoal prefere, por comodidade, usar a conexão direto no PHPStorm.

Aí vocês me dizem, "Pô, mas era só limitar o acesso direto no pg_hba.conf pelo IP do escritório." ou "Pô, sobe uma VPN site-to-site aí no escritório, menos incômodo.".
Acontece que, lá no escritório nós não temos um servidor on premises e muito menos um IP fixo. Logo, complica um pouco colocar em prática essas soluções.
Por mais que os dev's possuam seus ambientes locais, em Docker, muitas vezes eles precisam realizar alguns testes mais "garimpados".

Foi aí que resolvi colocar um OpenVPN "simplão" no nosso server de testes.




Cenário

Então vamos lá, para ilustrar o nosso hands-on, vou colocar um cenário do ambiente que vamos ter:

  • 1 Docker Network montada separadamente para os containers (10.0.81.0/24) [Vamos chamá-la de app_net]
  • 1 VPN Network (192.168.254.0/24) montada no nosso OpenVPN
  • 1 Rota configurada no firewall (iptables) para comunicação entre as redes (tun0 - eth0) do nosso container OpenVPN
  • Vamos trabalhar com a porta externa 3000/UDP para conexão na VPN
  • Vamos utilizar um DNS para gerar as configurações do OpenVPN, DNS do nosso host Docker (vpn.keep-linx.ga)

Preparando o Terreno (o oceano....)

Como não usaremos uma imagem pré-construída, vamos clonar o repositório (originalmente do kylemanna sob a licença MIT) e compilar nossa própria imagem com o Dockerfile e outros scripts. Esses "outros scripts" são uma série de shell scripts MUITO BEM desenvolvidos para já gerarem tudo em perfeitas condições dentro do nosso Docker, esse kylemanna realmente fez algo precioso com esses scripts.

Vamos adotar o diretório /opt como sendo nossa raiz do projeto.

$ cd /opt
# Vamos criar a pasta onde ficarão os arquivos do nosso openvpn (o volume Docker)
(opt)$ mkdir -p /opt/docker-openvpn/app/ovpn-data
# Clonem o repositório
(opt)$ git clone https://github.com/rmorilha/docker-openvpn.git
# Navegue até o nosso repositório clonado
(opt)$ cd docker-openvpn/

Agora, vamos compilar a imagem baseada no Alpine Linux, gerando uma tag em nosso docker:

(docker-openvpn)$ docker build -t keepl/ovpn .

Apenas com estes passos, já podemos começar a gerar nossa configuração do servidor OpenVPN de acordo com os scripts que estão no repositório clonado e nosso DNS apontado para o host docker:

(docker-openvpn)$ docker run -v /opt/docker-openvpn/app/ovpn-data:/etc/openvpn --rm keepl/ovpn ovpn_genconfig -u udp://vpn.keep-linux.ga:3000

Processing PUSH Config: 'block-outside-dns'
Processing Route Config: '192.168.254.0/24'
Processing PUSH Config: 'dhcp-option DNS 8.8.8.8'
Processing PUSH Config: 'dhcp-option DNS 8.8.4.4'
Successfully generated config
Cleaning up before Exit ...


No comando acima, a opção --rm é utilizada para que o script que queremos seja executado em um container que utiliza a imagem construída e o container seja removido imediatamente após sua execução, proporcionando para nós um ambiente mais limpo no Docker.
Após definir o volume montado (-v) e removermos o container após sua execução (--rm), o comando docker run executa de fato o comando/script que queremos, no caso ovpn_genconfig -u udp://vpn.keep-linux.ga:3000.

O próximo passo é gerar as chaves criptografadas (PKI) que uma VPN exige para o servidor OpenVPN funcionar.
Neste passo, serão realizadas algumas validações como, uma senha para a chave PKI gerada (PEM passphrase) guarde-a bem, Common Name (o CN é o nome que nosso servidor reconhecerá que será atendido, logo, nosso DNS apontado, vpn.keep-linux.ga).
Utilize a mesma senha para todas as perguntas sobre senha, é importantíssimo que sejam iguais.
Fiquem atentos a estes passos e suas informações:

(docker-openvpn)$ docker run -v /opt/docker-openvpn/app/ovpn-data:/etc/openvpn --rm -it keepl/ovpn ovpn_initpki
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /etc/openvpn/pki
Generating a 2048 bit RSA private key
............................................................................+++
....+++
writing new private key to '/etc/openvpn/pki/private/ca.key.XXXXCFGIEm'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
...
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:g
CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/etc/openvpn/pki/ca.crt
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
... # Wait a while
Enter pass phrase for /etc/openvpn/pki/private/ca.key:
Check that the request matches the signature
...
Enter pass phrase for /etc/openvpn/pki/private/ca.key:
An updated CRL has been created.
CRL file: /etc/openvpn/pki/crl.pem

Com estes passos realizados, podemos subir o nosso servidor OpenVPN através do arquivo docker-compose.yml que deixei criado para vocês no repositório GIT.
Caso tenham desejo por alterar o caminho do volume criado, lembrem-se de alterar no docker-compose.yml também.

(docker-openvpn)$ docker-compose up -d
(docker-openvpn)$ docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS              PORTS                    NAMES
003a8cd58ccb        keepl/ovpn               "ovpn_run"               48 seconds ago        Up 1 hours        0.0.0.0:3000->1194/udp   ovpn

É possível ainda, subirmos o nosso servidor sem o docker-compose, através do comando docker comum:

(docker-openvpn)$ docker run -v /opt/docker-openvpn/app/ovpn-data:/etc/openvpn -d -p 3000:1194/udp --cap-add=NET_ADMIN keepl/ovpn

Notem que temos uma opção bem interessante no comando acima (docker run) (descobri esses dias também, sempre aprendendo) a opção --cap. Esta opção permite que tenhamos algumas "regalias" dentro do container docker no âmbito do RunTime (privileges) e permissões do Linux, escalando permissões que não são permitidas por default para um container (ainda bem !) para acesso em seu host. 
No nosso caso, escalamos o container para termos a capability de NET_ADMIN, ou seja, para que o container possa interagir mais com a camada de network do Docker e do host.
Para quem quiser estudar mais sobre, dêem uma olhada na página de documentação do docker (https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) e na man 7 page do Linux  (http://man7.org/linux/man-pages/man7/capabilities.7.html).

Criando usuários em nosso OpenVPN

Agora que temos nosso servidor OpenVPN montado e configurado, precisamos criar nosso usuário de conexão com o easyrsa. Vamos chamar nosso usuário de keeplinux Ok ?

Lembram da senha que inserimos no passo de geração da chave PKI do servidor OpenVPN ? Pois é, vamos precisar dela para podermos criar um usuário em nossa VPN. No passo abaixo, insiram aquela senha.

(docker-openvpn)$ docker run -v /opt/docker-openvpn/app/ovpn-data:/etc/openvpn --rm -it keepl/ovpn easyrsa build-client-full keeplinux nopass
Generating a 2048 bit RSA private key
........................................................+++
..........................................................+++
writing new private key to '/etc/openvpn/pki/private/keeplinux.key.XXXXeoGIJE'
-----
Using configuration from /usr/share/easy-rsa/openssl-1.0.cnf
Enter pass phrase for /etc/openvpn/pki/private/ca.key:
...

Note que utilizamos a opção nopass, esta opção permite que façamos login na VPN sem a necessidade de uma senha, apenas com o arquivo de configuração do client OpenVPN que vamos gerar abaixo.

Neste passo, vamos gerar o arquivo que será enviado ao usuário, para criar a conexão da VPN em sua máquina local.

(docker-openvpn)$ docker run -v /opt/docker-openvpn/app/ovpn-data:/etc/openvpn --rm keepl/ovpn ovpn_getclient keeplinux > /opt/vpnusers/keeplinux.ovpn

Com o arquivo do usuário criado, basta enviá-lo ao usuário que precisa se conectar à VPN. No nosso exemplo, criamos o arquivo dentro de /opt/vpnusers/


Criando um forward de tráfego no firewall do servidor

Ok, chegamos na parte onde é muito específica de cada ambiente. O Firewall e suas regras com suas networks etc....

Levando em consideração que temos uma Docker Network (alvo do nosso acesso via VPN) e a própria VPN Network (rede onde pegamos um IP assim que conectamos na VPN), podemos executar alguns comandos dentro do container OpenVPN para gerar rotas entre estas redes e nos comunicar com os demais containers.

A interface criada pelos scripts no nosso container OpenVPN para a rede VPN é a tun0 e, geralmente, a interface que a Docker Network é "attachada" no container é a eth0.
Então vamos entrar em nosso container e inserir algumas regras de firewall para viabilizar a comunicação entre estas interfaces.
NOTA: Este passo é experimental e os comandos podem não ajudar completamente. Na situação em que passei, resolveram de certa forma. Porém, os scripts do container devem já realizar a implementação de regras NAT e este passo pode não ser necessário.

(docker-openvpn)$ docker exec -ti ovpn bash
(container)$ iptables -A FORWARD -i tun0 -j ACCEPT
(container)$ iptables -A FORWARD -i eth0 -j ACCEPT
(container)$ iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT

Espero que tenham gostado desse post. Na próxima vamos ver como criar uma docker network e inserir nossos containers nela via docker-compose.



SESSÃO EXTRA

Para quem quiser ir mais a fundo desse exemplo, o dev original (kylemanna) desse projeto deixou uma porrada de docs para seguirmos. Podem pegar por lá.
https://github.com/rmorilha/docker-openvpn/tree/master/docs


3 comentários :

  1. Show de bola, Morilha!

    Uma pena não ter dado tempo para aprender mais contigo sobre Docker, infra, etc. Quem sabe mais pra frente? Abraço :D

    ResponderExcluir
  2. Fala Danilão !
    Valeu brother. Vamos combinar um meetup para conversar mais, algum workshop interessante.


    Abraço !

    ResponderExcluir
  3. Opa, quando for me avisa, aí já chama a trupe toda também hahaha, abraço!

    ResponderExcluir