Peer-to-site WireGuard VPN entre MacOS e Ubuntu Linux

Como fechar um túnel VPN entre um servidor Ubuntu Linux rodando sobre instância EC2 na AWS e um cliente MacOS.

Peer-to-site WireGuard VPN entre MacOS e Ubuntu Linux
Photo by Johnson Martin / Unsplash

Se você está lendo esse post eu provavelmente não preciso te convencer a usar uma VPN. Seja qual for o seu motivo, privacidade, segurança ou qualquer outro, o projeto WireGuard tem grandes chances de ser a solução mais simples para você.

WireGuard® é uma VPN extremamente simples, porém rápida e moderna, que utiliza criptografia de última geração. Seu objetivo é ser mais rápido, mais simples, mais enxuto e mais útil que o IPsec, evitando ao mesmo tempo sua enorme dor de cabeça. Pretende ter um desempenho consideravelmente melhor que o OpenVPN.

Neste artigo vou demonstrar como fechar um túnel VPN entre um servidor Ubuntu Linux rodando sobre instância EC2 na AWS e um cliente MacOS. Mas o mesmo princípio e muito provavelmente as mesmas configurações se aplicam para distintos ambientes, como um servidor caseiro ou uma instância na Linode e um cliente Linux ou Windows®.

Motivação

Eu escolhi o WireGuard principalmente por ser uma solução mais simples para o meu problema, que é ter um IP fixo quando estou acessando a Internet a partir da minha conexão móvel 5G. É comum em diversos países que mesmo você estando em um local fixo, seu endereço IP fique oscilando entre um pequeno conjunto de endereços da sua operadora de telefonia móvel. Isso torna problemático o acesso a sites onde há restrição de IP.

No meu caso, eu estava em busca de uma solução que pudesse ser facilmente instalada no meu MacOS. Evitei soluções pagas que em geral custam em torno de U$5/mês, pois com esse valor é possível ter uma instância de máquina virtual em Clouds como AWS ou Linode e de quebra resolver outros problemas além de possuir uma VPN. Também evitei soluções menos triviais de se implementar, como OpenVPN e IPSec.

Com isso em mente e também minha grande familiaridade com Linux, decidi dar uma chance ao WireGuard, o que acabou me surpreendendo muito positivamente. Além de simples, o site oficial alega que o WireGuard é mais performático, moderno e enxuto do que outras soluções:

WireGuard technical whitepaper [1]

[1] https://www.wireguard.com/papers/wireguard.pdf

Configuração

É possível implementar o WireGuard para atender a diversos cenários:

Em qualquer um dos modos acima é possível escolher entre encaminhar todo o tráfego através do túnel VPN, o que o WireGuard chama de modo default gateway[4], ou limitar o encaminhamento para endereços ou intervalos IP específicos.

No meu caso, eu fiz a implementação peer-to-site combinado com default gateway. Dessa forma, quando estiver usando minha conexão 5G ou um WiFi em um local público, como em um evento ou em um café, todo o meu tráfego será enviado através da VPN.

[2] https://ubuntu.com/server/docs/wireguard-vpn-peer2site-introduction
[3] https://ubuntu.com/server/docs/wireguard-vpn-site2site
[4] https://ubuntu.com/server/docs/wireguard-vpn-defaultgw

Configuração inicial do servidor

No caso, tenho um servidor Ubuntu Linux rodando em uma instância AWS com IP público[5]:

  1. Instalação do pacote do WireGuard
sudo apt install wireguard

Este é um meta-pacote que por dependência irá instalar o wireguard-tools, único pacote realmente necessário, já que o módulo do WireGuard já está presente no kernel Linux das distribuições Linux mais modernas.

  1. Criação do par de chaves pública/privada:
cd /etc/wireguard
wg genkey > wg0.key
wg pubkey < wg0.key > wg0.pub
Se o conceito de par de chaves ou formalmente criptografia assimétrica[6] é novo para você, pense que o que a chave pública criptografa, somente seu par privado é capaz de descriptografar e vice-versa.
Dessa forma, se Bob quer se comunicar com Alice, é necessário que cada um tenha ser par de chaves e que as suas chaves públicas sejam enviadas de um para o outro. Com isso, quando Bob quer enviar uma mensagem secreta para Alice, ele usa a chave pública de Alice para criptografar a mensagem. E assim, somente Alice, com sua chave privada, é capaz de ler a mensagem que foi encriptada com sua chave pública. Dessa mesma forma, quando Alice quer enviar uma mensagem para Bob, ela deve usar a chave pública de Bob para encriptar a mensagem e com isso, somente Bob será capaz de reverter o processo, descriptografando a mensagem.

Não é necessário manter os arquivos wg0.key e wg0.pub, muito menos nomeá-los dessa forma. Mas acho uma boa prática, já que é possível ter diversas interfaces WireGuard no servidor, cada uma com seu próprio par de chaves e configurações específicas, conectando peers ou redes de forma isolada.

[5] https://docs.aws.amazon.com/pt_br/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html
[6] https://stakey.club/pt/uma-breve-nocao-de-criptografia/#criptografia-assim%C3%A9trica

Configuração do cliente

Instalei a versão mais recente do App do Wireguard[7] e após abrir o app fui em "Add Empty Tunnel":

É possível nomear a interface como quiser, eu preferi manter o mesmo nome da interface do servidor, wg0. O par de chaves no MacOS é gerado "automagicamente" pelo app. Acrescente as linhas faltando para que a configuração fique como a da figura abaixo:

Interface wg0 do cliente MacOS
  1. A Public key, que foi gerada automaticamente, deverá ser utilizada no bloco da seção [Peer] no arquivo wg0.conf que iremos criar mais adiante no servidor.
  2. Já a PrivateKey não deve ser utilizada em qualquer outro lugar que não aqui, na configuração do túnel do app do WireGuard no MacOS.
  3. Em PublicKey na seção [Peer], deve ser informado a chave pública gerada anteriormente no servidor, aquela que foi salva dentro do arquivo wg0.pub.

Demais configurações:

  • Address: endereço IP da interface wg0 do MacOS na VPN.
  • DNS: No mode default gateway o WireGuard remove os servidores DNS locais do sistema operacional. Por isso é necessário especificar pelo menos um endereço de DNS aqui.
  • MTU: evita problemas com jumbo packages. No meu caso, após estabelecer o túnel VPN eu não conseguia acessar sites https sem definir essa opção com algum valor menos que 1420.
  • AllowedIPs: quais endereços IP ou intervalos utilização a VPN, 0.0.0.0/0 significa todos os endereços IPv4.
  • Endpoint: Endereço IP público do servidor VPN e porta UDP onde o WireGuard estará escutando.
ℹ️
Assim como Bob e Alice devem ter cada um seu par de chaves e trocar suas chaves públicas, aqui estamos fazendo o mesmo para o servidor e o cliente. Quando o MacOS quer se comunicar com o servidor ele usa a chave pública do servidor. Quando o servidor quer se comunicar com o MacOS, ele usa a chave pública do MacOS. Para descriptografar essas mensagens, cada um usa sua própria chave privada, que deve ser mantida protegida no host onde foi gerada.

[7] https://apps.apple.com/us/app/wireguard/id1451685025

Finalizando a configuração no servidor

Agora que temos a chave pública do cliente MacOS, podemos finalizar as configurações do lado do servidor. O arquivo /etc/wireguard/wg0.conf deve ter o seguinte conteúdo:

[Interface]
PrivateKey = IGvLFCZfDq1UZDKAkZ+cB+2AZzSdO/Sgp+26Tl+NG0A=
ListenPort = 51820
MTU        = 1420
Address    = 10.10.11.10/24
PostUp     = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens5 -j MASQUERADE
PostDown   = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens5 -j MASQUERADE

[Peer]
# MacOS
PublicKey = 493MTFesLa1xis32ilUpn4nmQD5lKck9FaeqeMvCkVk=
AllowedIPs = 10.10.11.100/32

Na seção [Interface]:

  • Em PrivateKey temos a chave privada do servidor gerada anteriormente e armazenada em wg0.key.
  • ListenPort, define a porta UDP que o processo do WireGuard irá aguardar por comunicação do peer.
  • MTU, limita o tamanho máximo do pacote UDP. Eu tive problemas em estabelecer conexões https através do túnel VPN deixando esse valor ser calculado automaticamente.
  • Address, endereço IP da interface wg0 no servidor.
  • PostUp e PostDown, regras de firewall para liberar o tráfego de e para a interface wg0, além de fazer o mascaramento dos pacotes que saem pela interface ens5.

Na seção [Peer]:

  • Em PublicKey na seção [Peer] temos a chave pública do MacOS. Item 1 na figura "Interface wg0 do cliente MacOS".
  • AllowedIPs, endereço IP do cliente MacOS.

Com isso, já podemos levantar a interface wg0 no servidor através do systemd:

systemctl start wg-quick@wg0
⚠️
Não esqueça de liberar o tráfego UDP na porta 51820. Se você também usa uma instância EC2 na AWS, é necessário confirmar que o tráfego esteja liberado na Network ACL vinculada a Subnet onde a instância está rodando, no Security Group vinculado a instância e também no firewall local, UFW se você também estiver rodando Ubuntu.

Estabelecendo a conexão VPN

Com o servidor escutando na porta 51820/UDP, podemos estabelecer a conexão VPN a partir do cliente/MacOS:

Se tudo correu bem, você deve conseguir se comunicar com o servidor VPN e navegar na Internet através do túnel:

Tente acessar sites como https://ip-api.com/ ou https://ifconfig.me/ para confirmar que o seu IP de origem agora é o IP público do seu servidor e não mais da sua conexão móvel 5G ou do seu modem local.

💡
Caso tenha tido problemas, tente utilizar o ss para confirmar que o WireGuard está escutando na porta UDP: ss -nul |grep 51820
Ou o comando tcpdump para "sniffar" por pacotes relacionados ao túnel VPN:
- tcpdump -n -i ens5 port 51820
- tcpdump -n -i wg0