mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
3305 字
9 分钟
SSH 从入门到实践
2020-08-11

深夜的运维值班室,一封紧急邮件打破了宁静:刚上线的服务在欧洲区域全部报时间戳校验失败。排查发现,新部署的三台服务器中有两台时钟偏移超过 5 秒,而 NTP 服务根本没有启动——你需要通过 SSH 远程登录到这些服务器逐一修复,却发现密码认证被禁用了,而你的公钥还没来得及部署上去。

前言#

20 世纪 80 年代,也就是 TCP/IP 协议正式被采用和开始普及的时间点,此时互联网还未得到大范围的传播和普及,网络程序大多运行在一个可信的环境下,所以它们都是明文通信的。Berkeley r-commands 正是第一批基于 TCP/IP 协议实现的软件,它能用于远程登录、远程执行命令以及传送文件等。r-commands 是一项重要的创新,这使得它成为了早期 Unix 操作系统上的标准软件。但随着互联网的高速发展和普及,r-commands 的缺陷逐渐显露:信息明文传输、对客户端缺少认证机制,导致它逐渐退出历史舞台。

1995 年,芬兰赫尔辛基理工大学的学生 Tatu Ylonen 在校园网络中发现了密码嗅探器,于是他编写了一套进行安全通信的程序,并称其为 Secure Shell。设计目标是取代先前的 rlogin、telnet、ftp、rsh 等安全性不足的协议。1995 年 7 月,Tatu Ylonen 以免费软件的形式将其发布。程序很快流行起来,截至 1995 年底,SSH 的用户数已经达到两万,遍布五十个国家。1995 年 12 月,Tatu Ylonen 创立了 SSH 通信安全公司 来继续开发和销售 SSH。SSH 的早期版本用到了很多自由软件,例如 GNU libgmp,但后来由 SSH 公司发布的版本逐渐变成了专有软件。

1999 年,开源社区的程序员希望 SSH 能提供免费的版本,于是他们基于原始 SSH 程序的 1.2.12 版本继续开发,这也是原始 SSH 的最后一个在开源许可下发布的版本。截至 2005 年,OpenSSH 成为了最流行的 SSH 实现,默认情况下出现在大量操作系统中。

2006 年,SSH 协议的修订版 SSH-2 被采纳为标准。此版本与 SSH-1 不兼容。与 SSH-1 相比,SSH-2 具有安全性和功能改进。所以目前大多数实现仅支持 SSH-2 协议。

一、Secure Shell 协议的基本认识#

Secure Shell(SSH)是典型的 C/S 架构软件,在协议层面上有三个主要组成部分:

  • 传输层:保证服务器身份验证的机密性和完整性,提供压缩功能。该层通常基于 TCP/IP 实现。
  • 用户认证层:运行在传输层之上,该层处理客户端身份验证并提供多种身份验证方法,比如 password、publickey、键盘交互、GSSAPI 等。
  • 连接层:运行在用户认证层之上,该层定义了提供 SSH 服务的通道、通道请求和全局请求的概念。单个 SSH 连接可以同时托管多个通道,每个通道双向传输数据。通道请求用于中继带外通道特定数据,例如终端窗口大小的更改或服务器端进程的退出代码。此外,每个通道使用接收窗口大小执行自己的流量控制。SSH 客户端使用全局请求请求转发服务器端端口。标准通道类型包括 shell、direct-tcpip、forwarded-tcpip。

这种开放式架构使得 SSH 协议不仅可以用于 Secure Shell,任何需要安全传输以及用户认证的信息通道都能基于它进行实现。

