SSH 客户端 

Last Update: 2023-05-27

目录

基本用法

ssh 是典型的 C/S 架构的软件,最常见的用途是登录服务器,这要求服务器上必须运行 SSH 的守护进程 sshd。

ssh 登录服务器的命令如下:

  1. ssh <host>: <host> 可以是域名,IP 地址或局域网内部的主机名。
  2. ssh <user_name>@<host>: 指定登入主机的用户的名称,使用 @ 分隔。在不指定用户名的情况下,将使用客户端当前的用户名称作为登录远程服务器的用户名。
  3. ssh -l <user_name> <host>: 另一种指定用户名的方式。

ssh 默认连接服务器 22 端口,使用 -p 参数可以指定其他端口,比如 ssh -p 8821 lishouzhong.com 连接服务器 lishouzhong.com8821 端口。

连接流程

ssh 连接远程服务器后,启动验证流程:

  1. 如果首次连接该服务器 (找不到服务器指纹),确认是否连接。
  2. 如果该服务器有指纹留存,直接连接。

第一次连接服务器,命令行会显示一段文字:

The authenticity of host 'lishouzhong.com (10.12.161.193)' can't be established.
ECDSA key fingerprint is SHA256:C9Ix5mnGqC70au1W7H6AZX/PCqlMcfnyCOa6Z1ny7Rs.
Are you sure you want to continue connecting (yes/no)?

意思是 ssh 客户端没见过 lishouzhong.com 这台服务器的指纹,是否继续连接 (输入 yesno)。 服务器指纹 指 SSH 服务器公钥的哈希值。每台 SSH 服务器都唯一一对密钥用于跟客户端通信,其中公钥的哈希值可以用来识别服务器。

ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub 可以查看服务器采用 ecdsa 算法生成的公钥的指纹 (读取文件中的公钥,计算并输出其哈希值),其中:

ssh 会将本机连接过的所有服务器公钥的指纹,储存在本机 ~/.ssh/known_hosts 文件中。每次连接服务器时,ssh 客户端会把接收到的服务器指纹与 known_hosts 文件内已经记录的指纹进行比对,来判断服务器是否陌生 (陌生的公钥哈希值)。

在提示是否继续链接之后,输入 yes 并回车,就可以将当前服务器的指纹也储存在本机 ~/.ssh/known_hosts 文件中,下次连接不会再出现警告。同时,添加指纹后有下面的提示:

Warning: Permanently added 'lishouzhong.com' (ECDSA) to the list of known hosts.

最后,客户端与服务器建立连接,ssh 要求用户输入所要登录账户的密码,完成登录。

服务器密钥变更

SSH 公钥的指纹 (是对公钥做 sha256 得到的字符串) 可以用于识别主机,防止有人恶意冒充主机身份。如果服务器的密钥发生变更 (比如重装 SSH 服务),SSH 客户端再次发起连接时,就会检测到本地记录的服务器公钥指纹与本次连接收到的公钥指纹不匹配。这时,客户端就会中断连接,并显示一段警告信息:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    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 the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
77:a5:69:81:9b:eb:40:76:7b:13:04:a9:6c:f4:9c:5d.
Please contact your system administrator.
Add correct host key in /home/<username>/.ssh/known_hosts to get rid of this message.
Offending key in /home/<username>/.ssh/known_hosts:36

意思是,本次获取到的公钥指纹跟 SSH 客户端所在主机的 ~/.ssh/known_hosts 文件中所记录的公钥指纹不同,必须处理以后才能连接。

这时,需要确认是什么原因,使得服务器公钥指纹发生变更。到底是恶意劫持,还是管理员变更了服务器上的 SSH 公钥。

如果新的公钥确认可以信任,需要继续执行连接,可以在 SSH 客户端所在机器上执行 ssh-keygen -R <host> 将原来的公钥指纹从 ~/.ssh/known_hosts 文件内删除。

除了使用上面的命令,也可以手动修改 SSH 客户端所在机器的 ~/.ssh/known_hosts 文件,删除旧的公钥指纹。

删除旧的公钥指纹以后,重新执行 ssh 命令连接远程服务器,将新的指纹加入 ~/.ssh/known_hosts 文件,就可以顺利连接了。

ssh 命令行配置项

ssh 命令有很多配置项,修改它的默认行为。

-c 指定加密算法,可选 blowfish3des: ssh [-c blowfish | -c 3des] <host>

-C 表示压缩数据传输: ssh -C <host>

-d 设置打印的 debug 信息级别,数值越高,输出的内容越详细: ssh –d 1 <host>

-D 动态端口转发,指定本机的 Socks 监听端口,该端口收到的请求,都将转发到远程 SSH 主机 ssh -D 1080 <server> 将本机 1080 端口收到的请求,都转发到 <server>

-L 本地端口转发: ssh -L [<local_host>:]<local_port>:<target_host>:<target_port> <tunnel_host> 所有发向本地主机 <local_host> 上的端口 <local_port> 的请求,都会转发到 SSH 跳板机 <tunnel_host> ,然后 SSH 跳板机作为中介,将收到的请求发到目标服务器 <target_host> 的目标端口 <target_port> 。详见 SSH 端口转发

-R 远程端口转发。假定有 A B C 三台主机,A 在外网,B C 在内网,B 是跳板机,A 不能访问 B 而 B 可以访问 A: ssh -R <port_a>:<ip_c>:<port_c> -N <user_a>@<ip_a> (命令需在跳板机执行) 让 A 监听自己的 <port_a> 端口,并且数据由 <port_a> 流到 B 以后再经由 B 将数据包转发到 C 的 <port_c> 端口。详见 SSH 端口转发

-f 表示 SSH 连接在后台运行。

-F 指定配置文件 ssh -F </path/to/ssh_config>

-i 用于指定私钥,意为 identity_file ,值为密钥文件路径,默认为 ~/.ssh/id_dsa 。要使用指定私钥,对应的公钥必须存放到服务器。详见 SSH 服务器

-l 指定远程主机上的用户名: ssh -l <user_name> <host> = ssh <user_name>@<host>

-m 指定校验数据完整性的算法,意为 message authentication code 简称 MAC 。数据校验算法可以是 hmac-sha1hmac-md5: ssh -m [hmac-sha1 | hmac-md5] <host>

-o 用来指定一个配置命令,格式为 ssh -o "<keyword> <value>" 或者 ssh -o "<keyword>=<value>" 。现配置文件里面有如下内容:

User sally
Port 220

命令的等效写法为 ssh -o "User sally" -o "Port 220" <host>ssh -o User=sally -o Port=220 <host> 。使用等号时,配置命令可以不用写在引号里面,但等号前后不能有空格。

-p 指定 SSH 客户端连接的服务器端口: ssh -p <port> <host>

-q 表示安静模式 quiet ,不向用户输出任何警告信息。

-t 在 ssh 直接运行远端命令时,提供一个互动式 Shell: ssh -t <host> emacs

-v 显示详细信息可以重复多次,表示信息的详细程度,比如 -vv-vvv: ssh -vv <host>, ssh -vvv <host>

-V 输出 ssh 客户端的版本: ssh –V

-X 表示打开 X 窗口转发: ssh -X <host>

-1 指定使用 SSH 1 协议: ssh -1 <host>

-2 指定使用 SSH 2 协议: ssh -2 <host>

-4 指定使用 IPv4 协议,默认值: ssh -4 <host>

-6 指定使用 IPv6 协议: ssh -6 <host>

客户端配置文件

SSH 客户端的全局配置文件 /etc/ssh/ssh_config。

用户配置文件 ~/.ssh/config ,优先级高于全局配置文件。

除了配置文件,~/.ssh 目录还有一些用户个人的密钥文件和其他文件。下面是其中一些常见的文件:

用户配置文件 ~/.ssh/config,可以按照不同服务器,列出各自的连接参数,从而不必每一次登录都输入重复的参数。给一个例子:

Host *
    Port 2222

Host remoteserver
    HostName remote.example.com
    User neo
    Port 2112

Host * 表示对所有主机生效,后面的 Port 2222 表示所有主机的默认连接端口都是 2222,这样就不用在登录时特别指定端口了。这里的缩进并不是必需的,只是为了视觉上,易于识别针对不同主机的设置。

Host remote_server 表示,下面的设置只对主机 remote_server 生效。 remoteserver 只是一个别名,具体的主机由 HostName 指定, UserPort 这两项分别表示用户名和端口。这里的 Port 会覆盖上面 Host * 部分的 Port 设置。

以后,登录 remote.example.com 时,只要执行 ssh remoteserver 就会自动套用 ~/.ssh/config 文件里面指定的参数。

remoteserver 的配置等效于命令 ssh -p 2112 neo@remote.example.com

ssh 客户端配置文件的每一行,就是一个配置命令。配置命令与对应的值之间,可以使用空格,也可以使用等号。

# #开头的行表示注释,会被忽略。空行等同于注释。
Compression yes
# 等同于
Compression = yes

下面是 ssh 客户端的一些主要配置命令,以及它们的范例值:

执行远程命令

ssh <user_name>@<host> <command> 使得 SSH 在登录成功后,立刻在远程主机上执行命令 <command> 。比如 ssh foo@server.example.com cat /etc/hosts 可以在登录成功后,立即在远程主机上执行命令 cat /etc/hosts

采用这种语法执行命令时,ssh 客户端不会提供互动式的 Shell 环境,而是直接远程命令的执行结果输出在命令行。

但是,有些命令需要互动式的 Shell 环境,这时就要使用-t参数。

# 报错
$ ssh remote.server.com emacs
emacs: standard input is not a tty

# 不报错
$ ssh -t server.example.com emacs

上面代码中,emacs 命令需要一个互动式 Shell,所以报错。只有加上 -t 参数,ssh 才会分配一个互动式 Shell。