Contexto HAaaS: Esta VM actúa como router interno con roles de DHCP y DNS para la red de clientes (vmbr1). La WAN conecta con la red interna de Proxmox (vmbr0) que tiene salida a GCP/internet.
Internet (GCP)
│
ens4 (10.132.0.7) — IP real GCP
│
Proxmox Host
│
vmbr0 (192.168.100.1/24) ←── WAN OpenWRT (eth0 → 192.168.100.2)
│
OpenWRT VM
│
vmbr1 (eth1 → br-lan → 10.10.10.2/24) ←── LAN clientes
│
VMs de clientes (Home Assistant, etc.)
vmbr0 configurado como red interna (192.168.100.0/24)vmbr1 configurado como red interna (10.10.10.0/24)ens4Conectarse al host Proxmox por SSH y ejecutar:
cd /tmp
wget https://downloads.openwrt.org/releases/23.05.3/targets/x86/64/openwrt-23.05.3-x86-64-generic-ext4-combined.img.gz
gunzip openwrt-23.05.3-x86-64-generic-ext4-combined.img.gz
Se usa la imagen x86-64 ext4 combined porque es la más adecuada para virtualización. No requiere instalador.
qm create 200 \
--name openwrt \
--memory 512 \
--cores 1 \
--net0 virtio,bridge=vmbr0 \
--net1 virtio,bridge=vmbr1 \
--ostype l26 \
--serial0 socket \
--vga serial0
| Parámetro | Valor | Descripción |
|---|---|---|
| VMID | 200 |
Cambiar si está en uso |
| Memory | 512 MB |
Suficiente para DHCP/DNS |
| net0 | vmbr0 |
WAN → red interna Proxmox |
| net1 | vmbr1 |
LAN → red de clientes |
qm importdisk 200 /tmp/openwrt-23.05.3-x86-64-generic-ext4-combined.img local-lvm
El disco se importa como ide0, no como scsi0. Hay que ajustar el boot en consecuencia.
Verificar que el disco quedó bien:
qm config 200
La salida debe mostrar ide0: ... con el disco. Ajustar el boot:
qm set 200 --boot order=ide0
qm start 200
Acceder a la consola desde la web UI de Proxmox (Console → NoVNC), no por terminal serial (el serial puede no responder hasta tener la red configurada).
Por defecto OpenWRT asigna eth0 a LAN y eth1 a WAN. En Proxmox, net0=vmbr0 (WAN) se mapea a eth0 y net1=vmbr1 (LAN) a eth1, por lo que hay que reconfigurar.
Ejecutar en la consola de OpenWRT:
# Mover eth1 al bridge de LAN (en vez de eth0)
uci set network.@device[0].ports='eth1'
# WAN en eth0 (vmbr0)
uci set network.wan.proto='static'
uci set network.wan.ipaddr='192.168.100.2'
uci set network.wan.netmask='255.255.255.0'
uci set network.wan.gateway='192.168.100.1'
uci set network.wan.dns='8.8.8.8'
# LAN en eth1/br-lan (vmbr1)
uci set network.wan.device='eth0'
uci set network.lan.ipaddr='10.10.10.2'
uci set network.lan.netmask='255.255.255.0'
uci commit network
/etc/init.d/network restart
GCP no responde a DHCP broadcast desde VMs internas, por eso la WAN se configura como IP estática dentro de vmbr0.
# Verificar IPs asignadas
ip addr show eth0 # debe mostrar 192.168.100.2
ip addr show br-lan # debe mostrar 10.10.10.2
# Ping al host Proxmox (gateway WAN)
ping -c 3 192.168.100.1
# Ping a internet
ping -c 3 1.1.1.1
/etc/init.d/dnsmasq status
El servicio dnsmasq gestiona tanto DHCP como DNS en OpenWRT por defecto. La configuración base está en /etc/config/dhcp.
Para ver los leases activos:
cat /tmp/dhcp.leases
El hookscript necesita SSH sin contraseña hacia OpenWRT. Los pasos son:
Desde el host Proxmox, generar una clave RSA de 4096 bits:
ssh-keygen -t rsa -b 4096 -N "" -f /root/.ssh/id_rsa
Esto crea /root/.ssh/id_rsa (clave privada) y /root/.ssh/id_rsa.pub (clave pública).
ssh-copy-id root@192.168.100.2
Esto añade la clave pública a /root/.ssh/authorized_keys en OpenWRT.
ssh root@192.168.100.2 "echo ok"
Debe responder ok sin pedir contraseña.
Si aparece el error WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED (por ejemplo tras recrear la VM OpenWRT), ejecutar primero:
ssh-keygen -f "/root/.ssh/known_hosts" -R "192.168.100.2"
OpenWRT tiene un firewall activo por defecto que bloquea SSH desde la WAN. Para el uso interno de HAaaS se desactiva completamente:
/etc/init.d/firewall stop
/etc/init.d/firewall disable
Por defecto OpenWRT usa eth0 (WAN) como interfaz de origen al contactar 10.10.10.0/24, lo que hace que las VMs no puedan responder. Hay que forzar que el tráfico LAN salga siempre por br-lan:
uci add network route
uci set network.@route[-1].interface='lan'
uci set network.@route[-1].target='10.10.10.0'
uci set network.@route[-1].netmask='255.255.255.0'
uci set network.@route[-1].gateway='10.10.10.2'
uci commit network
/etc/init.d/network restart
lo que hace que las VMs no puedan responder. Hay que forzar que el tráfico LAN salga siempre por br-lan:
Verificar:
ping -c 3 10.10.10.100
| Interfaz | IP | Red Proxmox | Rol |
|---|---|---|---|
eth0 |
192.168.100.2/24 |
vmbr0 |
WAN |
br-lan (eth1) |
10.10.10.2/24 |
vmbr1 |
LAN / DHCP / DNS |
El hookscript se ejecuta automáticamente en el host Proxmox cada vez que una VM arranca o se detiene. Se comunica con OpenWRT vía SSH para registrar o eliminar la reserva DHCP de esa VM.
Flujo:
pre-start → hookscript registra MAC → 10.10.10.<VMID> en OpenWRT → reinicia dnsmasqpost-stop → hookscript elimina la reserva → reinicia dnsmasqmkdir -p /var/lib/vz/snippets
cat > /var/lib/vz/snippets/hookscript.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
# ─────────────────────────────────────────────────────────────────────────────
# hookscript.sh — Gestiona reservas DHCP en OpenWRT por MAC
#
# Proxmox ejecuta este script con dos argumentos:
# $1 = VMID
# $2 = PHASE (pre-start | post-start | pre-stop | post-stop)
#
# Requisitos:
# - Acceso SSH sin contraseña desde Proxmox a OpenWRT
# - Para configurarlo: ssh-copy-id root@192.168.100.2
#
# Regla de IPs: cada VM recibe 10.10.10. — el VMID debe ser 1-254.
# ─────────────────────────────────────────────────────────────────────────────
VMID="$1"
PHASE="$2"
OPENWRT_IP="192.168.100.2" # IP WAN de la VM OpenWRT en vmbr0
log() { echo "[hookscript:${VMID}] $*"; }
err() { echo "[hookscript:${VMID}] ERROR: $*" >&2; exit 1; }
# Obtener la MAC de net0 de esta VM
get_mac() {
grep "^net0:" "/etc/pve/qemu-server/${VMID}.conf" 2>/dev/null \
| grep -oP '([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' \
| head -1
}
# Verificar que OpenWRT es accesible por SSH antes de operar
check_openwrt() {
ssh -o ConnectTimeout=5 -o BatchMode=yes \
"root@${OPENWRT_IP}" "exit" 2>/dev/null \
|| err "No se puede conectar a OpenWRT (${OPENWRT_IP}). Comprueba SSH y que la VM OpenWRT está arriba."
}
# Ejecutar un comando en OpenWRT via SSH
owrt() {
ssh -o ConnectTimeout=5 -o BatchMode=yes \
"root@${OPENWRT_IP}" "$*" 2>/dev/null
}
# Eliminar TODAS las reservas DHCP existentes para una MAC dada.
# Se borran en orden inverso para no romper los índices del array uci.
remove_existing_reservation() {
local mac="$1"
local indices
indices=$(owrt "/sbin/uci show dhcp" \
| grep -i "\.mac='${mac}'" \
| grep -oP '@host\[\K[0-9]+' \
| sort -rn || true)
for idx in $indices; do
log "Eliminando reserva existente dhcp.@host[${idx}] (${mac})"
owrt "/sbin/uci delete dhcp.@host[${idx}]"
done
if [[ -n "$indices" ]]; then
owrt "/sbin/uci commit dhcp"
fi
}
reload_dnsmasq() {
owrt "/etc/init.d/dnsmasq restart"
log "dnsmasq reiniciado."
}
# Validar que el VMID puede usarse como último octeto de IP
validate_ip_from_vmid() {
local vmid="$1"
if [[ "$vmid" -lt 1 || "$vmid" -gt 254 ]]; then
err "VMID $vmid fuera de rango para IP 10.10.10.x (debe ser entre 1 y 254)"
fi
}
# ── Lógica por fase ───────────────────────────────────────────────────────────
case "$PHASE" in
pre-start)
validate_ip_from_vmid "$VMID"
MAC=$(get_mac)
IP="10.10.10.${VMID}"
[[ -z "$MAC" ]] && err "No se encontró MAC en net0 para VMID $VMID"
log "Fase pre-start: reservando ${MAC} → ${IP}"
check_openwrt
# Limpiar duplicados antes de añadir (por si la VM se reinició antes)
remove_existing_reservation "$MAC"
# Registrar reserva nueva
owrt "/sbin/uci add dhcp host"
owrt "/sbin/uci set dhcp.@host[-1].name='haas-vm-${VMID}'"
owrt "/sbin/uci set dhcp.@host[-1].mac='${MAC}'"
owrt "/sbin/uci set dhcp.@host[-1].ip='${IP}'"
owrt "/sbin/uci commit dhcp"
log "Reserva DHCP registrada: ${MAC} → ${IP}"
reload_dnsmasq
;;
post-stop)
MAC=$(get_mac)
[[ -z "$MAC" ]] && { log "No se encontró MAC, nada que limpiar."; exit 0; }
log "Fase post-stop: eliminando reserva para ${MAC}"
check_openwrt
remove_existing_reservation "$MAC"
reload_dnsmasq
;;
# Las fases post-start y pre-stop no requieren acción
*)
exit 0
;;
esac
exit 0
EOF
chmod +x /var/lib/vz/snippets/hookscript.sh
Las VMs en la red interna (vmbr1) necesitan acceso a internet a través de OpenWRT. Para esto hay que habilitar el reenvío de IP y configurar NAT (Masquerade).
# Activar inmediatamente
sysctl -w net.ipv4.ip_forward=1
# Hacer que el cambio sea permanente
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# Activar enmascaramiento en la zona WAN
uci set firewall.@zone[1].masq='1'
uci set firewall.wan.masq='1'
# Aplicar cambios y reiniciar el firewall
uci commit firewall
/etc/init.d/firewall restart
Para confirmar que la configuración es correcta:
# Verificar regla de NAT
nft list ruleset | grep masquerade
# Desde una VM interna, verificar conectividad
ping -c 3 8.8.8.8
Esta configuración Permite que todas las máquinas de la red interna salgan a internet. Si en el futuro se requiere restringir el acceso a ciertos puertos, se deberán añadir reglas adicionales en /etc/config/firewall.
| Problema | Causa | Solución |
|---|---|---|
| iPXE al arrancar | boot apuntando a net0 |
qm set 200 --boot order=ide0 |
| Terminal serial no responde | OpenWRT no activa serial por defecto | Usar consola NoVNC desde web UI |
eth0 sin IP tras DHCP |
GCP no responde a broadcast | Configurar WAN como IP estática |
| Sin ping a internet | Falta NAT en host Proxmox | Verificar iptables MASQUERADE en ens4 |
Ver también: Configuración de Proxmox, Nginx Proxy Manager