我有 3 个主机:
- 我的电脑。
- 具有静态 IP 的跳转主机 (又名堡垒)。
- 具有动态 IP 的服务器;位于 NAT 路由器/防火墙后面,没有打开任何入站端口。
服务器当前连接到 Jump Host,并通过 建立 SSH 隧道-R "0:localhost:22"
,因此端口由 Jump Host 动态分配(到目前为止这非常可靠)。
和一些插座魔法,我已将动态分配的端口号记录在 Jump Host 上的一个文件中。
这样,我就可以通过 SSH 连接到 Jump Host,然后运行ssh -p $(cat /path/to/port-file) localhost
但是否可以跳过这个额外的步骤?
这对于 Ansible 很有用,我inventory.yml
需要更新端口号:
server:
# /usr/bin/ssh jh cat /path/to/port-file
ansible_port: "34625"
ansible_host: "localhost"
ansible_ssh_common_args: "-o ProxyCommand='ssh -q -W %h:%p jh' -o StrictHostKeyChecking=no"
也许可以使用ProxyCommand
:
它可以(某种程度上)在我的计算机~/.ssh/config
文件上使用命令替换:
Host server
ProxyCommand ssh -q -W localhost:$(echo "34625") jh
这是可行的(即使没有echo -n
),替换似乎发生在我的本地计算机上。
尽管使用 SSH 先获取端口号效率很低,但这是行不通的:
Host server
ProxyCommand ssh -q -W localhost:$(ssh jh cat /path/to/port-file) jh
其结果是:
Bad packet length 1349676916.
ssh_dispatch_run_fatal: Connection to UNKNOWN port 65535: message authentication code incorrect
奇怪的是,服务器(在链的末端)确实在其身份验证日志中注意到了这一点:
sshd[5558]: Bad protocol version identification '' from ::1 port 36048
这意味着端口号正在被返回。但不知道为什么它会在此时中断。
并ssh -vvv
显示它识别了我的中的主机密钥~/.ssh/known_hosts
。
我还尝试在跳转主机上创建自定义“ssh_config”文件,使用ssh -F /path/to/ssh_config
,内容如下:
Host tunnel.server
HostName localhost
Port 34625
但我不认为我能将它与 一起使用ProxyCommand
。
我还怀疑StrictHostKeyChecking=no
当端口号改变时可能需要在某个时候引入。
答案1
第二个部分解决方案,受到@anx 的启发......
创建套接字文件
ssh -R '/path/to/socket-file:localhost:22' tunnel@jh
然后,为了使用这个套接字(来自 Jump Host),我可以使用socat
:
ssh -o "ProxyCommand socat - UNIX-CLIENT:/path/to/socket-file" localhost
使用socat
似乎是一个不必要的步骤,我确信一定有办法让命令ssh
直接使用套接字文件,但我还找不到它。
我还没有找到如何从我的电脑上使用这个套接字文件(因为 ProxyCommand 在本地主机上运行,而不是在 JumpHost 上运行)。
我还应该注意;由于tunnel
帐户(在 Jump Host 上)受到严格限制(仅用于建立这些隧道连接),我需要进行设置,StreamLocalBindMask=0111
以便 Jump Host 上的帐户可以使用此套接字文件。同样,如果通过 建立了新连接,则应删除旧套接字文件StreamLocalBindUnlink=yes
。
这两个选项都需要在跳转主机的“/etc/ssh/sshd_config”中设置:
Match User tunnel
StreamLocalBindMask 0111
StreamLocalBindUnlink yes
不幸的是,Match
在 2020 年 9 月 27 日发布的 OpenSSH 8.4 之前,“/etc/ssh/sshd_config.d/tunnel.conf”中的规则被忽略了(错误报告),此功能目前在 Ubuntu 20.04.1 LTS 上不可用。
答案2
临时解决方案,并不理想......
在我的计算机上添加一个每小时一次的 cron 作业,它从 Jump Host 收集端口号,并创建一个新~/.ssh/config_tunnels
文件。
#!/bin/bash
set -u;
config_path="${HOME}/.ssh/config_tunnels";
port=$(/bin/ssh jh /bin/cat /path/to/port-file 2> /dev/null | /bin/sed 's/[^0-9]//g');
if [[ "${port}" -lt 1024 ]]; then
exit; # Probably a blank/zero value (e.g. connection issue).
fi
{
echo "";
echo "Host server";
echo " ProxyCommand ssh -q -W localhost:${port} jh";
echo "";
} > "${config_path}";
chown user:group "${config_path}";
chmod 600 "${config_path}";
然后主~/.ssh/config
文件就可以使用Include ~/.ssh/config_tunnels
。
注意,我使用是sed
为了确保我能得到一个返回的数字。如果我的 Jump Host 被攻陷,我不希望端口文件包含一些额外的(恶意的)SSH 配置。