EAG Proxmox a través del túnel SSH de Cloudflare
El servidor Proxmox VE 9.2 aloja máquinas virtuales de prueba desechables. Desde este entorno, el anfitrión es eric-pve-ssh.eag.tools. Los agentes clonan una plantilla, ejecutan la prueba y luego preguntan antes de eliminar cualquier cosa.
Las plantillas, los pasos de clonación y los inicios de sesión en las invitadas coinciden con el entorno LAN. Lo que cambia es la ruta: el agente en la nube llega al anfitrión a través de Cloudflare Access, y llega a cada invitada saltando a través de ese anfitrión.
Acceso
El script de inicio del entorno escribe el par de claves de PVE_SSH_PRIVATE_B64 y PVE_SSH_PUBLIC_KEY:
| Archivo | Modo | Uso |
|---|---|---|
~/.ssh/pve_ssh | 600 | Clave privada. Anfitrión Proxmox, Administrator de Windows, erolleman de Ubuntu. |
~/.ssh/pve_ssh.pub | 644 | Clave pública correspondiente. |
/etc/ssh/ssh_config.d/eric-pve-ssh.conf ya establece User root, IdentityFile ~/.ssh/pve_ssh, IdentitiesOnly yes y ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h para eric-pve-ssh.eag.tools. Use ese alias de anfitrión. Deje el archivo del sistema en su lugar.
KEY="$HOME/.ssh/pve_ssh"
test -r "$KEY" || { echo "falta $KEY; el script de inicio lo crea desde PVE_SSH_PRIVATE_B64" >&2; exit 1; }
PVE="ssh -o StrictHostKeyChecking=accept-new eric-pve-ssh.eag.tools"
$PVE 'qm list'
SSH a las invitadas, tanto para 10.115.64.x (vmbr0) como para 10.20.0.x (nat0):
JUMP='ssh -o StrictHostKeyChecking=accept-new -W %h:%p eric-pve-ssh.eag.tools'
VMSSH="ssh -i $KEY -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -o ProxyCommand=\"$JUMP\" USER@$IP"
Establezca USER como Administrator o erolleman e IP como la dirección de la invitada antes de expandir VMSSH.
- Inicio de sesión en VM Windows:
Administrator/Change_me, o~/.ssh/pve_ssh. - Inicio de sesión en VM Ubuntu:
erollemancon~/.ssh/pve_ssh.sudono requiere contraseña.dockernecesitasudo. - La interfaz web de Proxmox escucha en la LAN en
https://10.115.64.44:8006. Este túnel es solo SSH. Administre el anfitrión conqmypvesha través de$PVE. - Mantenga el material de clave en los archivos anteriores. No lea
~/.ssh/pve_sshniPVE_SSH_PRIVATE_B64en un comando, un registro o una respuesta. No genere una clave de reemplazo.
La administración remota de PowerShell a través de SSH usa el mismo salto. Agregue esto a ~/.ssh/config (solo el bloque Host de la invitada; el archivo del sistema ya cubre el anfitrión del túnel):
Host 10.115.64.* 10.20.0.*
IdentityFile ~/.ssh/pve_ssh
IdentitiesOnly yes
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
ProxyCommand ssh -o StrictHostKeyChecking=accept-new -W %h:%p eric-pve-ssh.eag.tools
Enter-PSSession -HostName <ip> -UserName Administrator -KeyFilePath "$HOME/.ssh/pve_ssh"
Reglas estrictas
- Nunca iniciar, modificar, clonar sobre o eliminar la VM 100 (
ubuntu-2404) o la VM 101 (ws2025-template). Ambas son plantillas. - No cambiar la configuración del anfitrión (almacenamiento, red, SDN, paquetes) a menos que el usuario lo solicite explícitamente.
- No volver a ejecutar sysprep,
windows-template/Copy-SysprepFiles.ps1oubuntu-template/cloud-init-prepare.shen un clon. Esos scripts preparan las plantillas y permanecen en la estación de trabajo. No están en este entorno. - Etiquetar cada VM que cree con
ai-test. - Cuando las pruebas hayan terminado, preguntar al usuario si debe destruir las VMs que creó. Nunca destruir una VM sin la respuesta del usuario.
- No abrir ninguna sesión SSH directa a
10.115.64.44,10.115.64.xo10.20.0.x. Esas redes no están enrutadas al agente en la nube. Los comandos del anfitrión van a través deeric-pve-ssh.eag.tools. Los comandos de las invitadas usan el saltoProxyCommandanterior.
Datos del anfitrión
El anfitrión es una máquina virtual Hyper-V. VT-x anidado está expuesto. En la LAN su dirección es 10.115.64.44.
| Elemento | Valor |
|---|---|
| Hardware | Intel Xeon Gold 6234 (8 núcleos / 16 hilos), 94 GB de RAM. Ajuste las VMs en consecuencia. |
vmbr0 | Puente LAN, DHCP. Dirección del anfitrión 10.115.64.44/24, puerta de enlace y DNS 10.115.64.1, dominio de búsqueda eag.lan. Las NIC de ambas plantillas están en este puente. Las direcciones de las invitadas son 10.115.64.x. Desde este entorno, el SSH hacia ellas salta a través de eric-pve-ssh.eag.tools. |
nat0 | VNet SDN (zona simple nat), 10.20.0.0/24. Puerta de enlace 10.20.0.1. DHCP 10.20.0.100–10.20.0.200 (12 h) desde dnsmasq en el anfitrión, DNS 10.20.0.1. SNAT hacia fuera por vmbr0 como 10.115.64.44. El anfitrión Proxmox puede alcanzar esta subred. Desde este entorno, el SSH a 10.20.0.x usa el mismo salto. |
local-data | LVM-thin en VG vmdata, ~671 GB. Discos de VM, EFI, TPM y unidades cloud-init. |
local-lvm | LVM-thin en el pequeño VG del sistema PVE, ~23 GB, vacío. Déjelo sin usar. |
local | Directorio en el sistema de archivos raíz. Copias de seguridad, plantillas de contenedores, importaciones. |
isos | Directorio /mnt/isos. Solo ISOs de instalación. |
Qué plantilla
| Necesidad | Clonar |
|---|---|
| Windows, RDP o Windows PowerShell | 101 ws2025-template |
Linux, Docker, pwsh o Nushell | 100 ubuntu-2404 |
Si el usuario no dice qué SO, pregunte antes de clonar.
Los clones de Windows habilitan RDP dentro de la invitada. El túnel de Cloudflare transporta SSH. Use SSH o la administración remota de PowerShell para alcanzar la invitada.
Plantilla: ws2025-template (VMID 101)
Windows Server 2025 Datacenter Evaluation. 4 vCPU (host), 4 GB de RAM, UEFI + Secure Boot + TPM 2.0, disco VirtIO SCSI de 64 GB en local-data, NIC VirtIO en vmbr0 (4 colas), controladores VirtIO y agente invitado QEMU instalados.
En el primer arranque, cada clon automáticamente:
- Obtiene un nombre de equipo aleatorio, zona horaria del Pacífico, configuración regional en-US y contraseña de Administrador
Change_me. - Inicia sesión automáticamente una vez y ejecuta
Initialize-WindowsServer.ps1, que instala la última versión de PowerShell 7 desde GitHub, habilita el servidor OpenSSH con un subsistemapowershell(administración remota de PS sobre SSH), agrega la clave SSH aadministrators_authorized_keys, habilita RDP, establece las redes públicas como Privadas y elimina el requisito de Ctrl+Alt+Del. - Elimina la contraseña de inicio de sesión automático y el archivo de respuesta, escribe
C:\ProgramData\TemplateSetup\setup-complete, y luego se reinicia.
El primer arranque toma varios minutos y necesita internet (descarga de PowerShell 7). Registro: C:\ProgramData\TemplateSetup\Initialize-WindowsServer.log.
Plantilla: ubuntu-2404 (VMID 100)
Ubuntu 24.04.5 LTS. 2 vCPU (x86-64-v2-AES), 4 GB de RAM, UEFI (claves Secure Boot no inscritas), sin TPM, disco VirtIO SCSI de 32 GB en local-data, NIC VirtIO en vmbr0. Agente invitado QEMU, OpenSSH, PowerShell 7 (pwsh), Nushell (nu en /usr/local/bin) y Docker instalados. Podman no está instalado. Zona horaria UTC.
Cloud-init se ha limpiado y limitado a las fuentes de datos NoCloud y ConfigDrive. El disco no tiene netplan, por lo que un clon no tiene red hasta que se ejecute cloud-init de Proxmox. La cuenta erolleman ya está en sudo y ya tiene la clave pública del proyecto. SSH está activado por socket. El agente invitado se inicia desde udev cuando aparece el puerto virtio.
Los datos de usuario generados por Proxmox también crean una cuenta ubuntu bloqueada sin clave SSH. Inicie sesión como erolleman.
Flujo de trabajo: crear una VM de prueba
- [ ] 1. Confirmar que la VM de origen es una plantilla
- [ ] 2. Clonar, configurar, etiquetar, iniciar
- [ ] 3. Obtener la IP del agente invitado
- [ ] 4. Esperar hasta que el primer arranque haya terminado
- [ ] 5. Ejecutar la prueba
- [ ] 6. Preguntar al usuario si debe destruir la VM
1. Confirmar la plantilla. Si esto no imprime nada, deténgase e informe al usuario. No clone una VM que no sea una plantilla.
$PVE 'qm config 101 | grep ^template' # Windows
$PVE 'qm config 100 | grep ^template' # Ubuntu
2. Clonar, configurar, etiquetar, iniciar. Los clones vinculados son el valor predeterminado. Agregue --full --storage local-data solo cuando la VM deba sobrevivir a la plantilla.
Los clones heredan 4 vCPU / 4 GB (Windows) o 2 vCPU / 4 GB (Ubuntu). Cuando la prueba necesite más, use qm set en el clon antes de iniciarlo. Deje las plantillas sin cambios.
Windows:
ID=$($PVE 'pvesh get /cluster/nextid')
$PVE "qm clone 101 $ID --name test-<propósito> && qm set $ID --tags ai-test && qm start $ID"
Ubuntu: conecte la unidad cloud-init antes del primer inicio. ide2 en la plantilla es un CD vacío; reemplácelo. --ciupgrade 0 omite la actualización completa de paquetes predeterminada. Sin --ipconfig0 ip=dhcp el clon nunca obtiene una dirección.
ID=$($PVE 'pvesh get /cluster/nextid')
$PVE "qm clone 100 $ID --name test-<propósito> && qm set $ID --tags ai-test --ide2 local-data:cloudinit --citype nocloud --ipconfig0 ip=dhcp --ciupgrade 0 --boot order=scsi0 && qm start $ID"
Red aislada, para cualquier SO: antes de iniciar, coloque la NIC en nat0. Mantenga esto solo cuando el usuario quiera la VM fuera de la LAN. La dirección de la invitada será 10.20.0.x. El SSH desde este entorno aún usa el salto en Acceso; el anfitrión Proxmox es el que puede alcanzar nat0.
$PVE "qm set $ID --net0 virtio,bridge=nat0"
# Solo Ubuntu. Windows usa DHCP por sí mismo. Apunte el DNS de Ubuntu a dnsmasq en la puerta de enlace NAT.
$PVE "qm set $ID --ipconfig0 ip=dhcp --nameserver 10.20.0.1"
3. Obtener la IP. El agente invitado responde una vez que el SO está activo. Reintente cada ~15 s. Use la dirección 10.115.64.x para una NIC vmbr0, o la dirección 10.20.0.x para nat0.
$PVE "qm guest cmd $ID network-get-interfaces" \
| jq -r '.[]."ip-addresses"[]? | select(."ip-address-type"=="ipv4") | ."ip-address"' \
| grep -vE '^(127\.|169\.254\.)'
4. Esperar el primer arranque. Los clones obtienen nuevas claves de anfitrión y las direcciones se reutilizan, por lo que el SSH a la invitada omite la verificación de la clave del anfitrión. La clave del anfitrión de salto se almacena con accept-new.
Windows: SSH se activa durante la configuración, y el script reinicia la VM cuando termina. La VM está lista una vez que existe setup-complete y Windows se ha reiniciado desde que se escribió.
VMSSH="ssh -i $KEY -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -o ProxyCommand=\"$JUMP\" Administrator@$IP"
# -EncodedCommand (base64 UTF-16LE) evita el anidamiento de comillas bash/cmd/PowerShell.
PS_CHECK=$(cat <<'EOF'
$m = Get-Item C:\ProgramData\TemplateSetup\setup-complete -ErrorAction SilentlyContinue
if ($m -and (Get-CimInstance Win32_OperatingSystem).LastBootUpTime -gt $m.LastWriteTime) { 'READY ' + (Get-Content $m.FullName) } else { 'WAITING' }
EOF
)
CHECK="powershell -NoProfile -EncodedCommand $(printf %s "$PS_CHECK" | iconv -t UTF-16LE | base64 -w0)"
hasta que STATUS=$($VMSSH "$CHECK" 2>/dev/null | tr -d '\r'); [[ $STATUS == READY* ]]; haga sleep 15; hecho
echo "$STATUS"
READY succeeded significa que la configuración terminó y la VM se ha reiniciado. Si imprime READY failed, lea el registro e infórmelo:
$VMSSH 'powershell -NoProfile -Command "Get-Content C:\ProgramData\TemplateSetup\Initialize-WindowsServer.log -Tail 30"'
Ubuntu: espere hasta que cloud-init informe status: done. status: error significa detenerse y leer /var/log/cloud-init-output.log.
VMSSH="ssh -i $KEY -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 -o ProxyCommand=\"$JUMP\" erolleman@$IP"
hasta que STATUS=$($VMSSH 'cloud-init status 2>/dev/null' | tr -d '\r'); [[ $STATUS == status:\ done || $STATUS == status:\ error ]]; haga sleep 10; hecho
echo "$STATUS"
5. Ejecutar la prueba.
Windows: el shell SSH predeterminado es cmd.exe.
$VMSSH 'pwsh -NoProfile -Command "Get-ComputerInfo | Select-Object OsName, CsName"'
Administración remota de PowerShell a través de SSH (desde pwsh en el agente en la nube) usa el bloque Host de la invitada en ~/.ssh/config en Acceso:
Enter-PSSession -HostName <ip> -UserName Administrator -KeyFilePath "$HOME/.ssh/pve_ssh"
Ubuntu:
$VMSSH 'pwsh -NoLogo -Command "$PSVersionTable.PSVersion.ToString()"'
$VMSSH 'sudo docker info --format "{{.ServerVersion}}"'
6. Preguntar antes de limpiar. Informe los resultados de la prueba, enumere las VMs que creó (VMID, nombre, IP) y pregunte al usuario si debe destruirlas. Use la herramienta AskQuestion cuando esté disponible, con las opciones "Destruir", "Mantener en ejecución" y "Apagar pero conservar" (más opciones por VM si creó varias).
Si el usuario elige destruir:
$PVE "qm stop $ID && qm destroy $ID --purge"
Si el usuario elige conservar pero apagar:
$PVE "qm shutdown $ID --timeout 120 || qm stop $ID"
Cuando se conservan las VMs, informe al usuario sus VMIDs para que puedan encontrarlas más tarde (conservan la etiqueta ai-test).
Instantáneas
Los discos de ambas plantillas están en LVM-thin, por lo que los clones admiten instantáneas:
$PVE "qm snapshot $ID before-test"
$PVE "qm rollback $ID before-test"
$PVE "qm delsnapshot $ID before-test"
Limpieza e inventario
$PVE 'pvesh get /cluster/resources --type vm --output-format json' \
| jq -r '.[] | select((.tags // "") | split(";") | index("ai-test")) | "\(.vmid)\t\(.name)\t\(.status)"'
Solo ofrezca destruir las VMs ai-test que creó en esta sesión, a menos que el usuario solicite una limpieza más amplia. Siempre obtenga la confirmación del usuario primero.
Errores comunes
- Windows es una edición de evaluación con una licencia de 180 días. Úsela para VMs de corta duración.
- Un clon de Ubuntu que arranca una vez sin la unidad cloud-init se inicia sin red, y cloud-init no volverá a intentarlo limpiamente. Destrúyalo y clónelo de nuevo.
- No ejecute
sysprepen clones de Windows, y no convierta clones de ninguna de las plantillas en plantillas. - La dirección del anfitrión es una concesión DHCP. El SNAT de
nat0está fijado a10.115.64.44. Si esa dirección cambia, dígalo; no edite SDN a menos que el usuario lo solicite. cloudflaredse invoca como/usr/local/bin/cloudflaredpor la configuración SSH del sistema. Un archivo de clave faltante significa que el script de inicio no se ejecutó; deje los secretos intactos e infórmelo.- Las claves de anfitrión de las invitadas cambian en cada clon.
UserKnownHostsFile=/dev/nullse aplica a la sesión de la invitada. La clave del anfitrión del túnel permanece en~/.ssh/known_hosts.