背景

这其实是2021年的事情了,当时是要通过 SFTP 从另一个厂商的服务器下载数据,那个服务器的 IP 是浮动 IP,主备机共用一个 IP ,当主机发生故障的时候,IP 就会浮动到备机上。

问题来了,SFTP 是通过 SSH 通道连接,连接的时候默认会验证主机指纹,连接的远程机器变化了主机指纹也不一样了,导致 SSH 客户端认为你受到了中间人攻击:

1
2
3
4
5
6
7
8
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ 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
24:48:c9:a3:de:04:57:9f:f8:a5:ab:28:e0:d6:1a:bb.

解决思路

清理主机指纹缓存

作为经常玩 vps 的 mjj ,可能大家也有这样的经历,买的 vps ,重装了系统后, ssh 连接就会报错,因为现在主机指纹和你之前连接的主机指纹不一样了,这时候只需要清理 ~/.ssh/known_hosts 中对应行,再次 SSH 连接就可以了。

或者是终端会提示你运行以下命令,效果也是一样的 (前提是你在当前用户的家目录中)

1
ssh-keygen -f ".ssh/known_hosts" -R <IP>

选项含义解释

1
2
3
4
5
6
-f filename
Specifies the filename of the key file.

-R hostname | [hostname]:port
Removes all keys belonging to the specified hostname (with optional port number) from a known_hosts file. This
option is useful to delete hashed hosts (see the -H option above).

可以在每次 SSH 连接前清理一次,也可以 grep 关键词/查看 SSH 状态码为失败/expect,反正就是怎么样去检测一下指纹发生了改变,再去清理。

使用选项忽略指纹校验(推荐)

但是我还是不满足,我觉得这不是最优雅的方法,是否能有选项使 SSH 客户端仅使用密码验证。因为 sshd (ssh 服务端)都有选项来指定是否禁用密码验证(PasswordAuthentication)。

然后,还真被我找到了。

参考
https://unix.stackexchange.com/questions/15138/how-to-force-ssh-client-to-use-only-password-auth
https://serverfault.com/questions/559885/temporarily-ignore-my-ssh-known-hosts-file
https://linux.livejournal.com/1884229.html

最后命令如下

1
sshpass -p password sftp -o PreferredAuthentications=password -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null user@ip

解释:

  • sshpass 用于非交互式时,给 sftp 传送密码

  • -o PreferredAuthentications=password 优先使用密码验证

  • -o PubkeyAuthentication=no 不要使用公钥验证

  • -o StrictHostKeyChecking=no 不要检查主机的指纹

  • -o UserKnownHostsFile=/dev/null 不要使用 known_hosts 文件

最后尝试下来,得把这些选项都安排上才能忽略

使用配置文件忽略指纹校验(不推荐,不便于维护)

后来一次升级中,换成了用 lftp (另一种客户端,支持 sftp/ftp)拉取文件,似乎是因为 lftp 支持 mget (同时拉取多个文件)

使用-o选项来忽略就不起作用了,看了下 lftp 的源码,核心也是套用的 sftp,但是我是没找到办法把 -o 这些选项传给 sftp

但是 sftp/ssh 默认是会读取 ssh_config 的,这个不用显性指定(经验)

于是在 ~/.ssh/config 写了个配置

~/.ssh/config

1
2
3
4
5
Host 10.239.*.*
PreferredAuthentications password
PubkeyAuthentication no
StrictHostKeyChecking no
UserKnownHostsFile /dev/null

最后文件权限 600

1
chmod 600 ~/.ssh/config

这样最后也实现了目的,但是我觉得这样配置比较恶心,不便于维护。如果后续这个拉取客户端换到别的服务器上了,以后维护的人可能不会知道 ~/.ssh/config 里面还有配置.

ssh option 附录

这里是 ssh 的 所有 option,有兴趣可以再花时间研究下,看看还支持什么样的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
ssh option
-o ssh_option
Can be used to pass options to ssh in the format used in ssh_config(5). This is useful for specifying options for which there is no separate sftp command-line flag.
For example, to specify an alternate port use: sftp -oPort=24. For full details of the options listed below, and their possible values, see ssh_config(5).

AddressFamily
BatchModev
BindAddress
BindInterface
CanonicalDomains
CanonicalizeFallbackLocal
CanonicalizeHostname
CanonicalizeMaxDots
CanonicalizePermittedCNAMEs
CASignatureAlgorithms
CertificateFile
ChallengeResponseAuthentication
CheckHostIP
Ciphers
Compression
ConnectionAttempts
ConnectTimeout
ControlMaster
ControlPath
ControlPersist
GlobalKnownHostsFile
GSSAPIAuthentication
GSSAPIDelegateCredentials
HashKnownHosts
Host
HostbasedAuthentication
HostbasedKeyTypes
HostKeyAlgorithms
HostKeyAlias
Hostname
IdentitiesOnly
IdentityAgent
IdentityFile
IPQoS
KbdInteractiveAuthentication
KbdInteractiveDevices
KexAlgorithms
LogLevel
MACs
NoHostAuthenticationForLocalhost
NumberOfPasswordPrompts
PasswordAuthentication
PKCS11Provider
Port
PreferredAuthentications
ProxyCommand
ProxyJump
PubkeyAcceptedKeyTypes
PubkeyAuthentication
RekeyLimit
SendEnv
ServerAliveInterval
ServerAliveCountMax
SetEnv
StrictHostKeyChecking
TCPKeepAlive
UpdateHostKeys
User
UserKnownHostsFile
VerifyHostKeyDNS

ssh 登录出现 Are you sure you want to continue connecting (yes/no)? 解决方法

如果在脚本里面,没法交互式输入,可以使用ssh -o 的参数进行设置

1
ssh -o StrictHostKeyChecking=no [email protected]

再多点思考,指纹是怎么来的

后来我思考,这个指纹是怎么生成的呢,linux 一切皆文件,这个指纹一定是放在某个地方的吧

指纹信息如何生成
https://superuser.com/questions/421997/what-is-a-ssh-key-fingerprint-and-how-is-it-generated

里面提到是这个文件 /etc/ssh/ssh_host_rsa_key.pub

但是我们的报错是这样的

1
2
3
4
5
6
7
8
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ 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
24:48:c9:a3:de:04:57:9f:f8:a5:ab:28:e0:d6:1a:bb.

注意: ED25519 key

实际用的是 /etc/ssh/ssh_host_ed25519_key.pub 这个文件

使用 ssh-keygen 计算指纹

1
2
ssh-keygen -E md5 -lf /etc/ssh/ssh_host_ed25519_key.pub 
256 MD5:24:48:c9:a3:de:04:57:9f:f8:a5:ab:28:e0:d6:1a:bb no comment (ED25519)

可以看到和报错信息是一样了吧