我有两台运行 OpenBSD v6.9 的机器。我们不妨先简单介绍一下,将它们称为客户端和服务器。
我使用以下命令在服务器上生成了 SSHFP 记录:
ssh-keygen -r host.domain.tld
在 DNS 区域中,我使用以下行添加了 SSHFP 记录:
host IN SSHFP 1 2 02323a6fb8a12eba9288930ce2513fc94970e5575996ebede1ee352bd039c531
host IN SSHFP 4 2 3eef5929fe85038b2e2a7de70897175e7b0d68328b41ef6466a2fa06f9a8bb49
sshd 配置为仅使用 Ed25519 主机密钥,但我还保留 RSA 主机密钥以备将来需要。
DNS 区域使用 DNSSEC 签名,所有内容均通过“https://dnsviz.net/”和“https://dnssec-debugger.verisignlabs.com/”验证。
在客户端上,有一个带有 DNSSEC 验证器的 Unbound 解析器:
dig host.domain.tld -t SSHFP
; <<>> dig 9.10.8-P1 <<>> host.domain.tld -t SSHFP
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9525
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;host.domain.tld. IN SSHFP
;; ANSWER SECTION:
host.domain.tld. 3600 IN SSHFP 4 2 3eef5929fe85038b2e2a7de70897175e7b0d68328b41ef6466a2fa06 f9a8bb49
host.domain.tld. 3600 IN SSHFP 1 2 02323a6fb8a12eba9288930ce2513fc94970e5575996ebede1ee352b d039c531
;; Query time: 182 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon May 17 17:15:46 CEST 2021
;; MSG SIZE rcvd: 137
上面没有提到,但如果我将 +dnssec 添加到查询中,则会显示 RRSIG SSHFP。
SSH 客户端配置文件:
Host host.domain.tld
AddressFamily inet
HostName host.domain.tld
Port 22
Protocol 2
User username
CheckHostIP yes
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
VerifyHostKeyDNS yes
IdentityFile ~/.ssh/id_ed25519_host.domain.tld
但是,当我尝试从客户端连接到服务器时,SSHFP 似乎不起作用:
ssh host.domain.tld
The authenticity of host 'host.domain.tld (server_ip_address)' can't be established.
ED25519 key fingerprint is SHA256:Pu9ZKf6FA4suKn3nCJcXXnsNaDKLQe9kZqL6Bvmou0k.
Matching host key fingerprint found in DNS.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
根据互联网指南,我不应该被要求接受密钥。
总结一下:
- 服务器上生成的 SSHFP 记录,
- SSHFP 记录已添加到 DNS 区域,
- 使用 DNSSEC 签名的 DNS 区域,
- 客户端能够查询 SSHFP 记录并验证 DNSSEC,
- 带有选项“VerifyHostKeyDNS”的 SSH 配置文件,
- 而且它不起作用。
以下是连接的详细信息:
ssh -v host.domain.tld
OpenSSH_8.6, LibreSSL 3.3.2
debug1: Reading configuration data /home/username/.ssh/config
debug1: /home/username/.ssh/config line 2: Applying options for host.domain.tld
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Connecting to host.domain.tld [server_ip_address] port 22.
debug1: Connection established.
debug1: identity file /home/username/.ssh/id_ed25519_host.domain.tld type 3
debug1: identity file /home/username/.ssh/id_ed25519_host.domain.tld-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_8.6
debug1: Remote protocol version 2.0, remote software version OpenSSH_8.6
debug1: compat_banner: match: OpenSSH_8.6 pat OpenSSH* compat 0x04000000
debug1: Authenticating to host.domain.tld:22 as 'username'
debug1: load_hostkeys: fopen /home/username/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: [email protected]
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: [email protected] MAC: <implicit> compression: none
debug1: kex: client->server cipher: [email protected] MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: SSH2_MSG_KEX_ECDH_REPLY received
debug1: Server host key: ssh-ed25519 SHA256:Pu9ZKf6FA4suKn3nCJcXXnsNaDKLQe9kZqL6Bvmou0k
debug1: found 2 insecure fingerprints in DNS
debug1: matching host key fingerprint found in DNS
debug1: load_hostkeys: fopen /home/username/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /home/username/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: hostkeys_find_by_key_hostfile: hostkeys file /home/username/.ssh/known_hosts2 does not exist
debug1: hostkeys_find_by_key_hostfile: hostkeys file /etc/ssh/ssh_known_hosts does not exist
debug1: hostkeys_find_by_key_hostfile: hostkeys file /etc/ssh/ssh_known_hosts2 does not exist
The authenticity of host 'host.domain.tld (server_ip_address)' can't be established.
ED25519 key fingerprint is SHA256:Pu9ZKf6FA4suKn3nCJcXXnsNaDKLQe9kZqL6Bvmou0k.
Matching host key fingerprint found in DNS.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
你知道这个问题吗?
提前致谢。
答案1
OpenSSH v6.9 非常非常老了。最新的是 OpenSSH v9.0。我查看了dns.c
OpenSSH 存储库的 V_6_9 分支。调试输出中要注意的关键事项是:
found 2 insecure fingerprints in DNS
意味着 DNSSEC 查找host.domain.tld
不是有效的 DNS 记录(完全不安全),尽管通常会找到两个 (2) 个不同的SSHFP
资源记录(无论是否安全)。
这也意味着delv host.domain.tld.
不会在第一行输出预期的消息:
$ delv host.domain.tld.
; fully validated
...
虽然 DNSSEC 故障排除的范围很广,但在本文中很难找到解决方案。
我建议您将您的信息插入domain.tld
两个出色的在线 DNSSEC 疑难解答程序之一,然后从那里开始:
当然,作为最后的手段,[dnssec]
标签在 ServerFault(另一个 StackExchange 组)和您最喜欢的搜索引擎上,但更快地聘请内部 DNSSEC 专家来满足您的需求。
安全考虑
当然,您确实已经启动并运行了 DNSSEC?
只有通过 DNSSEC 正确签名的资源记录才能保证对 SSHFP 的保护,但是,但是...但是大多数人(和许多 ISP)不会指示他们的 DNS 解析器仅返回由 DNSSEC 确定的有效 DNS 记录;尽管如此,SSHFP 被劫持的可能性仍然存在。
这里的问题不是公钥的保密性(它并不保密)。
我们遇到的问题是保存此公钥的 DNS 记录的完整性。虽然问题陈述中的 SSHFP 密钥数据是正确的,但错误更多的是“缺乏清晰度”,但逻辑仍然是正确的。
这就是这篇原创问题帖子的结尾。但我将概述一些其他安全注意事项。
更多注意事项
如果没有安全地分发(通过 DNSSEC),此类 SSHFP DNS 记录很容易被篡改或被其他密钥替换。因此,在信任和使用之前必须“强制”执行 DNSSEC SSHFP
。
如果伪造的 SSH CA 服务器配置了错误的公钥(其私钥在其他人手中),客户端最终会信任其他人。
警告:不当使用 SSHFP 记录可能会产生严重的安全后果;请遵循以下规则以避免造成安全漏洞:
- 请勿在不受 DNSSEC 保护的区域中创建 SSHFP 记录。
- 切勿将 SSH 客户端配置为对未受 DNSSEC 保护的域使用 SSHFP。
- 切勿将 SSH 客户端配置为使用,
VerifyHostKeysDNS yes
除非当前解析器正确验证 DNSSEC 批准的查询或使用有效的支持 DNSSEC 的解析器我使用简单delv domain.tld.
命令创建的列表是为了测试其 DNSSEC 功能。 - 一定要确保您的 SSH 客户端直接使用的所有 DNS 解析器都具有“可访问的 DNSSEC”;这意味着要在每个 ISP 上测试您的移动笔记本电脑以进行该
; fully validated
检查。 - 最重要的是,始终执行
delv ssh.domain.tld.
并检查第一行输出; fully validated
或通过 DNSSEC 强制执行/etc/resolv.conf
(稍后会详细介绍)。
不遵守这些规则可能会让对手为他们的 SSH 服务器创建欺骗性的 SSHFP 记录来冒充您的服务器,从而使与服务器的 SSH 连接不安全并容易受到攻击。
您可以通过让主机库在解析时执行此操作(通过 resolver(3) 而不是 gethostbyname(3) 函数调用)来避免“始终执行” delv ssh.domain.tld
。如果您必须要求所有 DNS 需求都经过绝对 DNSSEC 验证的 DNS 查询,则可以将以下内容插入到您的(仅当您拥有 glibc v2.38 或更高版本时):; fully validated
glibc
/etc/resolv.conf
文件:/etc/resolv.conf
.
.
.
options edns0
options trust-ad
options ndots:2
.
.
.
search domain.tld
nameserver 192.168.1.1
一旦获得 DNSSEC 保护,就需要执行几个步骤来排除SSHFP
DNS 记录和 SSH 客户端机制的故障。
验证 SSHFP 设置
列出的七 (7) 个步骤的顺序是这样的(在前一步完成之前不要进行下一步)。
Ping
host.domain.tld
,得到 ICMP 响应。执行
delv host.domain.tld.
并确保第一行输出显示;请注意在主机名末尾添加的; fully validated
句点 ( )。.
正确
EDNS(0)
使用这个EDNS(0) 在线测试器针对您有问题的服务器。从您相关的 SSH 服务器获取 SSH 指纹以开始比较;执行
ssh-keyscan -D host.domain.tld.
(注意-D
选项)。
$ ssh-keyscan -D host.domain.tld.
输出:
host.domain.tld IN SSHFP 4 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
host.domain.tld IN SSHFP 4 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- 从在其区域数据库文件中保存 SSHFP 的权威名称服务器的 DNS 查询中,
SSHFP
使用 获取记录delv host.domain.tld. SSHFP
。
$ delv host.domain.tld. sshfp
生成以下输出:
; fully validated
host.domain.tld. 86400 IN SSHFP 1 1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
host.domain.tld. 86400 IN SSHFP 1 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXX
host.domain.tld. 86400 IN SSHFP 3 1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
host.domain.tld. 86400 IN SSHFP 3 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXX
host.domain.tld. 86400 IN SSHFP 4 1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
host.domain.tld. 86400 IN SSHFP 4 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXX
- 将实际的 SSH 指纹与
SSHFP
DNS 记录的内容进行比较。算法(1-6)和哈希 ID(1 或 2)也必须匹配。
必须匹配的算法ID到名称为:1=RSA,2=DSA,3=ECDSA,4=Ed25519,6=Ed448。
必须匹配的名称的哈希 ID 为:1=SHA1、2=SHA256。
- 尝试
ssh -oVerifyHostKeyDNS=yes -oStrictHostKeyChecking=yes host.domain.tld.
如果此步骤有效,请将两个选项集成到您的 SSH 客户端配置文件中。
如果指纹不匹配,则始终是 DNS 记录需要更新。
出现警告
使用较新的 OpenSSH,您甚至可能会在初始 SSH 登录期间收到以下令人讨厌的提示:
ssh host.domain.tld
可能输出以下内容:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx.
Please contact your system administrator.
Update the SSHFP RR in DNS with the new host key to get rid of this message.
Warning: Permanently added '[host.domain.tld.]:22,[999.999.999.999]:22' (ED25519) to the list of known hosts.
</etc/motd content goes here>
[email protected].'s password:
上述输出中要注意的关键是以下一行:
Update the SSHFP RR in DNS with the new host key to get rid of this message.
它的意思就是它的意思。
本地 SSH 客户端配置VerifyHostKeyDNS yes
中的 控制着该输出;配置可以是 在$USER/.ssh/config
或在 中/etc/ssh/ssh_config
。
上述决定基于以下之间的简单比较:
- 持有
SSHFP
此区域数据的权威名称服务器host.domain.tld
host.domain.tld
提供公钥的SSH 服务器
此消息涵盖了以下 99.999% 的潜在情景。
sshd
没有使用相同的公钥,可能是因为:- 通过以下方式生成了不同的公钥
ssh-keygen
(最常见) - 一组不同的配置文件(通过
sshd -c <config_file>
) AuthorizedKeyFile
已经改变- 指纹哈希(
FingerprintHash
)设置已更改 PubkeyAcceptedKeyTypes
已经改变CASignatureAlgorithms
已经改变TrustedCAUserKeys
已经改变
- 通过以下方式生成了不同的公钥
其余 0.001% 的时间则出现在许多不太常见的场景中:
- 指向不同的 SSH 服务器
- 已打开新端口号(合法或非法)。如果您的
ssh_config
/$HOME/.ssh/config
使用了不同的端口号,则可能会发生这种情况(这种情况很少见,但确实发生过恶意事件) - 一个(不同的?)Dockerized 容器重复使用相同的 IP 地址
- MAC 地址已更改为指向克隆服务器
- IP 过滤器(或 nftables)重新路由了你常用的 SSH 端口
- IP 路由已更改(通过路由表或各种 IP 路由协议,如 BGP、OSPF、RIP)
- 出现了一个幽灵守护进程,使得您的服务器的 rouge
sshd
守护进程通过 SysV init 启动(service XXXX start
或systemd start XXXX.service
或system start XXXX.timer
或偷偷通过 crond/atd 作业或甚至rc.local
)并使用其自己的 SSH 服务器公钥。
- 已打开新端口号(合法或非法)。如果您的
- 指向不同的
sshd
可执行文件。这需要使用软件包工具(您的发行版)进行文件验证,以确保其校验和值、修改时间、文件路径、安全上下文以及sshd
可执行文件及其引用库的属性(请参阅ldd/usr/sbin/sshd
)均已正确计算和设置。
无论如何,一旦你消除了剩下的 0.001%,这将是一个简单的问题:
- 生成新的
SSHFP
DNS 记录(ssh-keyscan -r host.domain.tld
);将输出保存到编辑器缓冲区 - 识别权威名称服务器
domain.tld
(dig domain.tld. soa
并注意MNAME
SOA 记录数据的部分)。 - 以 root 身份登录到该
MNAME
权威名称服务器host.domain.tld
- 转到名称服务器的正确配置文件(ISC Bind9 是
/etc/named.conf
;较新的 Bind v9.11+ 通过显示默认配置named -V
) - 记下区域的数据库文件名
domain.tld
。(ISC Bind9 是file /var/lib/bind/db.domain.tld
) - 将目录更改为该区域数据库文件所在的位置
- 编辑
db.domain.tld
文件。 SSHFP
从第一步保存的编辑器缓冲区中粘贴新的DNS 记录。
可能需要将句点 ( .
) 符号直接放在每个数据记录的主机名末尾SSHFP
(这是在关闭该区域数据库文件之前生成的ssh-keyscan
;例如:
; note the extra ending period '.' after hostnames
host.domain.tld. IN SSHFP 4 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
host.domain.tld. IN SSHFP 4 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
享受!
来源: