Discuta este tópico no fórum

Se este conteúdo te ajudou, deixe um presente!

terça-feira, 20 de janeiro de 2015

OpenWRT: Servidor VPN com OpenVPN

Mais um artigo da série sobre o OpenWRT.

Uma das características que acho mais fascinante da a internet é a possibilidade de trabalhar de qualquer lugar. Muitas empresas já utilizam esta característica para permitir que seus funcionários trabalhem em viagem, de casa, de onde estiver. Afinal, cada posto de trabalho e o deslocamento custa um bom trocado. Para trabalhar, em geral, é necessário ter acesso as informações e serviços da rede interna da empresa.

Esta necessidade de acesso também é comum para o mundo residencial. Quando temos um serviço na rede interna em casa e que queremos acessar "de fora", a técnica mais comum é o encaminhamento de portas. Ela mapeia uma porta do roteador para um serviço de computador da rede interna. Contudo, isto expõe estes serviços a ataques externos. Normalmente, não é uma boa ideia para serviços muito populares ou sem segurança.

Também podemos fazer este acesso por encaminhamento de portas em uma conexão SSH. Funciona muito bem e, desde que o SSH não seja comprometido, é seguro. Só não é muito prático. Configurar os encaminhamentos individualmente para cada serviço pode ser um tanto trabalhoso. Os acessos não ficam muito práticos pois tudo estará em portas locais (127.0.0.1:xxxx). Não fica transparente para o usuário. Para ter um acesso de forma mais simples e transparente, podemos usar uma conexão VPN.

A VPN (virtual private network) permite a conexão de dois pontos através de uma rede virtual, como se existisse um cabo de rede ligando diretamente as duas pontas. Para os programas que utilizarem este "cabo virtual", não existirá diferença entre estar ligado diretamente na rede local ou estar a milhares de quilômetros (exceto que a velocidade da internet é algumas vezes mais lenta que a rede local). Como a internet não é um meio dos mais seguros para trafegar informações internas (alô Obama!), se tornou quase implícito a utilização de criptografia nas conexões VPN.

Existem algumas formas de prover esta conectividade. As mais comuns são pelo protocolo PPTP, L2TP/IPSEC, ou pelo OpenVPN. O primeiro, apesar de ser um dos mais antigos e mais largamente disponíveis, não é muito seguro e vou deixar de lado. O segundo também é bem comum e disponível nativamente em diversos ambientes, como Windows e Android. O OpenVPN é ótimo mas depende da disponibilidade e da instalação do cliente OpenVPN para seu ambiente. Como é um serviço completo em si, o OpenVPN é mais fácil de implementar o servidor. Vou começar por este.

O OpenVPN é relativamente tranquilo de instalar e possui clientes para diversos ambientes, inclusive plataformas mobile. A parte mais complicada é a autenticação. Esta normalmente é feita através de certificados emitidos por uma CA, que criaremos durante o processo (não precisa comprar certificados).

Você vai precisar de um roteador com OpenWRT (utilizarei o Barrier Breaker - BB - mas o AA também funciona), com 8 MB de flash ou disco expandido via unidade USB. Você também precisará editar alguns arquivos pelo terminal (SSH/Telnet). Seria bom conhecer ao menos o básico do editor vi. A interface WEB não é suficiente.

Na versão Barrier Breaker, existe 3 opções de openvpn:
  • openvpn-nossl (sem criptografia)
  • openvpn-openssl
  • openvpn-polarssl

A primeira é mais enxuta mas não utiliza criptografia, o que normalmente é um requisito de um serviço VPN. Os dois seguintes são equivalentes, variando apenas a implementação de ssl que será utilizada. O polarssl é mais enxuto tanto em funcionalidades como em tamanho. É muito interessante para ambientes com restrição de recursos como o roteador e provavelmente terá tudo que você precisa. Contudo, se você já tem o libopenssl instalado por requisito de outro pacote, não vale a pena instalar o polarssl em paralelo. No meu caso, como tenho outros pacotes que já exigem o openssl, vou instalar o segundo pacote (em destaque).

Também é interessante instalar o openvpn-easy-rsa. Ele cria a infraestutura de uma CA de fácil gerenciamento. Você poderia utilizar o certificado de outra autorizade certificadora. Porém, considero uma CA dedicada ao VPN a solução mais atraente, tanto por simplificar o gerenciamento como para restringir problemas de segurança. Afinal, o comprometimento desta CA dedicada fornecerá ao atacante acesso ao roteador, que já seria previamente um requisito para poder comprometer a CA.

opkg update
opkg install openvpn-openssl openvpn-easy-rsa

Já adiantando, se o roteador fosse utilizado como um cliente OpenVPN, o pacote seria o mesmo (openvpn-{nossl,openssl,polarssl}). Só não seria necessário o openvpn-easy-rsa.

