Como funciona o WireGuard: roteamento por chaves
Entenda por que AllowedIPs funciona como tabela de roteamento e lista de acesso, além do handshake Noise, da rotação de chaves e da leitura do wg0.conf.
Como o WireGuard funciona, em uma ideia
O WireGuard associa cada pacote a uma chave pública. O mecanismo tem um nome, encaminhamento por chaves criptográficas, e constitui todo o design: a linha AllowedIPs junto de um peer é a tabela de encaminhamento dos pacotes que saem da sua máquina e a lista de controlo de acesso dos pacotes que chegam desse peer. Uma definição, duas funções. Leia AllowedIPs dessa forma e todos os ficheiros de configuração do WireGuard se tornam compreensíveis.
Não existe uma tabela de sessões indexada por endereço IP nem uma base de dados de utilizadores. Um peer é uma chave pública mais o conjunto de endereços que essa chave pode utilizar. O handshake e os temporizadores existem para manter essa associação válida enquanto a rede subjacente muda. Se quiser ter um túnel funcional antes de abordar a teoria, crie um com uma VPN WireGuard autoalojada no seu próprio VPS e volte aqui quando uma linha de configuração causar dúvidas.
AllowedIPs é uma tabela de encaminhamento e uma lista de acesso
Comece pelo sentido de saída. O kernel encaminha um pacote para o dispositivo wg0 da forma habitual, através da tabela de encaminhamento principal. Em seguida, o WireGuard compara o endereço de destino desse pacote com uma tabela que contém os prefixos permitidos de todos os peers, usando primeiro o prefixo mais específico. Uma correspondência identifica um peer, que identifica uma chave pública, que identifica uma chave de sessão e um endpoint UDP. O pacote é cifrado para esse peer e enviado para ele.
Se o AllowedIPs de nenhum peer abranger o destino, nada é enviado, porque não existe uma chave com a qual enviar o pacote.
ping: sendmsg: Required key not availableEsse erro significa uma única coisa: o endereço que tentou alcançar não está listado em nenhum peer. Um erro diferente, ping: sendmsg: Destination address required, significa que foi encontrado um peer correspondente, mas o WireGuard não tem um endpoint para ele, porque nenhum foi configurado e nenhum foi aprendido ainda.
Agora, o sentido de entrada. Um pacote UDP chega à porta de escuta. O WireGuard encontra a sessão através do índice do recetor no cabeçalho, compara o contador com uma janela deslizante contra repetição e, em seguida, desencripta e autentica o payload. Só depois lê o pacote interno, e o endereço de origem desse pacote interno tem de estar dentro do AllowedIPs do peer que o enviou. Caso contrário, o pacote é descartado. Com a depuração dinâmica ativada, o kernel imprime o motivo numa linha como esta:
wg0: Packet has unallowed src IP (10.8.0.9) from peer 2 (203.0.113.10:51820)É por isso que um peer no lado do servidor recebe um /32. Um peer configurado com AllowedIPs = 10.8.0.2/32 pode enviar pacotes a partir de 10.8.0.2 e de nenhum outro endereço. Escreva 0.0.0.0/0 nesse campo e esse único cliente poderá injetar pacotes que afirmem ter qualquer endereço de origem dentro do seu túnel, incluindo o endereço de outro cliente.
Os prefixos sobrepostos são resolvidos pela especificidade, porque a pesquisa usa a correspondência do prefixo mais longo. Prefixos idênticos em dois peers comportam-se de forma diferente: a entrada passa para o peer que foi configurado por último, e o primeiro peer deixa de receber esse tráfego sem que seja impresso qualquer erro. wg show wg0 allowed-ips imprime a tabela que está efetivamente no kernel. É essa tabela que conta quando o ficheiro no disco e o estado em execução deixaram de estar sincronizados.
Leitura de um ficheiro de configuração tendo em conta o encaminhamento por chave criptográfica
No lado do servidor:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <server private key>
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32No lado do cliente:
[Interface]
Address = 10.8.0.2/32
PrivateKey = <laptop private key>
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25A mesma palavra-chave tem funções opostas nos dois lados. No cliente, indica «enviar todos os destinos para este peer». No servidor, indica «aceitar apenas este endereço deste peer». A assimetria está nos valores, não em nenhuma função.
Metade destas chaves não pertence ao protocolo. Address, DNS, MTU, PostUp e SaveConfig pertencem a wg-quick, o script de shell que ativa a interface. O kernel nunca as vê. wg-quick strip wg0 apresenta a configuração reduzida que a ferramenta wg carrega efetivamente. É a forma mais rápida de identificar essa separação.
O que o handshake realmente faz
O handshake do WireGuard é Noise_IKpsk2, do Noise Protocol Framework. A parte IK é a mais útil para um sysadmin: a chave pública estática do responder já é conhecida pelo iniciador, porque está definida em PublicKey no seu bloco [Peer], e o iniciador envia a própria chave pública estática dentro da primeira mensagem, cifrada. Por isso, não existe troca de certificados nem uma ida e volta adicional para validar a identidade. Um observador passivo não consegue saber qual chave está a iniciar a ligação, a menos que tenha a chave privada do responder.
O custo é uma ida e volta. A mensagem de iniciação tem 148 bytes, a resposta tem 92 bytes e os dados começam a fluir imediatamente depois. Cada lado gera um novo par de chaves efémeras Curve25519 por handshake, e as chaves de sessão resultam de uma cadeia de operações Diffie-Hellman que combina as chaves estáticas e efémeras. As chaves privadas efémeras são eliminadas depois, o que fornece sigilo de encaminhamento: alguém que grave o seu tráfego hoje e roube a chave privada do servidor no próximo ano continuará sem conseguir ler o tráfego gravado.
Uma iniciação de handshake transporta um timestamp TAI64N, e cada peer memoriza o maior timestamp recebido do outro. Assim, uma iniciação repetida é rejeitada. Os pacotes de dados transportam um contador de 64 bits usado como nonce, e o recetor mantém uma janela deslizante dos contadores vistos recentemente. Dessa forma, repetições e reordenações significativas são tratadas sem manter estado de ligação semelhante ao do TCP.
As chaves de sessão não duram muito, e os temporizadores estão integrados no código, em vez de serem configuráveis.
The data behind this chart
[
{
"label": "REKEY_TIMEOUT",
"seconds": 5,
"notes": "resend a handshake initiation that got no answer"
},
{
"label": "KEEPALIVE_TIMEOUT",
"seconds": 10,
"notes": "send a keepalive after receiving data and sending none back"
},
{
"label": "REKEY_ATTEMPT_TIME",
"seconds": 90,
"notes": "give up on the handshake and report the peer as down"
},
{
"label": "REKEY_AFTER_TIME",
"seconds": 120,
"notes": "sender begins a fresh handshake for a new session key"
},
{
"label": "REJECT_AFTER_TIME",
"seconds": 180,
"notes": "the old session key is refused and traffic stops"
}
]Estas são constantes da especificação do protocolo, não medições. O 5 delas controla todo o ciclo de vida da sessão. Depois de 120 segundos de utilização, o emissor inicia um novo handshake. Depois de 180 segundos, a chave antiga é recusada imediatamente, e o tráfego para até à conclusão de um novo handshake. Uma iniciação sem resposta é reenviada a cada 5 segundos e abandonada depois de 90 segundos. É por isso que wg show apresenta latest handshake como uma idade relativa e que um túnel saudável e com tráfego mantém essa idade baixa. Se a idade aumentar enquanto está a enviar tráfego ativamente, isso significa que os handshakes estão a falhar, não que o túnel está inativo.
Por que um peer não tem função de cliente nem de servidor
Ambas as extremidades executam o mesmo código e usam o mesmo formato de configuração. Não existe um modo de servidor. A assimetria percebida vem de Endpoint, e Endpoint é opcional.
Um peer com um endpoint configurado pode iniciar um handshake. Um peer sem endpoint aguarda e aprende o endereço e a porta do outro lado a partir do primeiro pacote autenticado corretamente. Esse endpoint aprendido é armazenado e atualizado sempre que chega um pacote válido de um endereço novo. É assim que funciona o roaming: um portátil que passa de uma rede wifi para uma rede móvel mantém o mesmo túnel, porque uma sessão é identificada pela chave e pelo índice, não pelo endereço IP. Nada é reconectado, porque nada esteve ligado no sentido TCP.
O mesmo mecanismo cria um facto importante: o peer com o endereço público mantém sempre o último IP público conhecido do outro lado, e wg show apresenta-o.
Primitivas fixas, sem negociação
Não existe uma lista de conjuntos de cifras no WireGuard. O WireGuard usa ChaCha20-Poly1305 para encriptação autenticada, Curve25519 para acordo de chaves, BLAKE2s para hashing e HKDF para derivação de chaves. Todas as implementações usam essas primitivas. Por isso, não existe uma fase de negociação para analisar nem uma possibilidade de downgrade para uma opção mais fraca. O compromisso é real: se uma dessas primitivas for quebrada, a correção exige uma nova versão de todo o protocolo e uma atualização nos dois lados, não uma alteração de configuração. Essa decisão elimina grande parte do código e dos modos de falha de um túnel baseado em TLS. É sobretudo essa a diferença analisada em WireGuard em comparação com OpenVPN.
Por que a porta não responde a um scanner
Todas as mensagens de handshake contêm um campo chamado mac1. Trata-se de um MAC (código de autenticação de mensagem) calculado sobre a mensagem com uma chave derivada da chave pública estática do responder. Um remetente que não conheça essa chave pública não consegue produzir um mac1 válido, e o destinatário descarta esse pacote sem enviar qualquer resposta. Não há erro, reset nem mensagem ICMP.
O resultado visível é uma varredura UDP que não recebe qualquer resposta.
sudo nmap -sU -p 51820 vpn.example.comO nmap apresenta open|filtered, que é a mesma resposta que fornece para uma porta cujos pacotes são descartados silenciosamente por uma firewall. A porta comporta-se da mesma forma esteja o WireGuard a escutar ou não, pelo menos para quem ainda não possui a sua chave pública.
Um segundo campo, mac2, trata da pressão causada por ataques de negação de serviço. Quando o destinatário está sob carga, responde a uma iniciação válida com uma resposta de cookie de 64 bytes associada ao endereço de origem do remetente e recusa executar operações dispendiosas de chave pública até o remetente devolver esse cookie. Isto confirma que o endereço de origem é real antes de qualquer consumo de CPU e só é ativado sob carga.
Por que 0.0.0.0/0 transforma um peer na rota predefinida
Como AllowedIPs é a tabela de encaminhamento, AllowedIPs = 0.0.0.0/0, ::/0 declara esse peer como destino para qualquer endereço. Essa é toda a configuração de túnel completo.
O encaminhamento que faz isto funcionar é mais interessante do que a própria linha. Uma rota predefinida simples através de wg0 entraria num ciclo, porque o pacote UDP cifrado que transporta o seu tráfego também tem de sair da máquina e corresponderia à sua própria rota predefinida. wg-quick evita isso com encaminhamento baseado em políticas. Marca os próprios pacotes de saída do WireGuard com um fwmark, coloca a rota predefinida do túnel numa tabela de encaminhamento separada e adiciona regras para que apenas o tráfego sem marca lhe chegue. Execute ip rule show para ver o resultado:
32764: from all lookup main suppress_prefixlength 0
32765: not from all fwmark 0xca6c lookup 51820
32766: from all lookup main0xca6c é 51820 em hexadecimal, e 51820 também é o número da tabela. A regra suppress_prefixlength 0 faz com que a tabela principal ignore a sua própria rota predefinida. Assim, as rotas específicas, como a da sua sub-rede local, continuam a ter prioridade, enquanto todo o restante tráfego passa para a tabela do túnel. Um túnel dividido não precisa de nada disto: uma lista mais específica, como AllowedIPs = 10.8.0.0/24, 10.20.0.0/16, transforma-se em rotas normais na tabela principal.
Um túnel completo não resolve, por si só, a resolução de nomes, porque o resolvedor que o cliente aprendeu com a rede local normalmente continua configurado e a respetiva rota é mais específica. Essa é uma tarefa separada, abordada em DNS que sai para fora de um túnel WireGuard.
Para que serve realmente PersistentKeepalive
O WireGuard não envia nada quando não há tráfego. Não há heartbeat, renovação de sessão nem qualquer outra atividade na rede. Esse silêncio ajuda a poupar bateria e também ajuda no caso do scanner descrito acima, mas interrompe uma configuração específica.
Um peer atrás de NAT (tradução de endereços de rede) ou de uma firewall com estado só pode ser alcançado a partir do exterior enquanto existir um mapeamento nesse dispositivo. Esse mapeamento foi criado por um pacote de saída. Os tempos de vida comuns dos mapeamentos UDP começam em cerca de 30 segundos. Quando o mapeamento expira, o middlebox descarta os pacotes provenientes do lado público. O túnel parece inativo até o peer atrás de NAT enviar algo. PersistentKeepalive = 25 envia um pacote autenticado vazio a cada 25 segundos. Esse intervalo é inferior ao tempo de vida comum mais curto, por isso o mapeamento permanece aberto.
Defina-o no peer atrás de NAT. Um servidor com um endereço público e uma porta UDP aberta não precisa dessa opção. Defini-la nesse servidor apenas acrescenta tráfego. Não a confunda com o keepalive automático. Este é enviado 10 segundos depois de um peer receber dados, quando não tem dados próprios para enviar de volta. Está sempre ativo e não pode ser configurado.
O encaminhamento da sua LAN pelo túnel não é uma funcionalidade do WireGuard
Suponha que o peer B esteja numa rede doméstica 192.168.50.0/24 e que o peer A deva conseguir aceder a ela. Dois sistemas diferentes têm de concordar, e apenas um deles é o WireGuard.
A parte do WireGuard: adicione 192.168.50.0/24 ao AllowedIPs de B em A. Isso faz com que A encaminhe o prefixo para B e aceite de B pacotes com esses endereços de origem. Sem isso, o encaminhamento por cryptokey não tem uma chave para o destino nem permissão para a origem.
A parte do kernel: em B, net.ipv4.ip_forward tem de ser 1, ou o kernel descarta todos os pacotes desencriptados que não sejam destinados ao próprio B. A cadeia forward da firewall de B tem de permitir o tráfego. Os hosts da LAN precisam de uma rota de retorno para 10.8.0.0/24, ou B tem de aplicar NAT de origem para que as respostas regressem através de B.
A função do WireGuard termina quando entrega o pacote desencriptado ao kernel. Tudo o que acontece depois é encaminhamento e filtragem normal do Linux. Por isso, esta falha aparece nos contadores de nft list ruleset ou em ip -s link show wg0, e não em wg show. Se preferir gerir os peers através de uma interface web, executar o wg-easy no Docker gera as entradas dos peers por si, embora as regras de encaminhamento continuem a pertencer ao host.
Por que o WireGuard fica no kernel
wg0 é um driver de dispositivo de rede. Os pacotes chegam até ele pela pilha de encaminhamento normal, são cifrados no contexto de softirq e saem por um socket UDP sem nunca passar pelo userspace. É daí que vem o débito, e é também por isso que o módulo se mantém em cerca de quatro mil linhas de código, pequeno o suficiente para ser revisto e integrado no Linux 5.6 em março de 2020. Ubuntu 24.04 e Debian 13 já o incluem, portanto só falta o pacote wireguard-tools.
Ser uma interface normal tem consequências práticas. tcpdump -ni wg0 mostra os pacotes internos em texto simples, enquanto tcpdump -ni eth0 udp port 51820 mostra os pacotes externos cifrados; comparar os dois indica imediatamente qual direção está com problemas. O netfilter e o traffic shaping tratam wg0 como qualquer outra ligação. Quando o módulo do kernel não está disponível, como em virtualização de contentores que partilha o kernel do host, wireguard-go implementa o mesmo protocolo no userspace através de um dispositivo TUN, com um custo real no débito porque cada pacote atravessa duas vezes o limite entre o kernel e o userspace.
O que o WireGuard não protege
O modelo de ameaças é deliberadamente restrito, e um protocolo tão discreto pode levar a expectativas incorretas. É importante deixar isto claro.
- Ele não oculta que está a usar WireGuard. As mensagens de handshake têm tamanhos fixos, o primeiro byte indica o tipo de mensagem e o transporte é UDP. A inspeção profunda de pacotes reconhece-o facilmente, e uma rede que bloqueie VPNs pode bloqueá-lo. A ofuscação foi excluída por decisão de projeto.
- Ele não oculta o volume nem o momento do tráfego. Os payloads recebem padding apenas até um limite de 16 bytes, por isso um observador continua a ver quando envia dados e, aproximadamente, quanto envia.
- Mantém o último endpoint conhecido. O peer com o endereço público armazena o IP público atual do outro lado, e
wg showapresenta-o. Juntamente com um endereço de túnel fixo na configuração, isto funciona como um identificador estável que acompanha um utilizador entre redes. No seu próprio VPS, isto não é um problema. É também por isso que os serviços comerciais adicionam uma camada acima do protocolo. - Autentica uma chave, não uma pessoa. Quem tiver o ficheiro da chave privada é o peer. Mantenha
/etc/wireguardcom o modo 700 e os ficheiros de chave com o modo 600. - Não existe lista de revogação nem expiração. O acesso termina quando elimina a entrada do peer de todos os servidores que a mantêm, e as chaves estáticas permanecem válidas até serem removidas.
Nada disto torna o WireGuard fraco. Torna-o pequeno, e esse é o objetivo: autentica e cifra, deixando a gestão de identidades e a atribuição de endereços para o que for construído acima dele. Uma camada de coordenação do tipo descrito em WireGuard comparado com Tailscale existe precisamente para preencher essa lacuna, usando o mesmo plano de dados que acabou de ler.
FAQ
O que é o encaminhamento por chave criptográfica no WireGuard?
O encaminhamento por chave criptográfica é a regra que associa cada pacote a uma chave pública. Cada entrada de peer contém uma lista de prefixos em AllowedIPs. No tráfego de saída, o WireGuard escolhe o peer comparando o destino do pacote com a lista de cada peer, começando pelo prefixo mais específico. Assim, a lista funciona como uma tabela de encaminhamento. No tráfego de entrada, depois de o pacote ser desencriptado e autenticado, o endereço de origem interno tem de estar dentro da lista desse mesmo peer. Caso contrário, o pacote é descartado. Assim, a lista funciona como uma lista de controlo de acesso. O WireGuard não tem uma configuração de encaminhamento separada nem uma firewall interna separada, porque essa lista única desempenha as duas funções.
Preciso de configurar PersistentKeepalive nos dois peers?
Não. Configure-o no lado que está atrás de NAT (tradução de endereços de rede) ou de uma firewall com estado, normalmente o cliente. O WireGuard não envia dados quando está inativo. Por isso, o mapeamento que permite ao outro lado alcançar esse peer expira, muitas vezes dentro de um minuto, e o túnel parece ficar indisponível numa direção. PersistentKeepalive = 25 envia um pacote autenticado vazio a cada 25 segundos e mantém o mapeamento aberto. Um peer com um endereço público e uma porta UDP aberta não precisa dessa configuração.
Por que motivo o ping através do túnel apresenta "Required key not available"?
Porque o endereço de destino não está incluído em nenhum AllowedIPs de peer. O encaminhamento por chave criptográfica não encontrou uma chave com a qual encriptar o pacote, e o kernel recusou enviá-lo. Execute wg show wg0 allowed-ips e compare o resultado com o endereço para o qual está a fazer ping. O erro semelhante Destination address required indica um problema diferente: foi encontrado um peer correspondente, mas o WireGuard não tem um endpoint para ele, porque nenhum foi configurado e ainda não chegou qualquer pacote autenticado desse peer.
Uma firewall pode detetar e bloquear o WireGuard?
Sim. O WireGuard autentica e encripta o tráfego e não tenta disfarçar-se. As mensagens de handshake têm 148 e 92 bytes, o primeiro byte de cada mensagem identifica o respetivo tipo e o transporte usa UDP. Por isso, a inspeção profunda de pacotes identifica o protocolo sem dificuldade. As redes que bloqueiam UDP ou identificam protocolos por fingerprinting irão bloqueá-lo. Ocultar o túnel significa encapsulá-lo noutra coisa. Isso requer uma ferramenta separada, não uma configuração do WireGuard.