使用WireGuard隧道连接ssh和mysql
使用 WireGuard隧道连接 SSH 和 MySQL
服务器 Debian 12 / 内核 6.1 / MySQL 8.0 / Node 24,客户端 Windows 11。
做完之后公网只剩 80、443 和一个 UDP 端口,SSH 和数据库都只在内网地址上出现,客户端换 IP 也不用动安全组。
起因
服务器 Debian 12 / 内核 6.1 / MySQL 8.0 / Node 24,客户端 Windows 11。
做完之后公网只剩 80、443 和一个 UDP 端口,SSH 和数据库都只在内网地址上出现,客户端换 IP 也不用动安全组。
需求很小:本机的 Navicat 想连服务器的 MySQL。
把 3306 直接开放出去肯定不行,公网上的 MySQL 容易被扫到,不安全。退一步只开 22 走 SSH 隧道,比 3306 强,但 SSH 也是爆破重点;更麻烦的是家里和公司的出口 IP 会变,安全组按网段放行根本维护不过来。
剩下的路子就是先把远程访问整体收进内网,公网只多一个 UDP 端口,能不能连不再取决于 IP,而是取决于有没有私钥。WireGuard 干这件事的成本比预想低:Linux 5.6 之后已经进内核,apt install wireguard 就能用,配置一共两个文件。
为什么是 WireGuard
它认证的是公钥,不是 IP。客户端换了出口地址,只要加密包还能发到服务器,握手时就会把新地址记下来(peer roaming),所以客户端不需要固定 IP,前面安全组的麻烦也就没了。
它跑在 UDP 上,认证不过的包直接丢弃,不返回任何错误,扫描器拿不到有效响应,看起来跟没开端口一样。开销是每个包多 60 字节左右的头部,按 1400 字节载荷算约 4%,SSH 和数据库查询这点量感觉不出来。
排查也省事,wg show 直接给出最近握手时间和收发字节数,状态一眼能看到。
服务端
安装:
apt update
apt install -y wireguard wireguard-tools
# 确认内核模块可用
modprobe wireguard && lsmod | grep wireguard
生成密钥:
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
wg genpsk > /etc/wireguard/client.psk # 可选,但建议加
/etc/wireguard/wg0.conf:
[Interface]
Address = 192.168.100.1/24
ListenPort = 48230
PrivateKey = <SERVER_PRIVATE_KEY>
[Peer]
# 客户端备注
PublicKey = <CLIENT_PUBLIC_KEY>
PresharedKey = <PRESHARED_KEY>
AllowedIPs = 192.168.100.2/32
ListenPort 别用默认的 51820,那个端口是扫描器的重点对象。服务端的 AllowedIPs 含义是「允许这个 peer 使用哪些源地址」,写 /32 就够。配置文件权限必须收紧:
chmod 600 /etc/wireguard/wg0.conf
启动并设成开机自启:
systemctl enable --now wg-quick@wg0
systemctl status wg-quick@wg0 --no-pager
wg show
wg show 里出现 listening port: 48230,就说明在监听了。
安全组这边只加一条入方向规则,以阿里云为例:协议 UDP,端口 48230,源 0.0.0.0/0。然后才是关 22,顺序别搞反:先确认 VNC 或云控制台能登进去,再关。隧道一旦出问题,那是唯一的入口。
Windows 客户端
装好 WireGuard后,新建隧道,内容如下:
[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 192.168.100.2/24
MTU = 1420
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <PRESHARED_KEY>
Endpoint = 203.0.113.10:48230
AllowedIPs = 192.168.100.0/24
PersistentKeepalive = 25
AllowedIPs 这里写 192.168.100.0/24,表示仅仅需要访问内网,而非全局代理。
PersistentKeepalive = 25 表示连接存活时间是25秒,不加的话空闲一段时间就得由客户端重新发起握手。
连上后验证一下:
ping 192.168.100.1
ssh root@192.168.100.1
Navicat:两个「主机」不是一回事
Navicat 的 SSH 隧道模式里有两个主机字段,视角完全不同,这是最容易填反的地方。SSH 选项卡的主机是从你的电脑出发去连的,填隧道对端 192.168.100.1:22;常规选项卡的主机是登进服务器之后从服务器本机出发去连的,填 127.0.0.1:3306。
MySQL 的 bind-address 是 127.0.0.1,只监听回环,所以常规页写服务器公网 IP 或 192.168.100.1 都连不上,只能写 127.0.0.1。
SSH 选项卡:
| 字段 | 值 |
|---|---|
| 主机 | 192.168.100.1 |
| 端口 | 22 |
| 用户名 | root |
| 认证方式 | 公钥 |
| 私钥文件 | 本机 ~/.ssh/id_ed25519 |
| 通行短语 | 私钥没有加密就留空 |
左右滑动查看完整表格
常规选项卡:
| 字段 | 值 |
|---|---|
| 主机 | 127.0.0.1 |
| 端口 | 3306 |
| 用户名 | dbuser |
| 密码 | <DB_PASSWORD> |
| 数据库 | litenote |
左右滑动查看完整表格
如果私钥是 OpenSSH 格式而 Navicat 提示无法读取,用 PuTTYgen 转成 .ppk,转换时通行短语同样留空(前提是原私钥本身没有加密)。
连不上时怎么查
先用命令行把同样的链路走一遍,把 Navicat 这个变量排除掉:
ssh -N -L 13306:127.0.0.1:3306 root@192.168.100.1
# 另开一个终端
mysql -h 127.0.0.1 -P 13306 -u dbuser -p litenote
几种常见现象:
| 现象 | 原因 |
|---|---|
| 隧道显示已连接,但 ping 不通 | Endpoint 被解析到 Fake-IP;或安全组没放行 UDP |
ssh: connect to host 192.168.100.1 port 22: Connection timed out |
隧道没起来,或 AllowedIPs 没包含 192.168.100.0/24 |
| SSH 能通,Navicat 报 2003 | 常规页主机填成了公网 IP 或 192.168.100.1,应填 127.0.0.1 |
| 提示私钥口令错误 | 私钥没有加密,通行短语留空就行,不要随手填一个 |
左右滑动查看完整表格
几种常见现象:
| 现象 | 原因 |
|---|---|
| 隧道显示已连接,但 ping 不通 | Endpoint 被解析到 Fake-IP;或安全组没放行 UDP |
| `ssh: connect to host 192.168.100.1 port 22: Connection timed out` | 隧道没起来,或 `AllowedIPs` 没包含 192.168.100.0/24 |
| SSH 能通,Navicat 报 2003 | 常规页主机填成了公网 IP 或 192.168.100.1,应填 `127.0.0.1` |
| 提示私钥口令错误 | 私钥没有加密,通行短语留空就行,不要随手填一个 |
