It Instalando OpenVPN com autenticação em MySQL
Post
Cancel

Instalando OpenVPN com autenticação em MySQL

OpenVPN é um software Linux utilizado para criação de túneis VPN. Neste artigo, demonstrarei passo-a-passo como instalar o OpenVPN com os seguintes pré-requisitos:

  • Versão atualizada do próprio repositório do OpenVPN;
  • Utilização de Certificado digital;
  • Autenticação via banco de dados MySQL;
  • Scripts em Python para atualização dos dados em tempo real (usuário conectado, usuário desconectado e dados trafegados).

Maiores informações e documentação do OpenVPN podem ser obtidas neste endereço: https://openvpn.net/.

Instalando pacotes e dependências

Antes de mais nada, é necessário realizar a instalação de todas as dependências necessárias para o correto funcionamento do ambiente.

Adicionando repositório do OpenVPN no ambiente

1
2
echo "deb http://build.openvpn.net/debian/openvpn/stable `lsb_release --codename --short` main" > /etc/apt/sources.list.d/openvpn-aptrepo.list
curl -s https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add -

Nota: No momento que criei este tutorial, a instalação do mesmo foi em um Ubuntu 20.04, mas o repositório do OpenVPN ainda não estava aceitando o seu codinome ‘focal’, sendo assim, eu utilizei o repositório do Ubuntu 18.04, ficando como abaixo a minha linha de comando:

1
echo "deb http://build.openvpn.net/debian/openvpn/stable bionic main" > /etc/apt/sources.list.d/openvpn-aptrepo.list

Atualizando o ambiente

1
apt-get update && apt-get -y upgrade

Instalando pacotes e dependências

1
2
apt install -y openvpn easy-rsa libpam-mysql python python3 python3-pip libmariadb-dev python3-dev mariadb-client mariadb-server iptables-persistent
pip3 install mysqlclient

Configurando a Autoridade Certificadora (CA)

O OpenVPN é uma VPN TLS/SSL. Isso significa que ele utiliza certificados para criptografar o tráfego entre o servidor e os clientes. Para emitir certificados confiáveis, precisaremos configurar nossa própria autoridade de certificação (CA) simples.

Antes de iniciar os comandos propriamente ditos, creio ser bem importante entendermos o que estamos fazendo. Como esse assunto de autoridade certificadora é algo complexo, recomendo a leitura deste artigo que escrevi sobre o assunto: https://www.helviojunior.com.br/it/security/introducao-criptografia/.

Para ilustrar o que iremos realizar em termos de autoridade de certificação (CA), observe a imagem abaixo:

Certificados

Observe que na imagem ilustramos 4 certificados:

  1. Root CA: Este é a autoridade máxima nessa nossa estrutura, é a partir dela que todos os outros certificados são gerados, sendo assim será o primeiro certificado a ser gerado e o mais importante. Portanto, no momento que formos criar uma senha para suas chaves privadas, crie uma senha complexa e a guarde com carinho.
  2. Servidor: Este será o segundo certificado a ser gerado. Ele é utilizado exclusivamente no servidor e tem a função de que os clientes (OpenVPN) possam confiar em seu servidor.
  3. Cliente n: Os certificados de cliente são para que o servidor possa confiar e ter a certeza que o cliente foi autorizado por você a conectar no seu ambiente. O recomendado é que tenha um certificado para cada cliente, mas dependendo da criticidade do seu ambiente, você pode utilizar um único certificado para todos os clientes, uma vez que a autenticação (neste nosso caso) se dará através de usuário e senha. Com a utilização de um certificado por cliente, você terá na prática dois fatores de autenticação (um deles o certificado e outro o usuário/senha).

Para começar, podemos copiar o diretório modelo easy-rsa em nosso diretório home com o comando make-cadir:

1
make-cadir ~/openvpn-ca

Vamos para o diretório recém-criado para começar a configuração do CA:

1
cd ~/openvpn-ca

Para configurar os valores que nossa CA irá utilizar, precisamos editar o arquivo vars dentro do diretório. Abra esse arquivo (~/openvpn-ca/vars) agora em seu editor de textos.

Dentro, você encontrará algumas variáveis que podem ser ajustadas para determinar como os seus certificados serão criados. Somente precisamos nos preocupar com algumas delas.

Na parte inferior do arquivo, localize as configurações que definem padrões de campo para novos certificados. Deve ser algo como isto:

1
2
3
4
5
6
7
8
...
#set_var EASYRSA_REQ_COUNTRY    "US"
#set_var EASYRSA_REQ_PROVINCE   "California"
#set_var EASYRSA_REQ_CITY       "San Francisco"
#set_var EASYRSA_REQ_ORG        "Copyleft Certificate Co"
#set_var EASYRSA_REQ_EMAIL      "me@example.net"
#set_var EASYRSA_REQ_OU         "My Organizational Unit"
...

Edite os valores para o que você preferir, mas não os deixe em branco:

1
2
3
4
5
6
7
8
...
set_var EASYRSA_REQ_COUNTRY    "BR"
set_var EASYRSA_REQ_PROVINCE   "SP"
set_var EASYRSA_REQ_CITY       "Sao Paulo"
set_var EASYRSA_REQ_ORG        "Helvio Junior"
set_var EASYRSA_REQ_EMAIL      "contato@helviojunior.com.br"
set_var EASYRSA_REQ_OU         "Helvio Junior Treinamentos"
...

When you have finished, save and close the file.

Building the Root Certification Authority (Root-CA)

Now, we can use the variables we defined and the easy-rsa utilities to build our certification authority.

Make sure you are in your CA directory, and then create your PKI and CA structure:

1
2
3
cd ~/openvpn-ca
./easyrsa init-pki
./easyrsa build-ca

At this point, you will be prompted for a passphrase for your CA keys, as well as the name of your CA.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1f 31 Mar 2020

Enter New CA Key Passphrase:
Re-Enter New CA Key Passphrase:
Generating RSA private key, 2048 bit long modulus (2 primes)
......................................................+++++
..................+++++
e is 65537 (0x010001)
Can't load /root/openvpn-ca/pki/.rnd into RNG
140290726450496:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:98:Filename=/root/openvpn-ca/pki/.rnd
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:Helvio Junior CA

CA creation complete, and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/root/openvpn-ca/pki/ca.crt

At the end of this process, you will have the public certificate of your CA (public key within an X509 certificate) in the file ~/openvpn-ca/pki/ca.crt, and its respective private key in the file ~/openvpn-ca/pki/private/ca.key. In the future, when creating the configuration file for your OpenVPN client, you will use the contents of this X509 certificate file (~/openvpn-ca/pki/ca.crt).

Creating Key and Cryptography Files

Next, let’s generate some additional files used during the encryption process.

First, let’s generate strong Diffie-Hellman keys to use during the key exchange by typing:

1
./easyrsa gen-dh

Later, we can generate an HMAC signature to strengthen the server’s TLS integrity verification features:

1
openvpn --genkey --secret ~/openvpn-ca/pki/private/ta.key

This process will result in the file ~/openvpn-ca/pki/private/ta.key, which will also be used when generating the configuration for the OpenVPN client.

Creating the Server Certificate

Next, let’s generate our OpenVPN server certificate and its corresponding private key.

Note: If you choose a different name than “server” here, you will have to adjust some of the instructions below. For example, when copying the generated files to the /etc/openvpn directory, you will have to replace the correct names. You will also need to modify the /etc/openvpn/server.conf file later to point to the correct .crt and .key files.

Start by generating the OpenVPN server certificate and key pair. You can do this by typing:

1
./easyrsa build-server-full server nopass

Este processo gerou 2 arquivos ~/openvpn-ca/pki/issued/server.crt e ~/openvpn-ca/pki/private/server.key.

Nota: Observer que neste comando passamos o parâmetro nopass que irá deixar a chave privada do nosso servidor sem senha, isso tem um certo risco de segurança mas se faz necessário pois caso essa chave tenha senha a cada reboot do servidor ou restart do ser serviço OpenVPN você teria que digitar a senha na console o que poderia ocasionar um falha no serviço.

Criando o certificado do cliente