Antes de configurar o openvpn, vamos montar a CA. O easy-rsa trabalha apenas com scripts para fornecer toda a funcionalidade de uma CA necessária para o OpenVPN. Os parâmetros que você deve querer alterar estão em /etc/easy-rsa/vars. Você vai querer alterar estes campos:

# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="SanFrancisco"
export KEY_ORG="Fort-Funston"
export KEY_EMAIL="me@myhost.mydomain"
export KEY_OU="MyOrganizationalUnit"

Existem outros campos como tamanho de chave e expiração do certificado que você pode querer aumentar ou desativar:

export KEY_SIZE=2048
# In how many days should the root CA key expire?
export CA_EXPIRE=3650
# In how many days should certificates expire?
export KEY_EXPIRE=3650

Agora é só criar a CA:

# clean-all # opcional para primeira execução
# build-ca # (com o /etc/easy-rsa/vars configurado, só seguir)
# build-dh # vai demorar...

Não é necessário mas gosto de sempre gerar a lista de certificados revogados e configurá-la no OpenVPN. Caso precise revogar um certificado, tudo está pronto. Porém, a versão do easy-rsa do OpenVPN só gera o crl.pem ao revogar um certificado. Você pode gerá-lo manualmente com este comando:

# (. /etc/easy-rsa/vars;  CRL="/etc/easy-rsa/keys/crl.pem"; KEY_CN="" KEY_OU="" KEY_NAME="" $OPENSSL ca -gencrl -out "$CRL" -config "$KEY_CONFIG")

Agora podemos criar o certificado do servidor VPN e dos clientes:

# build-key-server server
# build-key cliente1
# build-key cliente2
# ...

Crie quantos clientes quiser e a qualquer tempo. Escolha nomes informativos como cliente-tablet-luiz. Quanto ao certificado do servidor, normalmente teremos apenas um durante toda a validade do certificado. Se trocar o nome de server para outra coisa, irá afetar os campos cert e key da configuração do OpenVPN.

Depois podemos configurar o servidor VPN. O arquivo de configuração fica em /etc/config/openvpn:


config 'openvpn' 'lan'

        option 'enable' '1'

        option 'port' '1194'

        option 'proto' 'udp'

        option 'dev' 'tun'
        option 'ca' '/etc/easy-rsa/keys/ca.crt'
        option 'cert' '/etc/easy-rsa/keys/server.crt'
        option 'key' '/etc/easy-rsa/keys/server.key'
        option 'dh' '/etc/easy-rsa/keys/dh2048.pem'
        option 'crl_verify' '/etc/easy-rsa/keys/crl.pem'
        option 'ifconfig_pool_persist' '/tmp/ipp.txt'
        option 'keepalive' '10 120'
        option 'comp_lzo' 'no'
        option 'persist_key' '1'
        option 'persist_tun' '1'
        option 'status' '/var/log/openvpn-status.log' 
        option 'verb' '3'
        option 'server' '192.168.2.0 255.255.255.0'
        option 'client_to_client' '1'
        list 'push' 'redirect-gateway def1'
        list 'push' 'dhcp-option DNS 192.168.1.1'
list 'push' 'route 192.168.1.0 255.255.255.0'

Os destaques tem as opções que você provavelmente quer alterar. A porta 1194 é a padrão e pode ser interessante trocá-la para evitar que uma varredura simples de rede achem seu OpenVPN. O crl_verify deve estar presente caso você tenha gerado o crl.pem que comentei anteriormente. Caso não deseje revogar certificados, remova-o. 

ifconfig_pool_persist permite que os endereços atribuídos a um cliente se mantenham na próxima conexão. Isto é equivalente ao arquivo leases do DHCP. Contudo, ao colocá-lo em /tmp, você irá perder este arquivo a cada reboot do roteador. Mude para /etc/openvpn/ipp.txt para manter mesmo após desligar o roteador ou remova a linha caso não queira manter os endereços.

A opção comp_lzo habilita a compactação do tráfego antes da criptografia. Isto poupa a quantidade de dados trafegados mas aumenta a carga sobre o roteador e também deixa a conexão um pouco mais lenta.

A opção server indica a faixa de endereços utilizada pelos clientes VPN. Ela não será a mesma da sua rede local. Procure escolher algo que não conflite com a rede local do servidor VPN e nem dos clientes (contudo algumas vezes não é possível conhecê-la previamente).

Os push finais são usados para enviar configurações ao cliente. As apresentadas criam a rota para seu cliente VPN falar com a LAN do servidor VPN. Caso contrário, ele somente irá ver o servidor VPN e não os clientes. Remova estas linhas se quer apenas acessar os serviço do roteador onde o servidor VPN está.

É hora de disparar o serviço:


/etc/init.d/openvpn start

Acompanhe pelo logread por possíveis erros. Isto é uma execução normal:

Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: OpenVPN 2.3.6 mips-openwrt-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [MH] [IPv6] built on Jan  6 2015
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: library versions: OpenSSL 1.0.1j 15 Oct 2014, LZO 2.08
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: Diffie-Hellman initialized with 2048 bit key
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: Socket Buffers: R=[163840->131072] S=[163840->131072]
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: TUN/TAP device tun0 opened
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: TUN/TAP TX queue length set to 100
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: /sbin/ifconfig tun0 192.168.xx.1 pointopoint 192.168.33.2 mtu 1500
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: /sbin/route add -net 192.168.xx.0 netmask 255.255.255.0 gw 192.168.xx.2
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: UDPv4 link local (bound): [undef]
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: UDPv4 link remote: [undef]
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: MULTI: multi_init called, r=256 v=256
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: IFCONFIG POOL: base=192.168.xx.4 size=62, ipv6=0
Tue Jan 20 16:07:20 2015 daemon.notice openvpn(server)[7991]: Initialization Sequence Completed

Já está funcionando? Sim, mas não para os clientes externos. Disparamos o serviço neste ponto para o OpenVPN criar o dispositivo tun0. Isto permite que seja utilizado o Luci (interface web) para configurar o firewall. Você pode visualizar esta interface pelo "ifconfig" ou "ip add show".

No firewall, abra a porta 1194 UDP ou a que você definiu anteriormente para receber conexões externas. Está em "Rede/Firewall/Regras de Tráfego/Abrir portas no roteado".

Neste momento, os clientes já podem conectar ao serviço. Todavia, ao conectarem, existirá um cabo virtual entre o cliente e o dispositivo tun0 do roteador (versão simplificada da topologia). O problema é que tun0 não pertence a nenhuma interface do OpenWRT (e consequentemente, a nenhuma zona do firewall). Por padrão, este tráfego será descartado.

Pela interface Luci, adicione uma nova interface (sugestão de nome: vpn) no OpenWRT em Rede/Interfaces. Configure-a para ser não gerenciada e escolha o dispositivo tun0. Nesta mesma interface, você já pode atribuir uma zona do firewall. Se os clientes VPN terão privilégios iguais aos da rede local, basta adicionar a interface vpn à zona lan. Se quiser algo diferenciado, crie uma nova zona e defina a vontade as regras de filtragem.

Está pronto. Seus clientes devem estar prontos para conectar ao seu novo serviço.
Para isto, eles vão precisar:
  • /etc/easy-rsa/keys/<cliente>.crt
  • /etc/easy-rsa/keys/<cliente>.key
  • /etc/easy-rsa/keys/ca.crt
  • O endereço internet do seu roteador (pode ser o FQDN de um DNS dinâmico)
  • Saber se está usando a compactação (lzo) ou não
Para alguns clientes você vai precisar de um profile xxx.opvn. Existem algumas alternativas para criá-lo. Uma seria fazê-lo manualmente. É um arquivo texto neste formato

client
remote-cert-tls server
remote endereco-internet porta
comp-lzo yes
<ca>
conteúdo de ca.crt
</ca>
<key>
conteúdo de <cliente>.key
</key>
<cert>
conteúdo de <cliente>.crt
</cert>

Retire o comp-lzo se não usar a compactação no servidor. Também existem ferramentas para Linux e Windows que geram o .ovpn. Alternativamente, use este script diretamente no roteador.

Caso a conexão de um cliente não funcione, observe os logs tanto do OpenWRT (logread) como do cliente OpenVPN. Um sniffer de rede escutando na interface externa também ajuda. Testes de clientes VPN vindos da rede local para o endereço IP do roteador externo não irão funcionar automaticamente pois ele irá responder com o endereço IP interno e o cliente recusará a resposta vinda de outro endereço. É um problema muito parecido ao apresentado em um post antigoSe for testar de dentro da sua rede, use o IP interno do roteador (por padrão, 192.168.1.1).

Mas eu queria ter um endereço de dentro da rede (LAN) e não em uma faixa diferente (VPN). Tem como? Sim. Neste caso, o modo do túnel deve ser tap e não tun. Além disto, a interface tap0 teria que ser adicionada a br-lan. Porém, isto faz com que todas as mensagens broadcast da LAN sejam enviadas pelo túnel. Normalmente isto resulta em um bom desperdício de rede. Outro problema é que alguns clientes não suportam tap, como o android, devido a limitação da API VpnService (reclamem com o Google).

No próximo post mostro como fazer o outro lado configurando um cliente OpenVPN no roteador.

Atualizando: Alguma dúvida? Criei um fórum para o blog e vou criar um tópico para cada novo artigo.

Até a próxima.