Homelab WireGuard VPN
WireGuard é um protocolo VPN moderno e rápido. É a escolha certa para acesso remoto a uma rede doméstica — mais simples de configurar que OpenVPN e mais rápido que a maioria das alternativas.
Todos os exemplos de configuração mostram setups comuns. Revise cada comando — especialmente as regras de encaminhamento iptables e permissões de arquivos de chave — antes de aplicá-los ao seu sistema, e faça alterações em uma janela de manutenção.
Quando Usar
- Configurando servidor WireGuard em um Raspberry Pi, host Linux, pfSense ou roteador
- Gerando pares de chaves WireGuard e escrevendo arquivos de configuração de peers
- Configurando acesso remoto a partir de um telefone ou laptop para uma rede doméstica
- Explicando split tunneling (rotear apenas tráfego doméstico) vs full tunnel (rotear todo o tráfego)
- Solucionando conexões WireGuard que não sobem
- Automatizando geração de configuração de peers para múltiplos clientes
Como o WireGuard Funciona
Seu telefone (cliente WireGuard)
│
│ Túnel UDP criptografado (porta 51820)
│
Seu roteador doméstico (servidor WireGuard — precisa de IP público ou DDNS)
│
Sua rede doméstica (192.168.1.0/24, NAS, Pi, etc.)
Cada dispositivo tem um par de chaves (chave pública + privada).
O servidor conhece a chave pública de cada cliente.
O cliente conhece a chave pública do servidor + endpoint (IP:porta).
O tráfego é criptografado ponto a ponto sem servidor central ou autoridade certificadora.
Configuração do Servidor (Linux)
# Instalar WireGuard
sudo apt update && sudo apt install wireguard -y
# Gerar par de chaves do servidor — criar arquivos com permissões privadas desde o início
sudo mkdir -p /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key'
# Escrever configuração do servidor — substituir pelo valor real da chave privada
# Não armazenar chaves privadas em controle de versão ou compartilhá-las
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24 # Sub-rede VPN — servidor recebe .1
ListenPort = 51820
PrivateKey = <cole_a_chave_privada_do_servidor_aqui>
# Regras de encaminhamento scoped: permitir tráfego VPN entrada/saída, não um FORWARD ACCEPT genérico
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Telefone — substituir pela chave pública real do telefone
PublicKey = <chave_publica_do_telefone>
AllowedIPs = 10.8.0.2/32
[Peer]
# Laptop — substituir pela chave pública real do laptop
PublicKey = <chave_publica_do_laptop>
AllowedIPs = 10.8.0.3/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf
# Substituir eth0 pelo nome real da sua interface de saída
# Verificar com: ip route show default
# Habilitar encaminhamento IP (necessário para rotear tráfego através do servidor)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
# Iniciar WireGuard e habilitar na inicialização
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
Configuração do Cliente
# Gerar um par de chaves único para cada dispositivo cliente
# Executar no cliente, ou no servidor e transferir a chave privada de forma segura — nunca em texto puro
umask 077
wg genkey | tee phone_private.key | wg pubkey > phone_public.key
# Arquivo de configuração do cliente (phone_wg0.conf):
[Interface]
PrivateKey = <chave_privada_do_telefone>
Address = 10.8.0.2/32
DNS = 192.168.1.2 # Opcional: usar Pi-hole para DNS através do túnel
[Peer]
PublicKey = <chave_publica_do_servidor>
Endpoint = your-home-ip.ddns.net:51820 # Seu IP público ou hostname DDNS
AllowedIPs = 192.168.1.0/24 # Split tunnel: apenas tráfego da rede doméstica
# AllowedIPs = 0.0.0.0/0, ::/0 # Full tunnel: todo o tráfego através da VPN
PersistentKeepalive = 25 # Manter buraco NAT aberto (necessário para clientes móveis)
Split Tunnel vs Full Tunnel
# Split tunnel: AllowedIPs = 192.168.1.0/24
Apenas o tráfego destinado à sua rede doméstica passa pela VPN.
O tráfego da Internet (YouTube, Spotify) vai diretamente — melhor performance em dispositivos móveis.
Melhor para: "Só quero acessar meu NAS e Pi de qualquer lugar."
# Full tunnel: AllowedIPs = 0.0.0.0/0, ::/0
TODO o tráfego passa pela sua conexão de Internet doméstica.
Útil para: aproveitar bloqueio de anúncios do Pi-hole/DNS doméstico.
Desvantagem: sua velocidade de upload doméstica se torna o gargalo em todos os lugares.
# Split tunnel multi-sub-rede (caso de uso mais comum em homelab):
AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
Roteia todas as suas VLANs através do túnel; Internet permanece direta.
Geração de Chaves e Gerenciamento de Peers
import subprocess
def generate_keypair() -> tuple[str, str]:
"""Gera um par de chaves WireGuard. Retorna (chave_privada, chave_publica)."""
private = subprocess.check_output(["wg", "genkey"]).decode().strip()
public = subprocess.run(
["wg", "pubkey"], input=private.encode(), capture_output=True
).stdout.decode().strip()
return private, public
def generate_preshared_key() -> str:
return subprocess.check_output(["wg", "genpsk"]).decode().strip()
def build_client_config(
client_private_key: str,
client_vpn_ip: str, # ex: "10.8.0.3"
server_public_key: str,
server_endpoint: str, # ex: "home.example.com:51820"
allowed_ips: str = "192.168.1.0/24",
dns: str = "",
) -> str:
dns_line = f"DNS = {dns}\n" if dns else ""
return f"""[Interface]
PrivateKey = {client_private_key}
Address = {client_vpn_ip}/32
{dns_line}
[Peer]
PublicKey = {server_public_key}
Endpoint = {server_endpoint}
AllowedIPs = {allowed_ips}
PersistentKeepalive = 25
"""
def build_server_peer_block(
client_public_key: str,
client_vpn_ip: str,
comment: str = "",
) -> str:
comment_line = f"# {comment}\n" if comment else ""
return f"""
{comment_line}[Peer]
PublicKey = {client_public_key}
AllowedIPs = {client_vpn_ip}/32
"""
Mantenha chaves privadas fora do controle de versão. Se usar este script, escreva o material de chave em arquivos com modo 600 e nunca o registre em log ou imprima.
WireGuard no pfSense / OPNsense
# pfSense: VPN → WireGuard → Adicionar Túnel
Interface Keys: Gerar (cria par de chaves automaticamente)
Listen Port: 51820
Interface Address: 10.8.0.1/24
# Adicionar Peer (um por cliente):
Public Key: <chave pública do cliente>
Allowed IPs: 10.8.0.2/32
# Atribuir a interface WireGuard:
Interfaces → Assignments → Adicionar (selecionar wg0)
Habilitar interface, sem IP necessário (definido na configuração do túnel)
# Regras de firewall:
WAN → Permitir UDP porta 51820 entrada (para clientes alcançarem o servidor)
Interface WireGuard → Permitir tráfego para redes LAN que deseja acessíveis
DDNS (DNS Dinâmico) para Servidores Domésticos
A maioria das conexões de Internet domésticas tem IP dinâmico. Use DDNS para que seu endpoint VPN permaneça acessível após uma mudança de IP.
# Opção 1: Cloudflare DDNS — armazenar credenciais em um arquivo de segredos, não inline
# Entrada docker-compose usando um arquivo env:
ddns-updater:
image: qmcgaw/ddns-updater
env_file: ./ddns.env # armazenar zone_id e token aqui, não no compose
restart: unless-stopped
# ddns.env (chmod 600, não commitado no git):
# SETTINGS_CLOUDFLARE_ZONE_ID=your_zone_id
# SETTINGS_CLOUDFLARE_TOKEN=your_api_token
# Opção 2: DuckDNS (gratuito, simples)
Cadastre-se em duckdns.org → obtenha um token e subdomínio (myhome.duckdns.org)
Armazene token em /etc/ddns.env (modo 600), depois use um pequeno script de propriedade do root:
# /usr/local/bin/update-duckdns
#!/bin/sh
set -eu
. /etc/ddns.env
curl --fail --silent --show-error --max-time 10 \
--get "https://www.duckdns.org/update" \
--data-urlencode "domains=myhome" \
--data-urlencode "token=${DUCKDNS_TOKEN}" \
--data-urlencode "ip="
# Cron job:
*/5 * * * * /usr/local/bin/update-duckdns >/dev/null 2>&1
Solução de Problemas
# Verificar status do WireGuard e último handshake
sudo wg show
# Se "latest handshake" for nunca ou muito antigo, o túnel não está conectado.
# Verificar:
# 1. A porta UDP 51820 está aberta no roteador/firewall?
sudo ufw status # ou verificar regras de firewall pfSense/UniFi
# 2. A chave pública do servidor está correta na configuração do cliente?
sudo wg show wg0 public-key # Comparar com o que está na configuração do cliente
# 3. O encaminhamento IP está habilitado no servidor?
cat /proc/sys/net/ipv4/ip_forward # Deve ser 1
# 4. O AllowedIPs do cliente cobre o IP que você está tentando alcançar?
# Se AllowedIPs = 192.168.1.0/24 e você está tentando alcançar 192.168.3.5, não vai rotear.
# Verificar logs do kernel para erros do WireGuard
dmesg | grep wireguard
# Reiniciar WireGuard
sudo wg-quick down wg0 && sudo wg-quick up wg0
Anti-Padrões
# RUIM: Armazenar chaves privadas em controle de versão ou compartilhá-las
# Chaves privadas são equivalentes a senhas — nunca as faça commit no git
# RUIM: Usar AllowedIPs = 0.0.0.0/0 em dispositivos móveis sem considerar o impacto
# Full tunnel roteia todo o tráfego móvel através do seu upload doméstico — geralmente lento
# RUIM: Não definir PersistentKeepalive em clientes móveis
# Clientes móveis atrás de NAT derrubam túneis ociosos sem isso
# RUIM: Abrir porta 51820 no firewall mas esquecer o encaminhamento IP no servidor
# Túnel conecta mas nenhum tráfego roteia — confuso de depurar
# RUIM: Compartilhar um par de chaves entre múltiplos dispositivos cliente
# Cada dispositivo deve ter seu próprio par de chaves único — chaves compartilhadas quebram o modelo de segurança
# RUIM: Usar uma regra iptables "FORWARD ACCEPT" genérica
# Escopo as regras de encaminhamento para a interface wg0 e direção apenas
Práticas Recomendadas
- Gere um par de chaves único por dispositivo cliente — nunca reutilize chaves
- Use split tunneling (
AllowedIPs = <sub-redes domésticas>) para dispositivos móveis - Defina
PersistentKeepalive = 25em todos os clientes móveis - Use DDNS se seu ISP atribuir IP dinâmico; armazene credenciais em arquivos env, não inline
- Use regras de encaminhamento iptables scoped (entrada apenas em wg0) em vez de um FORWARD ACCEPT genérico
- Adicione o IP do Pi-hole como
DNS =nas configurações do cliente para obter bloqueio de anúncios pela VPN - Rode o par de chaves do servidor periodicamente e atualize todas as configurações do cliente
Skills Relacionadas
- homelab-network-setup
- homelab-vlan-segmentation
- homelab-pihole-dns