Configuração de servidor OpenVPN

OpenVPN é um sistema de rede virtual privada (VPN), usado pra criar conexões seguras entre dispositivos, protegendo a troca de informações mesmo em trajetos não confiáveis, como a internet. Implementa tanto aplicação de servidor quanto de cliente e é distribuído sob a licença GPL-2.0.

O sistema funciona com base em infraestrutura de chave pública (PKI). Pra configurar a autenticação de forma segura é necessário gerar certificados SSL e chaves para uma autoridade certificadora (CA), para o servidor e para os clientes da VPN.

Felizmente, não precisamos tocar diretamente no OpenSSL, que é bem complexo. Em vez disso, a gente usa o EasyRSA, uma ferramenta que nos ajuda a gerar as chaves e certificados que a gente precisa.

Considerando uma instalação do Debian ou Ubuntu server:

# apt install openvpn easy-rsa

Pra facilitar e deixar a configuração organizada, copie a pasta do EasyRSA para dentro da pasta de configuração do OpenVPN.

# cp -r /usr/share/easy-rsa /etc/openvpn/

Vá para a pasta /etc/openvpn/easy-rsa, edite copie o arquivo vars.example como var e descomente as linhas que quiser editar de acordo com a sua preferência. Configurações sugeridas:

set_var EASYRSA_KEY_SIZE        4096
set_var EASYRSA_CERT_EXPIRE     731
set_var EASYRSA_CRL_DAYS        366
set_var  EASYRSA_FIX_OFFSET 30

Dessa forma, os certificados serão válidos por 2 anos, a lista de revogação de certificados (CRL) por 1 ano, e todos os certificados vão ser emitidos com validade fixa a partir de 30 de Janeiro.

Após configurado, podemos iniciar nossa PKI e gerar o CA:

# bash easyrsa init-pki
# bash easyrsa build-ca

Ao gerar a chave do CA, uma senha será pedida pra criptografar a chave do CA.

Deve-se também gerar a chave de Diffie-Hellman, que é usada para gerar a chave simétrica da conexão de VPN a ser estabelecida.

# bash easyrsa gen-dh

Opcionalmente, para maior segurança, pode-se gerar uma chave de autenticação para que o servidor exija uma assinatura válida nos pacotes recebidos dos clientes.

# openvpn --genkey secret pki/tls-auth.key

Ao gerar o par de chaves do servidor, é uma boa ideia gerar a chave sem senha pra que o serviço possa ser iniciado junto com o sistema:

# bash easyrsa build-server-full nome_do_servidor nopass

A senha da chave do CA ainda será pedida para assinar o certificado do servidor.

Assim como para o servidor, você pode escolher deixar as chaves dos clientes com senha ou não passando o parâmetro opcional nopass.

# bash easyrsa build-client-full nome_do_cliente0 nopass

Agora, a configuração do servidor e dos clientes vai depender do propósito da VPN: pode ser um gateway, pode ser apenas para conectar um cliente ao outro, pode ser pra conectar clientes à uma LAN.

Crie um arquivo com extensão .conf na pasta /etc/openvpn server, como por exemplo config_do_servidor.conf com esse conteúdo de exemplo – adapte-o para a sua necessidade:

# Configurações da conexão
# Descomente a linha abaixo e coloque o IP do seu servidor caso ele esteja dentro de uma LAN
;local 192.168.0.10
port 1194
proto udp4
dev tun0
keepalive 10 120
persist-key
persist-tun

# As linhas abaixo determinam a rede privada que seu servidor vai criar
server 10.8.0.0 255.255.255.0
topology subnet
push "route 10.8.0.0 255.255.255.0"

# Descomente a linha abaixo e substitua a LAN pela sua caso queira que os clientes tenham uma rota para a LAN do servidor
;push "route 192.168.0.0 255.255.255.0"

# Descomente a linha abaixo caso queira que o cliente use o servidor de VPN como gateway
;push "redirect-gateway def1 bypass-dhcp"

# Configura resolvedores de DNS alternativos
;push "dhcp-option DNS 208.67.222.222"
;push "dhcp-option DNS 208.67.220.220"

# Permite que clientes da VPN possam alcançar outros
;client-to-client

# Limita o número de clientes na VPN
;max-clients 5

# Configurações do processo
verb 3
explicit-exit-notify 1
status /var/log/openvpn/status-conext00.log
# Reduz o privilégio de execução após inicialização para o usuário e grupo abaixo - em distribuições da família Fedora e Red Hat, existe um usuário openvpn próprio. O grupo nogroup não existe.
user nobody
group nogroup
# Mantém clientes com o mesmo IP na VPN
;ifconfig-pool-persist /var/log/openvpn/ipp.txt

# Certificados e chaves necessários gerados através do easy-rsa
ca ../easy-rsa/pki/ca.crt
cert ../easy-rsa/pki/issued/nome_do_servidor.crt
key ../easy-rsa/pki/private/nome_do_servidor.key
dh ../easy-rsa/pki/dh.pem

# Lista opcional de revogação de certificados
;crl-verify ../easy-rsa/pki/crl.pem

# Chave TLS opcional para maior segurança, exigindo que pacotes recebidos tenham uma assinatura válida
;tls-auth ../easy-rsa/pki/tls-auth.key 0

Observe que as linhas do tipo

push "algum parâmetro"

são configurações que também podem ser especificadas no arquivo de configuração do cliente em vez do servidor.

Precisa-se também configurar as permissões de firewall e habilitar no kernel o encaminhamento em IPv4.

Para habilitar o encaminhamento no kernel, adicione um arquivo /etc/sysctl.d/98-ip-fwd.conf com a seguinte linha:

net.ipv4.ip_forward=1

Reinicie para que tenha efeito ou

# sysctl -w net.ipv4.ip_forward=1

No firewall, para permitir conexão de clientes:

# ufw allow 1194/udp

Caso use o servidor de VPN como um gateway ou estabeleça rota do cliente para a LAN em que o servidor está:

# ufw route allow in on tun0 out on eth0
# ufw route allow in on eth0 out on tun0

E configure o protocolo NAT adicionando este trecho no topo do arquivo /etc/ufw/before.rules

### START OF MANUAL NAT CONFIG
# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic through eth0 - Change to match you out-interface
-A POSTROUTING -s 10.8.0.0/24 -o tun0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't
# be processed
COMMIT
### END OF MANUAL NAT CONFIG

Lembre-se de substituir a interface física e a rede pelas do seu servidor.

Alternativamente ao NAT, no caso da existência de uma LAN, você pode configurar no seu roteador uma rota para 10.8.0.0/24 pelo IP privado do seu servidor.

Agora basta habilitar e iniciar o serviço:

# systemctl enable --now openvpn-server@config_do_servidor.service

É isso, o servidor deve estar funcionando.

Agora precisamos que clientes se conectem. Pra isso, eles precisam de um arquivo de configuração do mesmo tipo que foi feito para o servidor. O easy-rsa não gera esses arquivos, então eu fiz um pequeno script pra resolver esse problema, que deixo em /etc/openvpn/easy-rsa/build-client-config.sh

Conteúdo do script:

#!/bin/bash

system=$1
client=$2
clients_dir=./clients
client_template_win=./clients/client.ovpn
client_template_lnx=./clients/client.conf


init() {
        mkdir $clients_dir
cat <<'EOF' > $client_template_win
client
dev tun
proto udp4
remote servidor.exemplo.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
;key-direction 1
verb 3
EOF

cat <<'EOF' > $client_template_lnx
client
dev tun
proto udp4
remote servidor.exemplo.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
;key-direction 1
verb 3
user nobody
group nogroup
EOF

        if ! [[ -f ./pki/ca.crt ]]; then
                echo "CA não encontrado. Encerrando."
                exit 1
        else
                echo "" | tee -a $client_template_lnx $client_template_win
                echo "<ca>" | tee -a $client_template_lnx $client_template_win
                cat pki/ca.crt | tee -a $client_template_lnx $client_template_win
                echo "</ca>" | tee -a $client_template_lnx $client_template_win
        fi

        if [[ -f ./pki/tls-auth.key ]]; then
                sed -i 's/;//g' $client_template_lnx $client_template_win
                echo "" | tee -a $client_template_lnx $client_template_win
                echo "<tls-auth>" | tee -a $client_template_lnx $client_template_win
                cat pki/tls-auth.key | tee -a $client_template_lnx $client_template_win
                echo  "</tls-auth>" | tee -a $client_template_lnx $client_template_win
        else
                echo "tls-auth.key não encontrada, prosseguindo sem"
        fi
}

if ! [[ -d $clients_dir ]]; then
        init
        exit
fi

if ! [[ -f ./pki/issued/$client.crt ]]; then
        echo "Cliente não existe"
        exit 1
fi

if [[ $system == "-l" || $system == "--linux" ]]; then
        fileExt=conf
elif [[ $system == "-o" || $system == "--outro" ]]; then
        fileExt=ovpn
else
        echo "Sistema não especificado"
fi

targetFile=clients/$client.$fileExt

cp clients/client.$fileExt $targetFile

echo "" | tee -a $targetFile
echo "<cert>" | tee -a $targetFile
cat pki/issued/$client.crt | tee -a $targetFile
echo "</cert>" | tee -a $targetFile

echo "" | tee -a $targetFile
echo "<key>" | tee -a $targetFile
cat pki/private/$client.key | tee -a $targetFile
echo "</key>" | tee -a $targetFile

Pra inicializar, basta executar o script sem nenhum argumento

# bash build-client-config.sh

Ele vai criar uma pasta clients, com um template para Linux, client.conf, e outro para Windows, client.ovpn. Edite-os conforme necessário.

Feito isso, é possível criar o arquivo de configuração para um cliente existente com

# bash build-client-config.sh --linux nome_do_cliente0

ou para Windows, macOS, iOS ou Android

# bash build-client-config.sh --outro nome_do_cliente0