Homelab WireGuard VPN
WireGuard é um protocolo VPN rápido e moderno. É a escolha certa para acesso remoto a uma rede doméstica — mais simples de configurar que o 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 as permissões dos arquivos de chave — antes de aplicá-los ao seu sistema e faça as alterações em uma janela de manutenção.
Quando Usar
- Configurar servidor WireGuard em um Raspberry Pi, host Linux, pfSense ou roteador
- Gerar pares de chaves WireGuard e escrever arquivos de configuração de peers
- Configurar acesso remoto de um telefone ou laptop para uma rede doméstica
- Explicar split tunneling (rotear apenas tráfego doméstico) vs. full tunnel (rotear todo o tráfego)
- Solucionar problemas de conexões WireGuard que não sobem
- Automatizar a 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 residencial (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 ponta a ponta 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 — crie 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 — substitua pelo valor real da chave privada
# Não armazene chaves privadas em controle de versão nem as compartilhe
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 escopo: permitir tráfego VPN in/out, 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 — substitua pela chave pública real do telefone
PublicKey = <chave_publica_do_telefone>
AllowedIPs = 10.8.0.2/32
[Peer]
# Laptop — substitua 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
# Substitua eth0 pelo nome real da sua interface de saída
# Verifique 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
# Execute no cliente, ou no servidor e transfira 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: use Pi-hole para DNS através do túnel
[Peer]
PublicKey = <chave_publica_do_servidor>
Endpoint = seu-ip-casa.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 desempenho no celular.
Melhor para: "Eu 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: usar o DNS/bloqueio de anúncios do Pi-hole em casa.
Desvantagem: a 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; a 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 as chaves privadas fora do controle de versão. Se usar este script, escreva o material das chaves em arquivos com modo 600 e nunca registre ou imprima.
WireGuard no pfSense / OPNsense
# pfSense: VPN → WireGuard → Adicionar Túnel
Chaves da Interface: Gerar (cria o par de chaves automaticamente)
Porta de Escuta: 51820
Endereço da Interface: 10.8.0.1/24
# Adicionar Peer (um por cliente):
Chave Pública: <chave pública do cliente>
IPs Permitidos: 10.8.0.2/32
# Atribuir a interface WireGuard:
Interfaces → Atribuições → Adicionar (selecione wg0)
Habilitar interface, sem IP necessário (definido na configuração do túnel)
# Regras de Firewall:
WAN → Permitir UDP porta 51820 inbound (para clientes alcançarem o servidor)
Interface WireGuard → Permitir tráfego para as redes LAN que deseja acessar
DDNS (DNS Dinâmico) para Servidores Domésticos
A maioria das conexões de internet residenciais 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 — armazene credenciais em um arquivo de secrets, não inline
# Entrada docker-compose usando um arquivo env:
ddns-updater:
image: qmcgaw/ddns-updater
env_file: ./ddns.env # armazene 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=seu_zone_id
# SETTINGS_CLOUDFLARE_TOKEN=seu_api_token
# Opção 2: DuckDNS (grátis, simples)
Cadastre-se em duckdns.org → obtenha um token e subdomínio (minhacasa.duckdns.org)
Armazene token em /etc/ddns.env (modo 600), depois use um pequeno script owned by 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=minhacasa" \
--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" nunca aparece 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 verifique 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 # Compare 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 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 commite 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 upload doméstico — geralmente lento
# RUIM: Não definir PersistentKeepalive em clientes móveis
# Clientes móveis atrás de NAT perdem túneis ociosos sem ele
# RUIM: Abrir porta 51820 no firewall mas esquecer o encaminhamento IP no servidor
# O túnel conecta mas nenhum tráfego roteia — confuso de depurar
# RUIM: Compartilhar um par de chaves entre múltiplos dispositivos clientes
# 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
Melhores Práticas
- 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 um IP dinâmico; armazene credenciais em arquivos env, não inline
- Use regras de encaminhamento iptables escopo (inbound apenas no 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 via VPN - Rotacione 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