Communitygithub.com

homelab-wireguard-vpn

Configuración del servidor WireGuard VPN, configuración de pares, generación de claves, enrutamiento de túnel dividido vs completo y acceso remoto a una red doméstica desde clientes móviles y portátiles. Útil al configurar WireGuard para acceso remoto a una red doméstica o al decidir entre túnel dividido y completo.

¿Qué es homelab-wireguard-vpn?

WireGuard es un protocolo VPN moderno, rápido y sencillo, ideal para acceder de forma remota a una red doméstica. Esta habilidad proporciona una guía completa sobre la instalación y configuración de un servidor WireGuard en Linux (incluyendo Raspberry Pi, pfSense, etc.), la generación de pares de claves y la creación de archivos de configuración para clientes (teléfonos, portátiles). Explica en detalle las diferencias entre el túnel dividido (enrutar solo el tráfico de la red doméstica) y el túnel completo (enrutar todo el tráfico), ayudando al usuario a elegir la opción adecuada según sus necesidades. Además, incluye ejemplos prácticos de configuración en pfSense/OPNsense, scripts de automatización en Python para gestionar claves y pares, consejos para solucionar problemas comunes (como comprobar la fecha del último handshake o las reglas de firewall) y buenas prácticas de seguridad, como no almacenar claves privadas en repositorios de control de versiones y rotar las claves periódicamente.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/homelab-wireguard-vpn

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Homelab WireGuard VPN

WireGuard es un protocolo VPN moderno y rápido. Es la opción adecuada para el acceso remoto a una red doméstica: más sencillo de configurar que OpenVPN y más rápido que la mayoría de las alternativas.

Todos los ejemplos de configuración muestran configuraciones comunes. Revise cada comando, especialmente las reglas de reenvío de iptables y los permisos de los archivos de claves, antes de aplicarlos a su sistema y realice los cambios en una ventana de mantenimiento.

Cuándo Usar

  • Configuración de un servidor WireGuard en una Raspberry Pi, un host Linux, pfSense o un router
  • Generación de pares de claves WireGuard y escritura de archivos de configuración de pares
  • Configuración de acceso remoto desde un teléfono o portátil a una red doméstica
  • Explicación del túnel dividido (enrutar solo tráfico doméstico) vs túnel completo (enrutar todo el tráfico)
  • Solución de problemas de conexiones WireGuard que no se establecen
  • Automatización de la generación de configuración de pares para múltiples clientes

Cómo Funciona WireGuard

Tu teléfono (cliente WireGuard)
    │
    │  Túnel UDP cifrado (puerto 51820)
    │
Tu router doméstico (servidor WireGuard — necesita una IP pública o DDNS)
    │
    Tu red doméstica (192.168.1.0/24, NAS, Pi, etc.)

Cada dispositivo tiene un par de claves (clave pública + privada).
El servidor conoce la clave pública de cada cliente.
El cliente conoce la clave pública del servidor + endpoint (IP:puerto).
El tráfico se cifra de extremo a extremo sin servidor central ni autoridad certificadora.

Configuración del Servidor (Linux)

# Instalar WireGuard
sudo apt update && sudo apt install wireguard -y

# Generar par de claves del servidor — crear archivos con permisos privados desde el inicio
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'

# Escribir configuración del servidor — sustituir el valor real de la clave privada
# No almacene claves privadas en control de versiones ni las comparta
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24              # Subred VPN — el servidor obtiene .1
ListenPort = 51820
PrivateKey = <pegar_clave_privada_servidor_aqui>

# Reglas de reenvío con ámbito: permitir tráfico VPN dentro/fuera, no un 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]
# Teléfono — reemplazar con la clave pública real del teléfono
PublicKey = <clave_publica_telefono>
AllowedIPs = 10.8.0.2/32

[Peer]
# Portátil — reemplazar con la clave pública real del portátil
PublicKey = <clave_publica_portatil>
AllowedIPs = 10.8.0.3/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf

# Reemplazar eth0 con el nombre real de su interfaz de salida
# Verificar con: ip route show default

# Habilitar reenvío de IP (requerido para enrutar tráfico a través del servidor)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

# Iniciar WireGuard y habilitar al arranque
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0

Configuración del Cliente

# Generar un par de claves único para cada dispositivo cliente
# Ejecutar en el cliente, o en el servidor y transferir la clave privada de forma segura — nunca en texto plano
umask 077
wg genkey | tee phone_private.key | wg pubkey > phone_public.key

# Archivo de configuración del cliente (phone_wg0.conf):
[Interface]
PrivateKey = <clave_privada_telefono>
Address = 10.8.0.2/32
DNS = 192.168.1.2                  # Opcional: usar Pi-hole para DNS a través del túnel

[Peer]
PublicKey = <clave_publica_servidor>
Endpoint = tu-ip-casa.ddns.net:51820  # Tu IP pública o nombre de host DDNS
AllowedIPs = 192.168.1.0/24            # Túnel dividido: solo tráfico de red doméstica
# AllowedIPs = 0.0.0.0/0, ::/0        # Túnel completo: todo el tráfico a través de VPN

PersistentKeepalive = 25              # Mantener agujero NAT abierto (requerido para clientes móviles)

Túnel Dividido vs Túnel Completo

# Túnel dividido: AllowedIPs = 192.168.1.0/24
  Solo el tráfico destinado a tu red doméstica pasa por la VPN.
  El tráfico de Internet (YouTube, Spotify) va directamente — mejor rendimiento en móvil.
  Mejor para: "Solo quiero acceder a mi NAS y Pi desde cualquier lugar."

# Túnel completo: AllowedIPs = 0.0.0.0/0, ::/0
  TODO el tráfico pasa por tu conexión a Internet doméstica.
  Útil para: aprovechar el DNS doméstico/bloqueo de anuncios de Pi-hole.
  Inconveniente: la velocidad de subida de tu casa se convierte en el cuello de botella en todas partes.

# Túnel dividido de varias subredes (caso de uso más común en homelab):
  AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
  Enruta todas tus VLANs a través del túnel; el internet permanece directo.

Generación de Claves y Gestión de Pares

import subprocess

def generate_keypair() -> tuple[str, str]:
    """Genera un par de claves WireGuard. Devuelve (clave_privada, clave_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,       # ej. "10.8.0.3"
    server_public_key: str,
    server_endpoint: str,     # ej. "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
"""

Mantenga las claves privadas fuera del control de versiones. Si usa este script, escriba el material de las claves en archivos con modo 600 y nunca lo registre ni lo imprima.

WireGuard en pfSense / OPNsense

# pfSense: VPN → WireGuard → Añadir Túnel
  Claves de Interfaz: Generar (crea el par de claves automáticamente)
  Puerto de Escucha: 51820
  Dirección de Interfaz: 10.8.0.1/24

# Añadir Par (uno por cliente):
  Clave Pública: <clave pública del cliente>
  IPs Permitidas: 10.8.0.2/32

# Asignar la interfaz WireGuard:
  Interfaces → Asignaciones → Añadir (seleccionar wg0)
  Habilitar interfaz, no se necesita IP (se establece en la configuración del túnel)

# Reglas de firewall:
  WAN → Permitir tráfico UDP entrante en puerto 51820 (para que los clientes puedan alcanzar el servidor)
  Interfaz WireGuard → Permitir tráfico hacia las redes LAN que desee accesibles

DDNS (DNS Dinámico) para Servidores Domésticos

La mayoría de las conexiones a Internet domésticas tienen una IP dinámica. Use DDNS para que su endpoint VPN permanezca accesible después de un cambio de IP.

