服务器A无公网IP但能访问外网,服务器B有公网IP。目标:通过 SSH 到服务器B的 2222 端口即可访问服务器A,同时限制服务器A只能建立反向隧道,不能执行任何命令。
sudo useradd -m -s /bin/false tunnel-user
sudo passwd -l tunnel-user # 禁用密码登录ssh-keygen -t ed25519 -f ~/.ssh/tunnel_key -N "" -C "reverse-tunnel-to-serverB"在服务器A上查看公钥:
cat ~/.ssh/tunnel_key.pub在服务器B上配置(以 tunnel-user 身份):
sudo mkdir -p /home/tunnel-user/.ssh
sudo sh -c 'echo "command=\"/bin/false\",no-agent-forwarding,no-X11-forwarding,no-pty <粘贴服务器A的公钥内容>" > /home/tunnel-user/.ssh/authorized_keys'
sudo chown -R tunnel-user:tunnel-user /home/tunnel-user/.ssh
sudo chmod 700 /home/tunnel-user/.ssh
sudo chmod 600 /home/tunnel-user/.ssh/authorized_keys编辑 /etc/ssh/sshd_config,在末尾添加:
GatewayPorts clientspecified
Match User tunnel-user
AllowTcpForwarding remote
PermitOpen none
X11Forwarding no
AllowAgentForwarding no
ForceCommand /bin/false
重启 sshd:
sudo systemctl restart sshd# Debian/Ubuntu
sudo apt install -y autossh
# CentOS/RHEL
sudo yum install -y autosshssh -i ~/.ssh/tunnel_key -o StrictHostKeyChecking=accept-new tunnel-user@serverB连接会立即断开(因为 ForceCommand /bin/false),这是正常的,目的是将服务器B的主机密钥写入 known_hosts。
autossh -M 0 -R 0.0.0.0:2222:localhost:22 -N \
-i ~/.ssh/tunnel_key \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
tunnel-user@serverB-M 0 表示禁用 autossh 自带的监控端口,改用 SSH 自身的 ServerAlive 机制检测连接状态。连接断开后 autossh 会自动重连。
0.0.0.0:2222 让隧道绑定到服务器B的所有网络接口,这样才能从外部访问。如果省略则默认绑定 127.0.0.1,只能从服务器B本地访问。
在服务器A上创建 /etc/systemd/system/ssh-reverse-tunnel.service:
[Unit]
Description=SSH Reverse Tunnel to serverB
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -R 0.0.0.0:2222:localhost:22 -N \
-i /root/.ssh/tunnel_key \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
tunnel-user@serverB
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target启用服务:
sudo systemctl daemon-reload
sudo systemctl enable --now ssh-reverse-tunnelAUTOSSH_GATETIME=0 让 autossh 在首次连接失败时也会重试,适合开机时网络尚未就绪的场景。
从任意机器执行:
ssh -p 2222 <serverA的用户名>@serverB验证限制是否生效(以下命令应失败):
ssh -i ~/.ssh/tunnel_key tunnel-user@serverB # 应无法获得shell
ssh -L 8080:localhost:80 -i ~/.ssh/tunnel_key tunnel-user@serverB # 正向转发应被拒绝