SSH 的报文交互主要有以下几个阶段:

  • 连接建立:SSH 服务器端在 22 端口侦听客户端的连接请求,接收到客户端的连接建立请求后,与客户端进行三次握手,建立起一条 TCP 连接,后续的所有报文交互都在这个 TCP 连接之上进行。
  • 版本协商:TCP 连接建立之后,服务器和客户端都会向对端发送自己支持的版本号。服务器端和客户端收到对端发送过来的版本后,与本端的版本号进行比较,双方都支持的最高版本号即为协商出的版本号。
  • 算法协商:由于各种客户端和服务器对这些算法的支持情况不一样,因此客户端与服务器需要都将自己支持的算法列表发送给对方,双方依次协商每一种算法(密钥交换算法、加密算法等),直到全部成功则算协商成功,否则协商失败。
  • 密钥交换:加密算法协商成功后,为了保证加解密密钥的安全性,SSH 利用密钥交换算法在通信双方安全动态地生成和交互数据的加解密密钥,并能够有效防止第三方窃听加解密密钥。
  • 用户认证:SSH 支持多种认证方式,所以服务器会列出自己支持的认证方式,然后客户端从服务器发送给自己的认证方式列表中选择某种认证方式,向服务器发起认证请求,认证请求中包含用户名、认证方法、与该认证方法相关的内容。服务器收到客户端的认证请求,验证客户端的认证信息,验证成功则认证过程顺利完成,否则在达到最大认证次数之前重复之前的过程,直到失败为止。
  • 服务请求:SSH 协议支持多种应用服务。用户成功完成认证后,SSH 客户端向服务器端发起服务请求,请求服务器提供某种应用。首先客户端会发送 SSH_MSG_CHANNEL_OPEN 与服务器建立会话通道,建立完成后客户端便可以申请在通道上进行 shell 或 subsystem 类型的会话传输了。
  • 数据传输和连接关闭:服务请求成功,建立会话后,服务器和客户端可以在该会话上进行数据的传输。客户端将要执行的命令加密后传给服务器,服务器接收到报文,解密后执行该命令,将执行的结果加密发送给客户端,客户端将接收到的结果解密后显示到终端上。通信结束或用户空闲时间超时后,关闭会话,断开连接。

二、OpenSSH 的组成、常见配置和用法#

在 Redhat 系操作系统上执行 rpm -ql openssh-clients/openssh-server 就能查询到 OpenSSH 的客户端和服务端分别由哪些可执行文件组成:

$ rpm -ql openssh-clients | grep bin
/usr/bin/scp
/usr/bin/sftp
/usr/bin/slogin
/usr/bin/ssh
/usr/bin/ssh-add
/usr/bin/ssh-agent
/usr/bin/ssh-copy-id
/usr/bin/ssh-keyscan
$ rpm -ql openssh-server | grep bin
/usr/sbin/sshd
/usr/sbin/sshd-keygen

接下来我会分别介绍它们的常用配置和基本使用。

2.1 openssh-clients#

客户端配置#

首先介绍 SSH client 的配置,使用 man ssh_config 命令即可查询到客户端配置的文档信息,从中能看到全局配置的路径为 /etc/ssh/ssh_config,用户配置的路径为 ~/.ssh/config,配置优先级为:命令行选项优于用户配置,用户配置优于全局配置。配置的语法规则为:

  • # 开头的行或者空行表示注释。
  • 每行以关键字开头,后面接着参数。关键字不区分大小写,参数区分大小写。
  • 配置选项可以由空格或者 = 分隔,比如 参数名 参数值参数值=参数名,它们可以混用。
  • 对于包含空格的参数,可以用双引号 " 括起来。

下面用一个常见的 ~/.ssh/config 示例进行讲解:

~/.ssh/config
# Host 指令的值通常是带通配符的主机名或 IP,也可以自定义别名,配合 HostName 指定真实地址
# 以下为 cluster 主机的配置:使用密钥认证连接 192.168.100.1
Host cluster
User root
HostName 192.168.100.1
IdentityFile ~/.ssh/id_rsa_sroot
PreferredAuthentications publickey
Host 192.168.200.1?
User root
IdentityFile ~/.ssh/id_rsa_sroot
PreferredAuthentications publickey
Host *.local
User root
PreferredAuthentications password
Match User root
# ControlMaster 允许单个 socket 复用多个会话
# 首次登录后套接字保留在 ControlPath,后续连接直接复用,免去重复输密码
ControlMaster auto
ControlPath ~/.ssh/master-%r@%h:%p
ControlPersist yes
Match Host 192.168.200.*
ControlMaster auto
ControlPath ~/.ssh/master-%r@%h:%p
ControlPersist yes

从上面可以看到,使用 HostMatch 这类指令为界写了五块配置,每块配置仅作用于匹配到的主机。若一个主机能匹配多个块,则它们会同时生效。例如执行 slogin cluster 时,客户端会使用以下合并后的配置进行连接:

User root
HostName 192.168.100.1
IdentityFile ~/.ssh/id_rsa_sroot
PreferredAuthentications publickey
ControlPath ~/.ssh/master-%r@%h:%p
ControlMaster auto
ControlPersist yes

因为 cluster 匹配了 Host cluster 配置块,同时 root 用户又符合 Match User root 条件,所以最终配置是两块的合并结果。

HostMatch 的值支持通配符:? 匹配恰好一个字符,* 匹配零个或多个字符。

Info

note

块级指令(HostMatch)之下的指令带有缩进仅为可读性,实际上不缩进效果也相同。

客户端使用#

slogin 是 ssh 的软链接,大概率是为了兼容 r-commands 使用习惯而创建的,它的用法和 ssh 完全相同,所以不做介绍。

ssh 是 openssh-clients 软件包中最核心的命令,用于远程登录:

# 远程登录(本地用户名与远程一致时可省略用户名)
$ ssh [user@]hostname
# 登录后直接执行一条命令,执行完毕即退出
$ ssh [user@]hostname [command]

ssh 还支持本地端口转发远程端口转发动态端口转发,这类命令通常加上 -NTf 选项:-N 表示只转发数据不打开 shell,-T 表示不分配 tty,-f 表示后台运行。下面以执行命令的主机作为 host1 举例:

# 本地端口转发:以 host3 为跳板,将本地 8080 端口转发到 host2 的 5900 端口
# host1:8080 → host3 → host2:5900
$ ssh -NTf -L 8080:host2:5900 host3
# 远程端口转发:将 host3 的 2222 端口通过 host1 转发到 host2 的 22 端口
# host3:2222 → host1 → host2:22
$ ssh -NTf -R 2222:host2:22 host3
# 动态转发(SOCKS5 代理):在本地 2223 端口启动代理,所有请求通过隧道转发
$ ssh -NTf -D 2223 host2
# 配合 curl 使用
$ curl --socks5 localhost:2223 www.example.com

scpsftp 都是文件传输类命令。根据名称不难得知,scp 是在 SSH-2 协议上的 rcp 实现,而 sftp 则是在 SSH-2 协议上的 ftp 实现。一个用于安全复制,一个用于安全文件传输。前者比较简单,所以速度比较快。后者不仅支持文件复制,还支持在交互式环境下对本地或远程文件进行管理和传输,还支持断点续传等特性,功能更加复杂所以速度相对比较慢。

两者都支持通过一行命令上传和下载文件:

# 上传文件到远程主机
$ scp /path/to/localfile [user@]hostname:/path/to/remotefile
$ sftp /path/to/localfile [user@]hostname:/path/to/remotefile
# 从远程主机下载文件
$ scp [user@]hostname:/path/to/remotefile /path/to/localfile
$ sftp [user@]hostname:/path/to/remotefile /path/to/localfile

客户端认证#

第一次使用密码登录时,系统会出现如下提示:

$ ssh 192.168.100.1
The authenticity of host '192.168.100.1 (192.168.100.1)' can't be established.
ECDSA key fingerprint is SHA256:aVHr62YkGohXJMkjgD/MHAokU4ovii0/EqEenN69zNk.
ECDSA key fingerprint is MD5:5e:54:8a:a2:74:2c:4b:42:d3:fb:bb:95:11:2f:04:91.
Are you sure you want to continue connecting (yes/no)?

这表示客户端无法确认主机的真实性,只知道对方的公钥指纹,询问是否继续连接。输入 yes 后,客户端会将远程主机名、加密方式和公钥存储在 ~/.ssh/known_hosts 中,后续连接时直接跳过确认步骤。

Info

warning

中间人可以伪造公钥。若不事先核对公钥指纹,输入的密码可能被中间人截获。可通过 ssh-keyscan hostname 查看目标主机的完整公钥,与服务器 /etc/ssh/ssh_host_*.pub 对比验证。

若想免密登录,除了使用 ControlMaster 配置项(第一次仍需要输入密码)以外,还可以使用公钥登录

用户需要先使用 ssh-keygen 生成一个密钥对,然后执行 ssh-copy-id -i /path/to/rsa.pub user@hostname 命令并输入远程主机的密码,将本地公钥安装到远程主机的 ~/.ssh/authorized_keys 中。

公钥安装成功后便可以通过 ssh -i /path/to/rsa user@hostname 命令进行免密远程登录了。

Info

tip

ssh-keygenssh-copy-idssh 等命令使用默认密钥对时无需指定路径,默认路径为 ~/.ssh/id_rsa(私钥)和 ~/.ssh/id_rsa.pub(公钥)。

ssh-agentssh-add 构成密钥管理器:ssh-agent 是后台守护进程,负责托管私钥;ssh-add 用于向 agent 添加或删除密钥。

# 启动 agent 并进入托管环境(将 PID 写入 SSH_AGENT_PID 环境变量)
$ ssh-agent bash
$ env | grep SSH_AGENT
SSH_AGENT_PID=23912
# 将私钥添加到 agent 托管
$ ssh-add ~/.ssh/my_rsa
# 查看当前托管的所有私钥
$ ssh-add -l
2048 SHA256:hMXQoFMtT6ybjkribSLbYRCWMnoJ3MsBRkbB80abyQU /root/.ssh/my_rsa (RSA)
# 托管后,即使不是默认密钥对,连接时也无需显式指定 -i 参数
$ ssh user@hostname [command]
# 从 agent 中移除指定私钥
$ ssh-add -D /root/.ssh/my_rsa
All identities removed.

2.2 openssh-server#

服务器配置#

sshd 的配置,使用 man sshd_config 命令即可查询到服务端配置的文档信息,从中能看到默认配置文件的路径为 /etc/ssh/sshd_config,配置文件的路径也可以通过 sshd 的 -f 参数进行指定。服务器通过加载 sshd_config 还会根据里面的配置项加载 Host key 配置以及 authorized_keys 配置。服务端配置的语法规则和客户端类似:

  • # 开头的行或者空行表示注释。
  • 每行以关键字开头,后面接着参数。关键字不区分大小写,参数区分大小写。
  • 对于包含空格的参数,可以用双引号 " 括起来。

下面是一个常见的 /etc/ssh/sshd_config 示例:

/etc/ssh/sshd_config
# Host key 及 authorized_keys 文件位置
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
AuthorizedKeysFile .ssh/authorized_keys
# 认证方式
PasswordAuthentication yes # 允许密码认证
ChallengeResponseAuthentication no
# PAM 与 GSSAPI
UsePAM yes
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
# X11 转发(用于 GUI 应用远程显示)
X11Forwarding yes
# 接受语言相关的环境变量
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
# 子系统配置:sftp 的可执行文件路径
Subsystem sftp /usr/libexec/openssh/sftp-server
Info

tip

生产环境建议将 PasswordAuthentication 改为 no,仅允许公钥认证,可大幅降低暴力破解风险。同时可通过 AllowUsersAllowGroups 指令限制允许登录的用户范围。

三、参考资料#


参考#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

SSH 从入门到实践
https://blog.souloss.com/posts/tools/ssh-learn/
作者
Souloss
发布于
2020-08-11
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时