虚拟机没有 IP 时,如何通过 SSH 隧道进入 KVM 控制台
虚拟机没有 IP 时,如何通过 SSH 隧道进入 KVM 控制台
一台 KVM 虚拟机无法通过 SSH 连接,不一定意味着它已经失去控制。虚拟机可能尚未获取 DHCP 地址、静态网络配置错误、系统停在磁盘解密界面,甚至根本还没有启动到网络服务。
这类故障的可靠处理方式,是绕过 guest 网络,从 KVM 宿主机直接进入虚拟机的 VGA 控制台。本文介绍一条适用于 Windows、macOS 和 Linux 的通用路径:
运维电脑 -> SSH 隧道 -> KVM 宿主机 -> QEMU VNC -> 虚拟机 VGA/tty1
整个过程不要求运维电脑与虚拟机处于同一局域网,也不要求事先知道虚拟机 IP。
先分清 tty1 和 ttyS0
排查前最容易混淆的是两个终端:
| 终端 | 含义 | 常见接入方式 |
|---|---|---|
tty1 |
Linux 第一虚拟终端,显示在 VGA 屏幕上 | VNC、SPICE、物理显示器 |
ttyS0 |
第一串口终端 | virsh console <vm-name> |
virsh console 通常连接的是 ttyS0,不是 tty1。如果 guest 没有设置 console=ttyS0,或者没有启用 serial-getty@ttyS0.service,执行 virsh console 后可能只有空白画面。
需要查看 BIOS、启动日志、磁盘解密提示或图形化登录界面时,应使用 VNC 进入 VGA 控制台。
一、在宿主机确认虚拟机状态
先通过 SSH 登录 KVM 宿主机,然后进行只读检查:
virsh list --all
virsh dominfo <vm-name>
virsh domiflist <vm-name>
virsh net-list --all
virsh net-dhcp-leases default
如果虚拟机处于 shut off,没有 DHCP 租约是正常现象。QEMU 进程和 VNC 端口通常也只会在虚拟机运行时出现。
继续确认虚拟机是否配置了 VNC 和串口:
virsh dumpxml <vm-name> | sed -n '/<graphics/,/<\/graphics>/p'
virsh dumpxml <vm-name> | sed -n '/<serial/,/<\/serial>/p'
典型 VNC 配置如下:
<graphics type="vnc" port="5901" autoport="no" listen="127.0.0.1">
<listen type="address" address="127.0.0.1"/>
</graphics>
推荐只监听 127.0.0.1。VNC 协议本身通常不提供足够的传输保护,直接监听公网或办公网地址会扩大攻击面。
二、获得授权后启动虚拟机
启动虚拟机会改变运行状态。在生产环境中,应先确认维护窗口、业务归属和磁盘占用情况。
virsh start <vm-name>
virsh domstate <vm-name>
virsh vncdisplay <vm-name>
virsh ttyconsole <vm-name>
假设 virsh vncdisplay 返回:
:1
通常对应 TCP 端口 5901,计算方式为 5900 + display number。仍应使用监听结果进行确认:
ss -ltnp | grep ':5901'
三、建立 SSH 本地端口转发
下面的命令把运维电脑的 127.0.0.1:5901 转发到宿主机的 127.0.0.1:5901:
ssh -N \
-L 5901:127.0.0.1:5901 \
-p <HOST_SSH_PORT> \
-i ~/.ssh/id_ed25519 \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
root@<HOST_ADDRESS>
参数含义:
-N:只建立隧道,不执行远程命令。-L:创建本地端口转发。ExitOnForwardFailure=yes:端口绑定失败时立即报错,避免留下一个看似正常但不可用的 SSH 会话。ServerAliveInterval与ServerAliveCountMax:及时发现链路断开。
如果宿主机必须经过标准 SSH 堡垒机访问,可以加入 -J:
ssh -N \
-J <BASTION_USER>@<BASTION_HOST>:<BASTION_PORT> \
-L 5901:127.0.0.1:5901 \
-p <HOST_SSH_PORT> \
-i ~/.ssh/id_ed25519 \
root@<HOST_ADDRESS>
如果使用 HTTP CONNECT 代理,可通过 OpenSSH 的 ProxyCommand 调用 corkscrew:
ssh -N \
-L 5901:127.0.0.1:5901 \
-p <HOST_SSH_PORT> \
-i ~/.ssh/id_ed25519 \
-o ExitOnForwardFailure=yes \
-o 'ProxyCommand=corkscrew <PROXY_HOST> <PROXY_PORT> %h %p' \
root@<HOST_ROUTE>
四、Windows 客户端
直接 SSH 或 SSH 堡垒机
Windows 10 和 Windows 11 可以安装系统自带的 OpenSSH Client。在 PowerShell 中执行:
ssh.exe -N `
-L 5901:127.0.0.1:5901 `
-p <HOST_SSH_PORT> `
-i "$env:USERPROFILE\.ssh\id_ed25519" `
-o ExitOnForwardFailure=yes `
root@<HOST_ADDRESS>
经过标准 SSH 堡垒机时:
ssh.exe -N `
-J <BASTION_USER>@<BASTION_HOST>:<BASTION_PORT> `
-L 5901:127.0.0.1:5901 `
-p <HOST_SSH_PORT> `
-i "$env:USERPROFILE\.ssh\id_ed25519" `
root@<HOST_ADDRESS>
检查本地端口:
Get-NetTCPConnection -LocalPort 5901 -State Listen
Test-NetConnection 127.0.0.1 -Port 5901
然后使用 TigerVNC Viewer、RealVNC Viewer 或 TightVNC Viewer 连接:
127.0.0.1:5901
HTTP CONNECT 场景
Windows 原生环境并不默认提供 corkscrew。更稳定的方式是在 WSL 中安装并运行:
sudo apt-get update
sudo apt-get install -y corkscrew
随后在 WSL 中执行前文的 ProxyCommand=corkscrew ... 隧道命令。私钥应放到 WSL 的 ~/.ssh/ 并设置权限:
chmod 600 ~/.ssh/id_ed25519
五、macOS 客户端
macOS 自带 OpenSSH 和屏幕共享客户端。需要 HTTP CONNECT 时,可以安装 corkscrew:
brew install corkscrew
建立隧道后,打开系统自带屏幕共享:
open 'vnc://127.0.0.1:5901'
也可以在 Finder 中选择“前往 -> 连接服务器”,输入:
vnc://127.0.0.1:5901
六、Linux 客户端
Debian 或 Ubuntu:
sudo apt-get update
sudo apt-get install -y corkscrew tigervnc-viewer
Fedora 或 RHEL 系发行版的软件包名称可能略有不同:
sudo dnf install -y corkscrew tigervnc
建立 SSH 隧道后连接:
vncviewer 127.0.0.1:5901
桌面环境也可以使用 Remmina,新建 VNC 连接并填写 127.0.0.1:5901。
七、如何验证隧道真的可用
macOS 和 Linux 可以直接读取 VNC 协议头:
nc -w 3 127.0.0.1 5901 | head -c 32
正常结果以 RFB 开头,例如:
RFB 003.008
Windows 可以使用:
Test-NetConnection 127.0.0.1 -Port 5901
TCP 测试成功只能证明端口可达。最终仍应使用 VNC Viewer 确认可以看到虚拟机画面。
八、常见故障及判断边界
| 现象 | 故障位置 | 建议检查 |
|---|---|---|
| SSH 无法登录宿主机 | SSH、代理或路由层 | ssh -vv、代理连通性、密钥权限 |
| 本地 5901 没有监听 | 隧道建立失败或端口冲突 | SSH 输出、ExitOnForwardFailure |
| VNC 连接被拒绝 | QEMU 没有监听目标端口 | VM 状态、virsh vncdisplay、ss |
| VNC 连接后黑屏 | guest 未启动完成或停在固件/解密阶段 | 按 Enter、等待启动、检查 QEMU 日志 |
| guest 没有 DHCP 租约 | guest 网络尚未工作 | 继续在 tty1 检查网卡和网络服务 |
virsh console 空白 |
guest 未启用 ttyS0 输出 | 改走 VNC;维护后配置串口控制台 |
本地端口已被占用
可以把本地端口改成 15901,远端端口仍保持 5901:
ssh -N -L 15901:127.0.0.1:5901 root@<HOST_ADDRESS>
然后让 VNC Viewer 连接:
127.0.0.1:15901
九、安全注意事项
- 不要把无密码 VNC 直接暴露到公网或不受信任网络。
- 优先让 QEMU VNC 只监听宿主机
127.0.0.1。 - 使用 SSH 隧道提供传输加密和身份认证。
- 不要在脚本、工单、截图或文章中泄露真实私钥、内部域名、节点标识和网络拓扑。
- 启动、强制关机、修改 libvirt XML 或挂载 guest 磁盘前,应取得变更授权。
- 完成操作后关闭 VNC Viewer,并在隧道终端按
Ctrl+C。
十、结语
当虚拟机网络失效时,控制面应与数据面分离。guest IP、路由和 SSH 都属于虚拟机内部网络路径;VNC 和串口则由宿主机提供,是处理启动期故障的重要兜底能力。
实际运维中,建议同时配置两条路径:
- VNC 或 SPICE,用于 VGA、
tty1、固件和磁盘解密界面。 - serial console,用于纯终端环境和自动化接管。
配合只监听 loopback 的控制台、SSH 隧道和清晰的变更门禁,即使不知道虚拟机 IP,也能安全、可审计地恢复控制。