通过 Cloudflare SSH 隧道的 EAG Proxmox
Proxmox VE 9.2 服务器托管一次性测试虚拟机。在此环境中,主机名为 eric-pve-ssh.eag.tools。代理程序克隆模板、运行测试,然后在删除任何内容前征询用户意见。
模板、克隆步骤和客户机登录方式与 LAN 环境一致。改变的是路径:云代理通过 Cloudflare Access 连接到主机,并通过该主机跳转访问所有客户机。
访问
环境启动脚本根据 PVE_SSH_PRIVATE_B64 和 PVE_SSH_PUBLIC_KEY 创建密钥对:
| 文件 | 模式 | 用途 |
|---|---|---|
~/.ssh/pve_ssh | 600 | 私钥。用于 Proxmox 主机、Windows Administrator、Ubuntu erolleman。 |
~/.ssh/pve_ssh.pub | 644 | 匹配的公钥。 |
/etc/ssh/ssh_config.d/eric-pve-ssh.conf 已为 eric-pve-ssh.eag.tools 设置 User root、IdentityFile ~/.ssh/pve_ssh、IdentitiesOnly yes 以及 ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h。使用该主机别名。保留系统文件不变。
KEY="$HOME/.ssh/pve_ssh"
test -r "$KEY" || { echo "缺少 $KEY; 启动脚本会根据 PVE_SSH_PRIVATE_B64 创建它" >&2; exit 1; }
PVE="ssh -o StrictHostKeyChecking=accept-new eric-pve-ssh.eag.tools"
$PVE 'qm list'
客户机 SSH,同时适用于 10.115.64.x(vmbr0)和 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"
在使用 VMSSH 之前,将 USER 设置为 Administrator 或 erolleman,将 IP 设置为客户机地址。
- Windows 虚拟机登录:
Administrator/Change_me,或使用~/.ssh/pve_ssh。 - Ubuntu 虚拟机登录:
erolleman,使用~/.ssh/pve_ssh。sudo无需密码。docker需要sudo。 - Proxmox Web UI 在 LAN 上监听于
https://10.115.64.44:8006。此隧道仅为 SSH。通过$PVE使用qm和pvesh管理主机。 - 将密钥材料保存在上述文件中。不要将
~/.ssh/pve_ssh或PVE_SSH_PRIVATE_B64读入命令、日志或回复中。不要生成替代密钥。
通过 SSH 的 PowerShell 远程处理使用相同的跳转。将其添加到 ~/.ssh/config(仅限客户机 Host 块;系统文件已覆盖隧道主机):
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"
硬性规则
- 绝不要启动、修改、覆盖克隆或删除虚拟机 100(
ubuntu-2404)或虚拟机 101(ws2025-template)。两者都是模板。 - 除非用户明确要求,否则不要更改主机配置(存储、网络、SDN、软件包)。
- 不要在克隆上重新运行 sysprep、
windows-template/Copy-SysprepFiles.ps1或ubuntu-template/cloud-init-prepare.sh。这些脚本用于准备模板,且保留在工作站上。它们不在此环境中。 - 为你创建的每个虚拟机打上
ai-test标签。 - 测试完成后,询问用户是否销毁你创建的虚拟机。未得到用户回答前,绝不要销毁任何虚拟机。
- 不要打开直接 SSH 会话到
10.115.64.44、10.115.64.x或10.20.0.x。这些网络不会路由到云代理。主机命令通过eric-pve-ssh.eag.tools执行。客户机命令使用上述ProxyCommand跳转。
主机信息
该主机是一个 Hyper-V 虚拟机。嵌套 VT-x 已暴露。在 LAN 上,其地址为 10.115.64.44。
| 项目 | 值 |
|---|---|
| 硬件 | Intel Xeon Gold 6234(8 核 / 16 线程),94 GB RAM。请据此预算虚拟机资源。 |
vmbr0 | LAN 桥接,DHCP。主机地址 10.115.64.44/24,网关和 DNS 10.115.64.1,搜索域 eag.lan。两个模板的 NIC 都在此桥接上。客户机地址为 10.115.64.x。从此环境通过 eric-pve-ssh.eag.tools 跳转 SSH 到它们。 |
nat0 | SDN VNet(简单区域 nat),10.20.0.0/24。网关 10.20.0.1。DHCP 10.20.0.100–10.20.0.200(12 小时),由主机上的 dnsmasq 提供,DNS 10.20.0.1。SNAT 通过 vmbr0 出站,地址为 10.115.64.44。Proxmox 主机可以访问此子网。从此环境,使用相同的跳转 SSH 到 10.20.0.x。 |
local-data | VG vmdata 上的 LVM-thin,约 671 GB。虚拟机磁盘、EFI、TPM 和 cloud-init 驱动器。 |
local-lvm | 小型 PVE 系统 VG 上的 LVM-thin,约 23 GB,空。保持未使用。 |
local | 根文件系统上的目录。备份、容器模板、导入。 |
isos | 目录 /mnt/isos。仅安装 ISO。 |
选择模板
| 需求 | 克隆 |
|---|---|
| Windows、RDP 或 Windows PowerShell | 101 ws2025-template |
Linux、Docker、pwsh 或 Nushell | 100 ubuntu-2404 |
如果用户未指定操作系统,请先询问再克隆。
Windows 克隆会启用客户机内的 RDP。Cloudflare 隧道承载 SSH。使用 SSH 或 PowerShell 远程处理访问客户机。
模板:ws2025-template(VMID 101)
Windows Server 2025 Datacenter Evaluation。4 vCPU(host),4 GB RAM,UEFI + Secure Boot + TPM 2.0,local-data 上的 64 GB VirtIO SCSI 磁盘,vmbr0 上的 VirtIO NIC(4 队列),已安装 VirtIO 驱动程序和 QEMU Guest Agent。
首次启动时,每个克隆会自动:
- 获取随机计算机名、太平洋时区、en-US 区域设置和 Administrator 密码
Change_me。 - 自动登录一次并运行
Initialize-WindowsServer.ps1,该脚本从 GitHub 安装最新的 PowerShell 7,启用带有powershell子系统的 OpenSSH 服务器(通过 SSH 的 PS 远程处理),将 SSH 密钥添加到administrators_authorized_keys,启用 RDP,将公共网络设置为私有,并移除 Ctrl+Alt+Del 要求。 - 移除自动登录密码和应答文件,写入
C:\ProgramData\TemplateSetup\setup-complete,然后重启。
首次启动需要几分钟并需联网(下载 PowerShell 7)。日志:C:\ProgramData\TemplateSetup\Initialize-WindowsServer.log。
模板:ubuntu-2404(VMID 100)
Ubuntu 24.04.5 LTS。2 vCPU(x86-64-v2-AES),4 GB RAM,UEFI(Secure Boot 密钥未注册),无 TPM,local-data 上的 32 GB VirtIO SCSI 磁盘,vmbr0 上的 VirtIO NIC。已安装 QEMU Guest Agent、OpenSSH、PowerShell 7(pwsh)、Nushell(/usr/local/bin 上的 nu)和 Docker。未安装 Podman。时区为 UTC。
Cloud-init 已清理并仅限于 NoCloud 和 ConfigDrive 数据源。磁盘没有 netplan,因此在 Proxmox cloud-init 运行之前,克隆没有网络。erolleman 账户已在 sudo 组中,并已拥有项目公钥。SSH 是 socket 激活的。Guest Agent 在 virtio 端口出现时从 udev 启动。
Proxmox 生成的 user-data 还会创建一个锁定的 ubuntu 账户,没有 SSH 密钥。请以 erolleman 身份登录。
工作流程:创建测试虚拟机
- [ ] 1. 确认源虚拟机是模板
- [ ] 2. 克隆、配置、打标签、启动
- [ ] 3. 从 Guest Agent 获取 IP
- [ ] 4. 等待首次启动完成
- [ ] 5. 运行测试
- [ ] 6. 询问用户是否销毁虚拟机
1. 确认模板。 如果此命令无输出,则停止并告知用户。不要克隆非模板的虚拟机。
$PVE 'qm config 101 | grep ^template' # Windows
$PVE 'qm config 100 | grep ^template' # Ubuntu
2. 克隆、配置、打标签、启动。 链接克隆是默认选项。仅在虚拟机需要比模板存活更久时添加 --full --storage local-data。
克隆继承 4 vCPU / 4 GB(Windows)或 2 vCPU / 4 GB(Ubuntu)。当测试需要更多资源时,在启动前使用 qm set 修改克隆。保持模板不变。
Windows:
ID=$($PVE 'pvesh get /cluster/nextid')
$PVE "qm clone 101 $ID --name test-<用途> && qm set $ID --tags ai-test && qm start $ID"
Ubuntu:在首次启动前附加 cloud-init 驱动器。模板上的 ide2 是一个空 CD;替换它。--ciupgrade 0 跳过默认的完整软件包升级。没有 --ipconfig0 ip=dhcp,克隆将永远无法获取地址。
ID=$($PVE 'pvesh get /cluster/nextid')
$PVE "qm clone 100 $ID --name test-<用途> && qm set $ID --tags ai-test --ide2 local-data:cloudinit --citype nocloud --ipconfig0 ip=dhcp --ciupgrade 0 --boot order=scsi0 && qm start $ID"
隔离网络,不论操作系统:在启动前,将 NIC 放到 nat0 上。仅在用户希望将虚拟机脱离 LAN 时使用此选项。客户机地址将为 10.20.0.x。从此环境的 SSH 仍然使用访问部分中的跳转;Proxmox 主机才是能访问 nat0 的。
$PVE "qm set $ID --net0 virtio,bridge=nat0"
# 仅限 Ubuntu。Windows 自行 DHCP。将 Ubuntu DNS 指向 NAT 网关上的 dnsmasq。
$PVE "qm set $ID --ipconfig0 ip=dhcp --nameserver 10.20.0.1"
3. 获取 IP。 操作系统启动后,Guest Agent 会响应。每约 15 秒重试一次。对于 vmbr0 NIC 使用 10.115.64.x 地址,对于 nat0 使用 10.20.0.x 地址。
$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. 等待首次启动。 克隆会获得新的主机密钥,地址会被重用,因此客户机 SSH 会跳过主机密钥检查。跳转主机密钥已使用 accept-new 存储。
Windows:SSH 在安装过程中启动,脚本在其完成后重启虚拟机。当 setup-complete 存在且 Windows 自其写入后已再次启动时,虚拟机即准备就绪。
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) 避免嵌套 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)"
until STATUS=$($VMSSH "$CHECK" 2>/dev/null | tr -d '\r'); [[ $STATUS == READY* ]]; do sleep 15; done
echo "$STATUS"
READY succeeded 表示安装完成且虚拟机已重启。如果显示 READY failed,请阅读日志并报告:
$VMSSH 'powershell -NoProfile -Command "Get-Content C:\ProgramData\TemplateSetup\Initialize-WindowsServer.log -Tail 30"'
Ubuntu:等待 cloud-init 报告 status: done。status: error 表示停止并阅读 /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"
until STATUS=$($VMSSH 'cloud-init status 2>/dev/null' | tr -d '\r'); [[ $STATUS == status:\ done || $STATUS == status:\ error ]]; do sleep 10; done
echo "$STATUS"
5. 运行测试。
Windows:默认 SSH shell 是 cmd.exe。
$VMSSH 'pwsh -NoProfile -Command "Get-ComputerInfo | Select-Object OsName, CsName"'
通过 SSH 的 PowerShell 远程处理(从云代理的 pwsh)使用访问部分中的 ~/.ssh/config 客户机块:
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. 清理前询问。 报告测试结果,列出你创建的虚拟机(VMID、名称、IP),并询问用户是否销毁它们。在可用时使用 AskQuestion 工具,提供选项“销毁”、“保持运行”和“关闭但不销毁”(如果你创建了多个虚拟机,还可提供每个虚拟机的选择)。
如果用户选择销毁:
$PVE "qm stop $ID && qm destroy $ID --purge"
如果用户选择保持但关闭:
$PVE "qm shutdown $ID --timeout 120 || qm stop $ID"
当虚拟机被保留时,告知用户它们的 VMID,以便以后可以找到(它们保留 ai-test 标签)。
快照
两个模板的磁盘都在 LVM-thin 上,因此克隆支持快照:
$PVE "qm snapshot $ID before-test"
$PVE "qm rollback $ID before-test"
$PVE "qm delsnapshot $ID before-test"
清理和盘点
$PVE 'pvesh get /cluster/resources --type vm --output-format json' \
| jq -r '.[] | select((.tags // "") | split(";") | index("ai-test")) | "\(.vmid)\t\(.name)\t\(.status)"'
只提供销毁你在本次会话中创建的 ai-test 虚拟机,除非用户要求更广泛的清理。始终先获得用户的确认。
注意事项
- Windows 是评估版,具有 180 天许可。仅用于短期虚拟机。
- 如果 Ubuntu 克隆在未附加 cloud-init 驱动器的情况下启动一次,将无法获取网络,且 cloud-init 不会干净地重试。销毁它并重新克隆。
- 不要在 Windows 克隆上运行
sysprep,也不要将任一模板的克隆转换为模板。 - 主机地址是 DHCP 租约。
nat0SNAT 固定为10.115.64.44。如果该地址更改,请说明;除非用户要求,否则不要编辑 SDN。 cloudflared由系统 SSH 配置调用为/usr/local/bin/cloudflared。缺少密钥文件意味着启动脚本未运行;保持密钥不变并报告该情况。- 客户机主机密钥随每次克隆更改。
UserKnownHostsFile=/dev/null适用于客户机会话。隧道主机密钥保留在~/.ssh/known_hosts中。