# Opción 1: Cloudflare DDNS — almacenar credenciales en un archivo de secretos, no en línea
# Entrada de docker-compose usando un archivo env:
  ddns-updater:
    image: qmcgaw/ddns-updater
    env_file: ./ddns.env   # almacenar zone_id y token aquí, no en compose
    restart: unless-stopped

# ddns.env (chmod 600, no comprometido en git):
# SETTINGS_CLOUDFLARE_ZONE_ID=tu_zone_id
# SETTINGS_CLOUDFLARE_TOKEN=tu_api_token

# Opción 2: DuckDNS (gratuito, simple)
  Registrarse en duckdns.org → obtener un token y subdominio (micasa.duckdns.org)
  Almacenar token en /etc/ddns.env (modo 600), luego usar un pequeño script propiedad de 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=micasa" \
    --data-urlencode "token=${DUCKDNS_TOKEN}" \
    --data-urlencode "ip="

  # Tarea cron:
  */5 * * * * /usr/local/bin/update-duckdns >/dev/null 2>&1

Solución de Problemas

# Verificar el estado de WireGuard y el último handshake
sudo wg show

# Si "latest handshake" es nunca o muy antiguo, el túnel no está conectado.
# Verificar:
# 1. ¿Está abierto el puerto UDP 51820 en el router/firewall?
sudo ufw status  # o verificar reglas de firewall en pfSense/UniFi

# 2. ¿Es correcta la clave pública del servidor en la configuración del cliente?
sudo wg show wg0 public-key   # Comparar con lo que está en la configuración del cliente

# 3. ¿Está habilitado el reenvío de IP en el servidor?
cat /proc/sys/net/ipv4/ip_forward  # Debería ser 1

# 4. ¿Cubre AllowedIPs del cliente la IP a la que intenta acceder?
# Si AllowedIPs = 192.168.1.0/24 e intenta acceder a 192.168.3.5, no se enrutará.

# Verificar registros del kernel para errores de WireGuard
dmesg | grep wireguard

# Reiniciar WireGuard
sudo wg-quick down wg0 && sudo wg-quick up wg0

Anti-Patrones

# MALO: Almacenar claves privadas en control de versiones o compartirlas
# Las claves privadas son equivalentes a contraseñas — nunca las comprometa en git

# MALO: Usar AllowedIPs = 0.0.0.0/0 en móvil sin considerar el impacto
# El túnel completo enruta todo el tráfico móvil a través de tu subida doméstica — generalmente lento

# MALO: No establecer PersistentKeepalive en clientes móviles
# Los clientes móviles detrás de NAT pierden túneles inactivos sin esto

# MALO: Abrir el puerto 51820 en el firewall pero olvidar el reenvío de IP en el servidor
# El túnel se conecta pero no se enruta tráfico — confuso de depurar

# MALO: Compartir un par de claves entre múltiples dispositivos cliente
# Cada dispositivo debe tener su propio par de claves único — las claves compartidas rompen el modelo de seguridad

# MALO: Usar una regla iptables "FORWARD ACCEPT" amplia
# Limitar las reglas de reenvío a la interfaz wg0 y dirección solamente

Mejores Prácticas

  • Generar un par de claves único por dispositivo cliente — nunca reutilizar claves
  • Usar túnel dividido (AllowedIPs = <subredes domésticas>) para móvil
  • Establecer PersistentKeepalive = 25 en todos los clientes móviles
  • Usar DDNS si su ISP asigna una IP dinámica; almacenar credenciales en archivos env, no en línea
  • Usar reglas de reenvío iptables con ámbito (solo entrante en wg0) en lugar de un FORWARD ACCEPT genérico
  • Añadir la IP de Pi-hole como DNS = en las configuraciones del cliente para obtener bloqueo de anuncios a través de la VPN
  • Rotar el par de claves del servidor periódicamente y actualizar todas las configuraciones de los clientes

Habilidades Relacionadas

  • homelab-network-setup
  • homelab-vlan-segmentation
  • homelab-pihole-dns

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados