OpenSSH服务最佳实践
2012-11-11 15:29:09 阿炯

OpenSSH 是 SSH 协议的开源实现。OpenSSH 可进行远程登录操作,通过 scp 或 sftp 进行远程文件传输等。SSH 或 Secure Shell 是一种安全协议,是安全管理远程服务器的最常用方法。它使用了多种加密技术,用于在双方之间建立加密安全连接以及来回传递命令和输出。SSH 确保两个网络和系统之间交换数据的机密性和完整性,其主要的优点是通过使用公共密钥加密进行服务器身份验证。本文总结一些与之相关的原理、使用方法和技巧。

默认配置文件和端口
/etc/ssh/sshd_config - OpenSSH 服务器配置文件
/etc/ssh/ssh_config - OpenSSH 客户端配置文件
~/.ssh/ - 用户独立的 ssh 配置目录
~/.ssh/authorized_keys or ~/.ssh/authorized_keys - 公钥 (RSA or DSA)
/etc/nologin - 如果该文件存在,则只允许 root 帐号登录
/etc/hosts.allow and /etc/hosts.deny : 访问控制定义
SSH 默认端口 : TCP/22


工作原理

SSH 协议使用客户端 - 服务器模型来验证双方并加密它们之间的数据。服务器组件侦听指定的端口以进行连接。它负责协商安全连接,验证连接方,并在接受凭证时生成正确的环境。客户端负责开始与服务器的初始 TCP 握手,协商安全连接,验证服务器的身份是否与先前记录的信息匹配,以及提供身份验证的凭据。SSH 会话分两个阶段建立。第一阶段是同意并建立加密以保护未来的通信。第二阶段是对用户进行身份验证,并发现是否应授予对服务器的访问权限。

协商会话加密

当客户端建立 TCP 连接时,服务器会使用它支持的协议版本进行响应。如果客户端可以匹配其中一个可接受的协议版本,则继续连接。服务器还提供其公共主机密钥,客户端可以使用该密钥来检查这是否是预期的主机。此时双方使用称为 Diffie-Hellman 算法的版本协商会话密钥。该算法(及其变体)使得每一方能够将他们自己的私有数据与来自另一系统的公共数据组合以得到相同的秘密会话密钥;会话密钥将用于加密整个会话。用于此部分过程的公钥和私钥对与用于向服务器验证客户端的SSH密钥完全分开。

经典 Diffie-Hellman 秘钥协商的基本流程为:
双方都同意一个大的素数,它将作为种子值。
双方就密文生成器(通常为 AES)达成一致,该生成器将用于以预定义的方式操纵值。
独立地,每一方都提出另一个素数,该号码对另一方保密。此编号用作此交互的私钥(与用于身份验证的专用 SSH 密钥不同)。
生成的私钥,密文生成器和共享素数用于生成从私钥导出但可以与另一方共享的公钥。
然后两个参与者交换他们生成的公钥。
接收实体使用他们自己的私钥,另一方的公钥和原始共享素数来计算共享密钥。虽然这是由各方独立计算的,但使用相反的私钥和公钥,它将产生相同的共享密钥。
 然后使用共享密钥加密随后的所有通信。

用于其余连接的共享秘密加密称为二进制数据包协议。上述过程允许每一方平等地参与生成共享秘密,这不允许一端控制秘密。它还完成了生成相同的共享秘密的任务,而无需通过不安全的通道发送该信息。生成的秘密是对称密钥,这意味着用于加密消息的相同密钥可用于在另一侧解密它,这样做的目的是将所有进一步的通信包装在一个无法被外人破译的加密隧道中。建立会话加密后,用户身份验证阶段开始。


验证用户对服务器的访问权限

下一阶段涉及验证用户和决定访问权限。根据服务器接受的内容,有几种不同的方法可用于身份验证。

最简单的可能是密码验证,其中服务器只是提示客户端输入他们尝试登录的帐户的密码,密码通过协商加密发送,因此对外方是安全的。即使密码将被加密,由于密码复杂性的限制,通常不建议使用此方法。与其他身份验证方法相比,自动脚本可以非常轻松地破坏正常长度的密码。最受欢迎和推荐的替代方案是使用 SSH 密钥对。SSH 密钥对是非对称密钥,这意味着两个关联密钥服务于不同的功能。公钥用于加密只能使用私钥解密的数据。公钥可以自由共享,因为虽然它可以加密私钥,但是没有从公钥导出私钥的方法。

在建立对称加密之后,使用 SSH 密钥对进行身份验证,如上一节所述,过程如下:
客户端首先向服务器发送要对其进行身份验证的密钥对的 ID。
服务器检查客户端尝试登录密钥ID的帐户的 authorized_keys 文件。
如果在文件中找到具有匹配 ID 的公钥,则服务器生成随机数并使用公钥加密该号码。
服务器向客户端发送此加密消息。
如果客户端实际上具有关联的私钥,则它将能够使用该密钥解密消息,从而显示原始号码。
客户端将解密的号码与用于加密通信的共享会话密钥组合,并计算该值的 MD5 哈希值。
然后,客户端将此 MD5 哈希值发送回服务器,作为加密号码消息的答案。
服务器使用相同的共享会话密钥和它发送给客户端的原始编号来自行计算 MD5 值,它将自己的计算与客户端发回的计算进行比较。如果这两个值匹配,则证明客户端拥有私钥并且客户端已经过身份验证。

密钥的不对称性允许服务器使用公钥加密到客户端的消息,然后客户端可以通过正确解密消息来证明它拥有私钥。使用的两种类型的加密(对称共享密钥和非对称公钥 - 私钥)都能够利用它们在此模型中的特定优势。


加密的方式


为了确保信息的传输,SSH 在事务中的各个点采用了许多不同类型的数据操作技术。这些包括对称加密,非对称加密和散列的形式。

1.对称加密

加密和解密数据的组件的关系决定加密方案是对称的还是非对称的。

对称加密是一种加密类型,其中一个密钥可用于加密对方的消息,也可用于解密从另一个参与者接收的消息。这意味着持有该密钥的任何人都可以加密和解密持有该密钥的任何其他人的消息。

这种类型的加密方案通常称为“共享秘密”加密或“秘密密钥”加密。通常只有一个密钥用于所有操作,或者一对密钥,其中关系易于发现,并且导出相反的密钥是微不足道的。

SSH 使用对称密钥来加密整个连接。与某些用户假设的相反,可以创建的公共/私有非对称密钥对仅用于身份验证,而不是用于加密连接。对称加密允许对密码认证进行保护以防止窥探。

客户端和服务器都有助于建立此密钥,并且外部各方永远不知道所产生的秘密。密钥是通过称为密钥交换算法的过程创建的。这种交换导致服务器和客户端通过共享某些公共数据并用某些秘密数据操纵它们而独立地到达相同的密钥。稍后将更详细地解释该过程。

此过程创建的对称加密密钥是基于会话的,并构成服务器和客户端之间发送的数据的实际加密。一旦建立,其余数据必须使用此共享密钥加密。这是在验证客户端之前完成的。

SSH 可以配置为使用各种不同的对称密码系统,包括 AES,Blowfish,3DES,CAST128 和 Arcfour。服务器和客户端都可以根据优先顺序决定其支持的密码列表,服务器上可用的客户端列表中的第一个选项用作两个方向的密码算法。

在 Ubuntu 14上,客户端和服务器都是默认的:aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes128-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour。

这意味着如果两台 Ubuntu 14 计算机相互连接(不通过配置选项覆盖默认密码),它们将始终使用aes128-ctr密码加密其连接。

2.非对称加密

非对称加密与对称加密的不同之处在于,为了在单个方向上发送数据,需要两个相关的密钥。其中一个密钥称为私钥,而另一个称为公钥。

公钥可以与任何一方自由共享。它与其配对密钥相关联,但私钥不能从公钥中派生。公钥和私钥之间的数学关系允许公钥加密只能由私钥解密的消息。这是一种单向能力,这意味着公钥无法解密它写入的消息,也无法解密私钥可能发送的任何内容。

私钥应保密,绝不应与另一方共享,这是公钥范式的关键要求。私钥是唯一能够解密使用关联公钥加密的消息的组件,凭借这一事实,任何能够解密这些消息的实体已经证明它们可以控制私钥。

SSH 在几个不同的地方使用非对称加密。在用于建立对称加密(用于加密会话)的初始密钥交换过程中,使用非对称加密。在这个阶段,双方都生成临时密钥对并交换公钥,以便产生将用于对称加密的共享密钥。

使用 SSH 进行非对称加密的更好讨论来自基于 SSH 密钥的身份验证。 SSH 密钥对可用于向服务器验证客户端,客户端创建密钥对,然后将公钥上载到其希望访问的任何远程服务器。它放在远程服务器上用户帐户主目录的~/ .ssh目录下名为authorized_keys的文件中。

在建立对称加密以保护服务器和客户端之间的通信之后,客户端必须进行身份验证以允许访问,服务器可以使用此文件中的公钥来加密到客户端的质询消息。如果客户端可以证明它能够解密此消息,则表明它拥有相关的私钥。然后服务器可以为客户端设置环境。

3.哈希

SSH 利用的另一种形式的数据操作是加密散列。加密散列函数是创建简洁“签名”或一组信息摘要的方法。它们的主要区别在于它们永远不会被颠倒,它们几乎不可能以可预测的方式影响,它们实际上是独一无二的。

使用相同的散列函数和消息应该产生相同的散列;修改数据的任何部分应该产生完全不同的哈希。用户不应该能够从给定的哈希生成原始消息,但是他们应该能够判断给定的消息是否产生给定的哈希。

鉴于这些属性,散列主要用于数据完整性目的并验证通信的真实性。 SSH 中的主要用途是使用 HMAC 或基于散列的消息验证代码,这些用于确保收到的消息文本完整且未经修改。

作为上面概述的对称加密协商的一部分,选择消息认证码(MAC)算法。通过客户端可接受的 MAC 选择列表来选择算法,将使用服务器支持的列表中的第一个。协商加密后发送的每条消息都必须包含 MAC,以便另一方可以验证数据包的完整性。 MAC 是根据对称共享密钥,消息的分组序列号和实际消息内容计算的,MAC 本身作为数据包的最后部分发送到对称加密区域之外。研究人员通常建议首先加密数据,然后计算 MAC。


实践一

1、指定登陆用户

默认的,ssh 会尝试用当前用户作为用户名来连接。从一开始就要指定用户名的,可以使用 -l 选项参数。
ssh -l freeoa ipaddr
ssh freeoa@ipaddr

2、指定端口

SSH 默认使用的端口号是 22。如果你运行 ssh 命令没有指定端口号,它直接就是通过 22 端口发送请求的。一些系统管理员会改变 SSH 的默认端口号。让我们试试,现在端口号是 1234。要连上那主机,就要使用 -p 选项,后面在加上 SSH 端口号。
ssh ipaddr -p 1234

要改变端口号就需要修改 /etc/ssh/sshd_config 文件,找到此行:
Port 22

把它换成其他的端口号,比如上面示例的 1234 端口,然后重启 SSH 服务。

3、开启数据压缩

有了这个选项,所有通过 SSH 发送或接收的数据将会被压缩,并且还是加密的。要使用 SSH 的压缩功能,使用 -C 选项。
ssh -C ipaddr

如果你的连网速度很慢的话,这个选项非常有用。但如果你使用的是像 LAN 或其它更高级网络的话,压缩反而会降低你的传输速度。可以使用 -o 选项加上压缩级别参数来控制压缩的级别,但这个选项仅在 SSH-1 下起作用。

4、指定加密算法

SSH 提供了一些可用的加密算法。可以在 /etc/ssh/ssh_config or ~/.ssh/config 文件中看到(如果存在的话)。

比如想使用 blowfish 算法来加密 SSH 会话,那么只要把这一行加入你的/etc/ssh/ssh_config or ~/.ssh/config 文件就可以:
Cipher blowfish

开启调试模式

出于某些原因想要追踪调试我们建立的 SSH 连接登录认证情况。SSH 提供的 -v 选项参数正是为此而设的(多个v可以开启更多的消息打印)。
ssh -vvv

5、绑定源地址

如果客户端有多于两个以上的 IP 地址,不可能确定在使用哪一个 IP 连接到 SSH 服务器。为了解决这种情况,我们可以使用 -b 选项来指定一个IP 地址。这个 IP 将会被使用做建立连接的源地址。
ssh -b local_ipaddr -l freeoa remote_ipaddr

6、使用其他配置文件

默认情况下,ssh 会使用位于 /etc/ssh/ssh_config 的配置文件,该配置文件作用于系统的所有用户。想要为特定的用户指定特殊的设置的话,可以把配置写入到用户的家目录下~/.ssh/config 文件中。如果此文件不存在,可以手工创建一个。

下面是一个通用 ssh_config 文件配置的示例,这配置文件位于 /home/user 目录下。
Host 192.168.8.*
ForwardX11 yes
PasswordAuthentication yes
ConnectTimeout 10
Ciphers aes128-ctr,aes192-ctr,aes256-ctr
Protocol 2
HashKnownHosts yes

要使用指定的配置文件,可以使用 -F 选项。
ssh -F /home/user/my_ssh_config

7、使用X11 Forwarding

想把服务端的 X11 应用程序显示到客户端计算机上,SSH 提供了 -X 选项。但要启用这个功能需要做些准备,下面为其设置过程:在服务器端,你需要使 /etc/ssh/sshd_config 文件中的行设置成X11Forwad yes,以启用 X11 Forwarding,并重启 SSH 服务。然后在客户端,输入 ssh -X user@host:
ssh -X freeoa@ipaddr

一旦登陆,可以输入:
$ echo $DISPLAY

来检查,正常情况下应该可以看到向如下所示的
localhost:10:0

随后就可以运行应用了,比如想运行 xclock 程序可以输入:
$ xclock

它就运行起来了,xclock 确实是运行在远端系统下的,但它在你的本地系统中显示。


实践二

1、只使用 SSH Protocol 2
SSH 协议版本 1 有很多漏洞和安全问题,应该避免使用 SSH-1,可通过在 sshd_config 文件中配置如下信息来启用 SSH-2:
Protocol 2

2、限制用户访问 SSH
默认所有系统用户都可以通过 SSH 登录,只需要用密码或者公钥即可。有时候你创建某个用户只是为了使用邮件或者是 FTP,但是这些用户也可以通过 ssh 登录,登录后就可以访问很多的系统工具,包括编译器和脚本语言,可打开网络端口以及做很多其他的事情。我们可以通过 sshd_config 文件中的 AllowUsers 和 DenyUsers 来设置可访问 SSH 服务的用户名单。

下面配置只允许 root, vivek 和 jerry 三个帐号使用 SSH 服务

AllowUsers root freeoa jerry
你也可以设置哪些用户不能访问 SSH:
DenyUsers saroj anjali foo
你也可以配置 Linux PAM 来允许或者拒绝通过 sshd 服务器登录,你也可以对一个分组进行设置是否可以访问 ssh。

3、配置空闲登出的超时间隔
用户通过ssh登录到服务器后,如果长时间没有任何动作的话,可通过设置空闲超时时间来让登录的用户自动登出,以避免一些不必要的 ssh 会话连接。打开 sshd_config 文件查看并编辑如下配置:
ClientAliveInterval 300
ClientAliveCountMax 0
这里我们设置了 300 秒(5分钟),一旦用户在 5 分钟内没有动作则会自动被踢出。

4、禁用 .rhosts 文件
不读取用户命令下的 ~/.rhosts 和 ~/.shosts 文件,只需在 sshd_config  中使用如下设置:
IgnoreRhosts yes
SSH 可模拟过时的 rsh 命令的行为,需要禁用通过 RSH 的非安全登录。

5、禁用基于主机的认证
在 sshd_config 中使用如下配置:
HostbasedAuthentication no

6、禁止 root 帐号通过 SSH 登录
没必要让 root 帐号可通过 ssh 登录,可通过正常用户登录后然后执行 su 或者 sudo 来执行 root 权限的操作,可在 sshd_config 中使用如下配置来禁用 root 帐号登录:
PermitRootLogin no

7、启用警告的 Banner
可以在 sshd_config 中通过如下配置来启用通过 ssh 登录后的警告信息:
Banner /etc/issue

8、修改 SSH 端口和限制 IP 绑定
默认 SSH 绑定到所有网卡的所有 IP,端口号是 22,建议只绑定到需要的网卡 IP ,并修改默认的端口。可通过 ssh_config 配置文件中使用如下配置信息将端口修改为 300:
Port 300
ListenAddress 192.168.1.5
ListenAddress 202.54.1.5
还有一个更好的方法是使用积极主动的脚本,诸如 fail2ban 或者是 denyhosts。

9、Chroot SSHD (将用户锁定在他的主目录下)
默认用户允许浏览服务器上的目录,如 /etc、/bin 等,我们可使用 chroot 或者是 special tools such as rssh 来保护 ssh。而 OpenSSH 4.8p1 和 4.9p1 让你不再依赖第三方的工具(如rssh和组合 chroot)来将用户锁定在他的主目录下。

10、使用 TCP Wrappers
TCP Wrapper 是一个基于主机地址的网络 ACL 系统,用来过滤网络地址访问互联网。OpenSSH 支持 TCP Wrappers。只需要更新你的 /etc/hosts.allow 文件只允许通过 192.168.1.2 172.16.0.12 访问 sshd:
sshd:192.168.1.2 172.16.0.12

11、禁用空密码
你应该禁止帐号使用空密码进行远程登录,在 sshd_config 使用如下配置即可:
PermitEmptyPasswords no

限制 22 端口连接的速率
netfilter 和 pf 都提供了连接速率限制选项

Iptables 示例
下面配置禁止在一分钟内 22 端口超过 5 个连接:
#!/bin/bash
inet_if=eth1
ssh_port=22
$IPT -I INPUT -p tcp --dport ${ssh_port} -i ${inet_if} -m state --state NEW -m recent  --set
$IPT -I INPUT -p tcp --dport ${ssh_port} -i ${inet_if} -m state --state NEW -m recent  --update --seconds 60 --hitcount 5 -j DROP

另外的配置选项:
$IPT -A INPUT  -i ${inet_if} -p tcp --dport ${ssh_port} -m state --state NEW -m limit --limit 3/min --limit-burst 3 -j ACCEPT
$IPT -A INPUT  -i ${inet_if} -p tcp --dport ${ssh_port} -m state --state ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o ${inet_if} -p tcp --sport ${ssh_port} -m state --state ESTABLISHED -j ACCEPT
# another one line example
# $IPT -A INPUT -i ${inet_if} -m state --state NEW,ESTABLISHED,RELATED -p tcp --dport 22 -m limit --limit 5/minute --limit-burst 5-j ACCEPT
更多的配置详情请看 iptables 的 man 页。

*BSD PF 示例
以下将限制的最大连接数到20,每个源速率限制连接数,在一个5秒的跨度15。如果有人打破我们的规则将它们添加到我们的阻止的ip表和阻止他们做任何进一步的连接。

sshd_server_ip="202.54.1.5"
table <abusive_ips> persist
block in quick from <abusive_ips>
pass in on $ext_if proto tcp to $sshd_server_ip port ssh flags S/SA keep state (max-src-conn 20, max-src-conn-rate 15/5, overload <abusive_ips> flush)

12、远程GUI
有时候通过本地的GUI程序来访问远程服务器的文件会非常有用,比如,编辑一副图片,或者查看一个PDF文件,或者只是简单的通过一个非命令行的编辑器来修改代码,我发现GVim要比终端里的Vim更有用,因为我可以通过gvimopens打开一个新窗口来编辑文件,而用当前的SSH窗口继续执行其 它操作,不要要这样做,需要先在你的SSH配置中开启一个叫做X forwarding的选项:
ForwardX11 yes

这个选项需要服务器配置才能起作用,服务器也需要开启X forwarding,你可以在服务器的/etc/ssh/sshd_config中添加下面这个命令:
X11Forwarding yes

同时你还需要确保安装了xauth,编辑器,图片查看器以及其它的你需要运行的图形化程序,这种方式只有在支持本地X服务器的操作提供才可以工 作,mac和Windows上都有免费的X Server,你可能需要花些时间配置它们,相比之下,切换到Linux相对会更容易一下。

13、本地操作远程文件
另一种让远程GUI程序显示在本地的替代方案就是让本地的GUI程序可以直接操作远程文件,你可以通过SSHFS来实现,只需要创建一个空目录,然后使用SSHFS将一个远程目录mount到这个目录就可以了:
$ mkdir mywork
$ sshfs dev:projects/gallery/src mywork
$ cd mywork
$ ls

现在你就可以使用任何你喜欢的本地程序来便捷这个目录中的文件了,它们看起来是在你的本地,但其实时远程服务器上的文件,你可以使用fusermount命令来unmount这些文件,不要担心记不住,它们就在sshfs手册的顶上:
$ cd ..
$ fusermount -u mywork

SSHFS可以在Linux和OSX上工作,Windows用户目前有相关软件但不好用。


实践三

1、旧版本的ssh客户端登录新版本的sshd服务器报无密钥适配错误

见于xshell4连接sshd8.8时,报xshell 找不到匹配的host key算法,在sshd 8.8服务中发现的错误日志
Sep 28 12:11:07 freeoahost sshd[4248]: Unable to negotiate with 172.18.X.Y port 59566: no matching host key type found. Their offer: ssh-dss,ssh-rsa [preauth]

userauth_pubkey: key type ssh-dss not in PubkeyAcceptedKeyTypes [preauth]

RHEL 8.0, and initially went the same route, adding PubkeyAcceptedKeyTypes=+ssh-dss,+ssh-rsa to /etc/ssh/sshd_config.

如果不能启动sshd服务,将检查/etc/ssh/sshd_config中的相关key文件是否存在,存在的话其key长度是否达到对应版本的最小长度(ssh8x的rsa不再是2048bits,而是3072bits),相应的算法是否被支持,DSA就基本上不再被支持了。这里有关于《SSH服务支持的加密算法介绍》。

SSH不支持ssh-dss的解决方案(Skipping ssh-dss key: ........... not in PubkeyAcceptedKeyTypes)

Unable to negotiate with legacyhost: no matching host key type found. Their offer: ssh-dss.

在Linux下不报错,连不上服务器。打开ssh的verbose输出模式,可以看到最后一行输出也是ssh-dss的故障。参考openssh的官方文档原因是新版本的SSH禁用了ssh-dss(DSA)公钥算法,DSA算法太脆弱不推荐使用。如果要重新激活,可以在命令行参数中指明:
ssh -oHostKeyAlgorithms=+ssh-dss user@legacyhost

或者在配置文件 ~/.ssh/config 中针对该主机进行专项设置:
Host somehost.example.org
    HostKeyAlgorithms +ssh-dss

当然这个还得要服务器上支持dsa算法(sshd_config):
HostKeyAlgorithms +ssh-dss

或直接在客户端的ssh_config配置文件加入该选项来全局生效:
PubkeyAcceptedKeyTypes +ssh-dss


2、重新生成本机的相关密钥(key)

在重启sshd服务时会报:sshd error: could not load host key

当用户连接时会报:Connection closed by hostIP

ssh服务器方面:在系统日志中,你看到如下错误消息(如在Debian上的/var/log/auth.log日志文件中就有如下报错:)。
error:Couldnot load host key:/etc/ssh/ssh_host_rsa_key
error:Couldnot load host key:/etc/ssh/ssh_host_dsa_key
error:Couldnot load host key:/etc/ssh/ssh_host_ecdsa_key
fatal:No supported key exchange algorithms [preauth]

导致该问题的根源是,sshd守护进程不能加载主机密钥了。当OpenSSH服务器首次安装到Linux系统时,SSH主机密钥应该会自动生成以供后续使用。即密钥生成过程没有成功完成或被人为操作修改,就会导致这样的SSH登录问题,可以重新生成相关的key来解决此问题。

可以查看一下是不是没有相关的key文件(默认在:/etc/ssh/ssh*key),或是没有对其的访问权限,或者是其大小成为0。

在Debian及其衍生版上,可以使用dpkg-reconfigure工具来重新生成SSH主机密钥,过程如下:
mv -v /etc/ssh/ssh*key /tmp
dpkg-reconfigure openssh-server

RPM的包管理系统上,所要做的是,删除现存(有问题的)密钥,然后重启sshd服务(过程中的启动脚本对此做了不存在时自动生成的逻辑)。
mv -v /etc/ssh/ssh*key /tmp
systemctl restart sshd

另外一个重新生成SSH主机密钥的方式是,使用ssh-keygen命令来手动生成(dsa因其缺陷太多就放弃了吧)。

可以手动生成相关的host key
/opt/ssh/bin/ssh-keygen -t rsa -f /opt/ssh/etc/ssh_host_rsa_key
/opt/ssh/bin/ssh-keygen -t ecdsa -f /opt/ssh/etc/ssh_host_ecdsa_key

ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key

在生成新的SSH主机密钥后,确保它们能在/etc/ssh目录中且大小、权限正常,可不必重启sshd服务便可解决此问题。在重启系统等情况下,只要发现密钥不存在,大部分系统都可以自动生成密钥,因为在脚本中做了检测判断,会调用生成指令。

重设SSH密钥还有其它因素的考虑

理论上来说,每次安装服务器时SSH密钥(SSH Host Key)都是自动生成的,而生成出相同密钥的概率接近于0,这样避免了中间攻击等情况;但就是存在以下情况使得两台SSH密钥相同:
在虚拟化技术中克隆了一台虚拟机;
将原来的虚拟硬盘复制后新建虚拟机运行,如果是公有云平台提供的模板机...

重设SSH密钥方法比较简单,首先要移除现有的密钥,在/etc/ssh/目录中存放了所有以ssh_host_开头的私钥和公钥文件。

另外要注意的是,更新后的SSH公钥和你的电脑中记录的公钥不同,因此在下一次登录SSH时,肯定会收到一个中间人攻击的警告(man-in-the-middle attack)。此时只需要按照提示执行其中的ssh-keygen命令即可取消对原先密钥的信任,然后再次连接SSH便会提示有新密钥了。当然最简单粗暴地直接删除known_hosts文件:
mv -fv ~/.ssh/known_hosts /tmp/

也是可以的。

提供一个比较原生的sshd的启动及初始脚本

#!/bin/sh
#
# /etc/rc.d/sshd: start/stop ssh daemon
#

case $1 in
start)
    if [ ! -f /etc/ssh/ssh_host_key ]; then
        /usr/bin/ssh-keygen -t rsa1 -N "" -f /etc/ssh/ssh_host_key > /dev/null
    fi
    if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
        /usr/bin/ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key > /dev/null
    fi
    if [ ! -f /etc/ssh/ssh_host_dsa_key ]; then
        /usr/bin/ssh-keygen -t dsa -N "" -f /etc/ssh/ssh_host_dsa_key > /dev/null
    fi
    if [ ! -f /etc/ssh/ssh_host_ecdsa_key ]; then
        /usr/bin/ssh-keygen -t ecdsa -N "" -f /etc/ssh/ssh_host_ecdsa_key > /dev/null
    fi
    /usr/sbin/sshd
    ;;
stop)
    if [ -f /var/run/sshd.pid ]; then
        kill $(< /var/run/sshd.pid)
        rm -f /var/run/sshd.pid
    else
        killall -q /usr/sbin/sshd
    fi
    ;;
restart)
    $0 stop
    sleep 2
    $0 start
    ;;
*)
    echo "usage: $0 [start|stop|restart]"
    ;;
esac

# End of file


实践四 在 Linux 下保护 SSH 服务器连接的几种方法

1.禁用 root 用户登录

禁用 root 用户的 SSH 访问并创建一个具有 root 权限的新用户。关闭 root 用户的服务器访问是一种防御策略,可以防止攻击者实现入侵系统的目标。例如可以创建一个名为 freeoa 的用户,如下所示:
useradd -m freeoa
passwd freeoa
usermod -aG sudo freeoa

以下是上述命令的简要说明:
useradd 创建一个新用户,并且 -m 参数在创建的用户的主目录下创建一个文件夹。
passwd 命令用于为新用户分配密码。分配给用户的密码应该很复杂且难以猜测。
usermod -aG sudo 将新创建的用户添加到管理员组。

在用户创建过程之后,需要对 sshd_config 文件进行一些更改。可以在 / etc/ssh/sshd_config 找到此文件。使用任何文本编辑器打开文件并对其进行以下更改:
# Authentication:
#LoginGraceTime 2m
PermitRootLogin no
AllowUsers freeoa

PermitRootLogin 行将阻止 root 用户使用 SSH 获得远程访问。在 AllowUsers 列表中包含 freeoa 会向用户授予必要的权限。最后重启 SSH 服务使设置生效。

2.更改默认端口

默认的 SSH 连接端口是 22。当然所有的攻击者都知道这一点,因此需要更改默认端口号以确保 SSH 安全。尽管攻击者可以通过 Nmap 扫描轻松找到新的端口号,但这里的目标是让攻击者的工作更加困难。要更改端口号,对/etc/ssh/sshd_config文件进行以下更改:
Include /etc/ssh/sshd_config.d/*.conf
Port 9922

在这一步之后,使用 sudo systemctl restart ssh 再次重启 SSH 服务。现在可以使用刚刚定义的端口访问你的服务器。如果使用的是防火墙,则还必须在此处进行必要的规则更改。在运行 netstat -tlpn 命令时可以看到 SSH 端口号已更改。

3.禁止使用空密码的用户访问

在系统上可能有不小心创建的没有密码的用户。要防止此类用户访问服务器,可以将 sshd_config 文件中的 PermitEmptyPasswords 行值设置为 no。
PermitEmptyPasswords no

4.限制登录/访问尝试

默认情况下可以根据需要尝试多次输入密码来访问服务器。但攻击者可以利用此漏洞对服务器进行暴力破解。通过指定允许的密码尝试次数,可以在尝试一定次数后自动终止 SSH 连接。为此请更改 sshd_config 文件中的 MaxAuthTries 值。
MaxAuthTries 3

5.使用 SSHv2

SSH 的第二个版本发布是因为第一个版本中存在许多漏洞。默认情况下可以通过将 Protocol 参数添加到 sshd_config 文件来启用服务器使用第二个版本。这样未来的所有连接都将使用第二个版本的 SSH(据说在8.x之后已经没有这个参考配置了,因为软件只提供第二版本的协议,就无需再配置了)。
Include /etc/ssh/sshd_config.d/*.conf
Protocol 2

6.关闭 TCP 端口转发和 X11 转发

攻击者可以尝试通过 SSH 连接的端口转发来访问你的其他系统。为了防止这种情况,可以在 sshd_config 文件中关闭 AllowTcpForwarding 和 X11Forwarding 功能。
X11Forwarding no
AllowTcpForwarding no

7.使用 SSH 密钥连接

连接到服务器的最安全方法之一是使用 SSH 密钥。使用 SSH 密钥时,无需密码即可访问服务器。另外可以通过更改 sshd_config 文件中与密码相关的参数来完全关闭对服务器的密码访问。创建 SSH 密钥时,有两个密钥:Public 和 Private。公钥将上传到要连接的服务器,而私钥则存储在将用来建立连接的计算机上。

在计算机上使用 ssh-keygen 命令创建 SSH 密钥。不要将密码短语字段留空并记住在此处输入的密码。如果将其留空将只能使用 SSH 密钥文件访问它。但如果你设置了密码,则可以防止不该拥有该密钥文件的攻击者访问它。

8.限制IP连接来源

大多数情况下,防火墙使用自己的标准框架阻止访问,旨在保护服务器。但这并不总是足够的,需要增加这种安全潜力。为此可打开 /etc/hosts.allow 文件。通过对该文件进行的添加就可以限制 SSH 权限,允许特定 IP 段,或输入单个 IP 并使用拒绝命令阻止所有剩余的 IP 地址。





该文章最后由 阿炯 于 2023-09-13 13:09:05 更新,目前是第 2 版。