我有一台位于防火墙后面的机器。我使用 VPN 隧道通过 ssh 进行端口转发来远程连接到它。为了连接到机器,我使用 VPN 的外部 IP 以及我个人和临时分配的端口。我使用的命令是:
ssh USER@VPN_IP -p PORT
由于VPN_IP
和PORT
经常更改,我无法利用保存主机密钥来known_host
摆脱中间人攻击,但同时我非常了解主机密钥,我可以将其提供给 ssh 以便将其用于当前的VPN_IP
和PORT
组合。这可能吗?怎么做?
答案1
该known_hosts
文件用于提供这些密钥,没有直接的命令行替代方案(而且无论如何它都不太方便)。但是,使用该known_hosts
文件完全可以实现您的目标!
阅读man sshd
ssh_known_hosts
文件格式。
执行主机身份验证时,如果任何匹配的行具有正确的密钥,则身份验证被接受;要么是完全匹配的密钥,要么是签署该证书的证书颁发机构的密钥(如果服务器已出示证书进行身份验证)。
可以在~/.ssh/known_hosts
(和/etc/ssh/ssh_known_hosts
) 中使用通配符:
这些文件中的每一行包含以下字段:标记(可选)、主机名、密钥类型、base64 编码密钥、注释。字段之间用空格分隔。
主机名是一个以逗号分隔的模式列表(
*
和?
充当通配符);每个模式依次与规范主机名(在验证客户端时)或用户提供的名称(在验证服务器时)进行匹配。模式前面还可以加上!
表示否定的符号:如果主机名与否定模式匹配,则即使它与行上的另一个模式匹配,也不会被接受(该行)。主机名或地址可以选择用括号括起来[
,]
然后跟上“:”和非标准端口号。
可以将密钥信任给
网络范围(如果已知),例如
TEST-NET-2
:198.51.100.* ssh-rsa AAAAB3Nza...2iQ==
TEST-NET
使用逗号分隔列表的多个范围(例如全部):192.0.2.*,198.51.100.*,203.0.113.* ssh-rsa AAAAB3Nza...2iQ==
或者甚至在任何地方连接时:
* ssh-rsa AAAAB3Nza...2iQ==
如果此密钥不存在,它仍会警告您其他密钥的真实性,显示指纹并在您回答时自动添加yes
。比较是逐行进行的。
答案2
使用 Esa Jokinen 的有用答案并自动化该过程,我的解决方案是:
#!/bin/sh
previous_IP_OR_known_host_key_line=$1
current_IP=$2
user=$3
port=$4
#random extension to avoid collision by multiple script execution
temp_file="/tmp/temp_host_file.$(hexdump -n 2 -e '/2 "%u"' /dev/urandom)"
if [ "$(echo "$previous_IP_OR_known_host_key_line"|wc -w)" -gt 1 ]; then
echo "$previous_IP_OR_known_host_key_line"|sed "s/^[^ ]*/$current_IP/">"$temp_file"
else
ssh-keygen -F "$previous_IP_OR_known_host_key_line"|grep -v "^#"|sed "s/^[^ ]*/$current_IP/">"$temp_file"
fi
ssh "$user"@"$current_IP" -p "$port" -o UserKnownHostsFile="$temp_file"
rm "$temp_file"
该脚本生成一个临时的known_host文件提供给ssh。该脚本需要以下参数(按顺序):
- 机器先前接受的连接所具有的目标 IP 或带有来自 known_host 文件的密钥的相应 RAW 行。
-当前 IP(在 VPN/防火墙后面)。
-用户。
-港口。