A seguir, vamos gerar nosso certificado de cliente e sua respectiva chave privada. Conforme comentado anteriormente você tem 2 modelos de deploy de clientes, o primeiro deles menos seguro onde você gera somente 1 certificado para todos os clientes, e outro mais seguro onde você gera um certificado para cada cliente, cada modelo tem suas vantagens e desvantagens, segue algumas abaixo:

  • Um certificado para TODOS os clientes:
    1. Baixo custo de criação de novos clientes, pois basta adicionar no banco de dados (pois o arquivo de configuração é o mesmo para todos);
    2. Caso outras pessoas não autorizadas tenham acesso ao arquivo de configuração, poderão realizar um ataque de força bruta no usuário e senha de forma que não há como ter rastreabilidade qual cliente que vazou a configuração;
    3. Muito em alinhamento que o item acima, caso necessite revogar o certificado digital de cliente, terá que reenviar a configuração para todos os clientes;
  • Um certificado para CADA cliente:
    1. Custo médio de criação de novos clientes, pois para cada cliente novo se faz necessário gerar o certificado digital, chave privada e gerar um novo arquivo de configuração com esse certificado e chave;
    2. Fácil rastreabilidade em caso de vazamento de configuração pois o certificado é único para cada cliente;
    3. Fácil revogação do certificado digital, pois a regeração da configuração é para somente um cliente, sem impactar nos demais.

Desta forma o procedimento técnico para geração de novos clientes sempre será o mesmo, bastando alterar o nome cliente nos comandos a seguir.

1
./easyrsa build-client-full cliente1 nopass

Este processo gerou 2 arquivos ~/openvpn-ca/pki/issued/cliente1.crt e ~/openvpn-ca/pki/private/cliente1.key. Estes dois arquivos serão utilizados futuramente no momento da criação do arquivo de configuração do cliente OpenVPN.

Configurando o serviço do OpenVPN

Enfim, podemos começar a configuração do serviço OpenVPN utilizando as credenciais e arquivos que geramos.

Copiar os Arquivos para o Diretório OpenVPN

Para começar, precisamos copiar os arquivos que necessitamos para o diretório de configuração /etc/openvpn.

Podemos começar com todos os arquivos que acabamos de gerar. Eles foram colocados dentro do diretório ~/openvpn-ca/pki/ quando foram criados. Precisamos copiar o certificado e chave de nossa CA, o certificado e chave de nosso servidor, a assinatura HMAC, e o arquivo Diffie-Hellman.

1
2
cd ~/openvpn-ca/pki
root@M4v3r1ck:~/openvpn-ca/pki# cp ca.crt private/ca.key issued/server.crt private/server.key private/ta.key dh.pem /etc/openvpn

Configuração OpenVPN Server

Agora que nossos arquivos estão no lugar, podemos criar o arquivo de configuração do servidor. Crie o arquivo /etc/openvpn/server.conf com o conteúdo abaixo:

Configuracoes gerais

1
2
3
4
5
6
7
8
9
10
11
12
13
port 4321
proto udp
dev tun
sndbuf 0
rcvbuf 0
topology subnet
duplicate-cn
status-version 2
keepalive 10 120
persist-key
persist-tun
verb 3
comp-lzo no

Chaves

1
2
3
4
5
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-auth ta.key 0

Rede

1
2
3
4
server 192.168.50.0 255.255.255.0
ifconfig-pool-persist ipp_server.txt
#push "redirect-gateway def1 bypass-dhcp" # Opcional para redirecionar todo tráfego de rede pela VPN
#push "route 192.168.1.0 255.255.252.0" # rotas adicionais

Autenticacao

1
2
3
4
5
6
7
cipher AES-128-CBC
ncp-ciphers AES-256-GCM:AES-128-GCM
auth SHA1
user nobody
group nogroup
client-to-client
username-as-common-name

user/pass auth from mysql

1
plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn

script connect-disconnect (opcional)

1
2
3
script-security 2
client-connect /etc/openvpn/connected.py
client-disconnect /etc/openvpn/disconnected.py

Configuracoes especificas para cada cliente (opcional)

1
#client-config-dir /etc/openvpn/static_clients_server

Arquivo de status de usuarios conectados (opcional)

1
status openvpn-status.log

Verifique o local do plugin openvpn-auth-pam.so para ter certeza que o caminho apontado na configuração acima está correto.

1
find / -name "*pam*" | grep -ioE ".*openvpn.*so"

Em meu ambiente o arquivo foi localizado em /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so sendo assim vamos criar um link simbólico para o local da configuração.

1
2
mkdir /usr/lib/openvpn/
ln -s /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so /usr/lib/openvpn/openvpn-auth-pam.so

Criando a base de dados e configurando o conector do PAM para o MySQL

Para que o módulo do PAM possa se coletar e autenticar os usuários é necessária a criação da base de dados onde os usuários e senhas serão salvos bem como um arquivo de configuração do conector. Desta forma crie a base de dados conforme o script abaixo:

1
mysql -u root
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
CREATE DATABASE openvpn;
USE openvpn;

CREATE USER 'openvpn'@'localhost' IDENTIFIED BY 'MinhaS3nhASuperSegura';
GRANT ALL PRIVILEGES ON openvpn.* TO 'openvpn'@'localhost';
FLUSH PRIVILEGES;

CREATE TABLE IF NOT EXISTS `users` (
    `user_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
    `user_pass` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
    `user_mail` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
    `user_start_date` date NOT NULL,
    `user_end_date` date NOT NULL,
    `user_online` enum('yes','no') NOT NULL DEFAULT 'no',
    `user_enable` enum('yes','no') NOT NULL DEFAULT 'yes',
PRIMARY KEY (`user_id`),
KEY `user_pass` (`user_pass`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `log` (
    `log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
    `log_trusted_ip` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_trusted_port` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_remote_ip` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_remote_port` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `log_end_time` timestamp NULL,
    `log_received` float NOT NULL DEFAULT '0',
    `log_send` float NOT NULL DEFAULT '0',
PRIMARY KEY (`log_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Caso deseje criar um usuário de teste execute os comandos abaixo:

1
mysql -u root
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
CREATE DATABASE openvpn;
USE openvpn;

CREATE USER 'openvpn'@'localhost' IDENTIFIED BY 'MinhaS3nhASuperSegura';
GRANT ALL PRIVILEGES ON openvpn.* TO 'openvpn'@'localhost';
FLUSH PRIVILEGES;

CREATE TABLE IF NOT EXISTS `users` (
    `user_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
    `user_pass` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
    `user_mail` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
    `user_start_date` date NOT NULL,
    `user_end_date` date NOT NULL,
    `user_online` enum('yes','no') NOT NULL DEFAULT 'no',
    `user_enable` enum('yes','no') NOT NULL DEFAULT 'yes',
PRIMARY KEY (`user_id`),
KEY `user_pass` (`user_pass`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `log` (
    `log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `user_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
    `log_trusted_ip` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_trusted_port` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_remote_ip` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_remote_port` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
    `log_start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `log_end_time` timestamp NULL,
    `log_received` float NOT NULL DEFAULT '0',
    `log_send` float NOT NULL DEFAULT '0',
PRIMARY KEY (`log_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Caso deseje criar um usuário de teste execute os comandos abaixo:

1
mysql -u root
1
2
3
4
use openvpn;
INSERT INTO users (user_id, user_pass, user_start_date, user_end_date) VALUES ('helvio_junior','@Pass123', '2020-05-27

', '2030-05-27');

Caso deseje verificar o usuário criado:

1
mysql -u root
1
2
use openvpn;
SELECT * FROM users;

A saída deverá ser a seguinte:

1
2
3
4
5
+-----------------+---------------------+-------------+---------------------+---------------------+----------------+-------------+
| user_id         | user_pass           | user_mail   | user_start_date     | user_end_date       | user_online    | user_enable |
+-----------------+---------------------+-------------+---------------------+---------------------+----------------+-------------+
| helvio_junior   | @Pass123            | NULL        | 2020-05-27          | 2030-05-27          | no             | yes         |
+-----------------+---------------------+-------------+---------------------+---------------------+----------------+-------------+

Configurando o conector do PAM

Após criado o banco de dados, agora é necessário configurar o conector PAM para acessar o banco de dados criado.

Crie o arquivo /etc/openvpn/openvpn-auth-mysql.conf com o conteúdo abaixo:

1
2
3
4
5
6
MYSQL_SERVER      localhost
MYSQL_PORT        3306
MYSQL_USER        openvpn
MYSQL_PASSWORD    MinhaS3nhASuperSegura
MYSQL_DB          openvpn
MYSQL_CLEAR_TEXT_PASSWORDS    y

Após a criação do arquivo, é necessário configurar o PAM para o OpenVPN. Crie o arquivo /etc/pam.d/openvpn com o seguinte conteúdo:

1
2
auth    required    pam_mysql.so config-file=/etc/openvpn/openvpn-auth-mysql.conf
account required    pam_mysql.so config-file=/etc/openvpn/openvpn-auth-mysql.conf

Testando o módulo do PAM

Você pode testar se o módulo PAM está funcionando corretamente usando o comando pamtester. Certifique-se de que o usuário do sistema esteja adicionado ao grupo openvpn:

1
sudo usermod -a -G openvpn <seu_usuário>

Depois disso, você pode testar o módulo PAM com o comando pamtester. Por exemplo, para verificar a autenticação para o usuário “helvio_junior”:

1
sudo pamtester openvpn helvio_junior authenticate

A saída deve indicar que a autenticação foi bem-sucedida. Certifique-se de substituir <seu_usuário> pelo seu nome de usuário do sistema.

Script para liberar usuário

Para que um usuário seja liberado e possa acessar a VPN é necessário alterar o valor da coluna user_enable na tabela users para “yes”. Isso pode ser feito diretamente no banco de dados MySQL ou por meio de um script. Abaixo, você pode encontrar um exemplo de um script Python para realizar essa operação:

Crie o arquivo /etc/openvpn/enable_user.py com o seguinte conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python3
import sys
import MySQLdb

def enable_user(username):
    try:
        db = MySQLdb.connect(host="localhost", user="openvpn", passwd="MinhaS3nhASuperSegura", db="openvpn")
        cursor = db.cursor()

        # Atualiza o campo user_enable para 'yes' para o usuário especificado
        cursor.execute(f"UPDATE users SET user_enable='yes' WHERE user_id='{username}'")
        db.commit()

        print(f"Usuário {username} foi habilitado com sucesso.")
    except Exception as e:
        print(f"Erro ao habilitar o usuário {username}: {str(e)}")
    finally:
        db.close()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Uso: enable_user.py <username>")
        sys.exit(1)

    username = sys.argv[1]
    enable_user(username)

Dê permissões de execução ao script:

1
chmod +x /etc/openvpn/enable_user.py

Agora, você pode habilitar um usuário com o seguinte comando (substitua <username> pelo nome do usuário que deseja habilitar):

1
/etc/openvpn/enable_user.py <username>

Isso atualizará o campo user_enable para “yes” no banco de dados, permitindo que o usuário acesse a VPN.

Script para desabilitar usuário

Da mesma forma, você pode criar um script para desabilitar um usuário, impedindo que ele acesse a VPN. Crie o arquivo /etc/openvpn/disable_user.py com o seguinte conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python3
import sys
import MySQLdb

def disable_user(username):
    try:
        db = MySQLdb.connect(host="localhost", user="openvpn", passwd="MinhaS3nhASuperSegura", db="openvpn")
        cursor = db.cursor()

        # Atualiza o campo user_enable para 'no' para o usuário especificado
        cursor.execute(f"UPDATE users SET user_enable='no' WHERE user_id='{username}'")
        db.commit()

        print(f"Usuário {username} foi desabilitado com sucesso.")
    except Exception as e:
        print(f"Erro ao desabilitar o usuário {username}: {str(e)}")
    finally:
        db.close()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Uso: disable_user.py <username>")
        sys.exit(1)

    username = sys.argv[1]
    disable_user(username)

Dê permissões de execução ao script:

1
chmod +x /etc/openvpn/disable_user.py

Agora, você pode desabilitar um usuário com o seguinte comando (substitua <username> pelo nome do usuário que deseja desabilitar):

1
/etc/openvpn/disable_user.py <username>

Isso atualizará o campo user_enable para “no” no banco de dados, impedindo que o usuário acesse a VPN.

Fontes: