跳转到主要内容

OpenSSH 安全登录实践笔记

设置新服务器后应该做的第一件事,强化你的 SSH 安全。
相信拥有公网服务器的你都经历过 SSH 密码被尝试暴力破解。


SSH 普遍用于连接远程机器上的 Shell,SSH 会话最重要的部分是建立安全连接。

现代客户端仅支持 SSH 2.0,因为 1.0 被发现有缺陷,为了使 SSH 会话正常工作,客户端和服务器都必须支持相同版本的 SSH 协议。

SSH 是协议,而 OpenSSH 是它的开源实现,你也可以用别的实现。

我们这里只讲 OpenSSH,所用系统为 Red Hat Enterprise Linux 8.6,版本 OpenSSH_8.0p1。
本文省略了大量相关内容,你可能还需要自行了解。


程序种类

ssh (SSH client)

用于登录远程机器并在远程机器上执行命令的程序。它旨在通过不安全的网络在两个不受信任的主机之间提供安全的加密通信。
可以理解为客户端。

sshd (OpenSSH Daemon)

OpenSSH 的守护程序。它通过不安全的网络在两个不受信任的主机之间提供安全的加密通信。
可以理解为服务端。


秘钥种类

Host Key 主机秘钥

用于证明主机的身份,第一次连接时会让你确认,避免中间人攻击。
每个主机都应该是唯一的,这会在安装 OpenSSH 或首次开机时自动生成。

Public Key 公钥

密钥对外公开的部分。
不能用私钥导出公钥。
用公钥加密的消息只能用相应的私钥解密;

Private Key 私钥

保护好它,给它设置密码,只使用安全方式传输!
可以用私钥导出公钥。
用私钥加密的消息只能用相应的公钥解密。


算法

理论上 Openssh 8.0/9.0 支持 4 种算法。

  • RSA
  • DSA
  • EcDSA
  • Ed25519(EdDSA)

但 DSA 被证明不安全,所以默认禁用,打开你的 sshd_config 你应该能看到

HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

sshd 会读取默认会使用这些格式的 host key,可以看到不安全的 DSA 并不在其中。


密码学很复杂,这里就不过多叙述了。

长话短说,你应该选择 RSA 4096 位Ed25519 (EdDSA) 256位,若要保证最佳兼容性,请选择 RSA 4096 位。
虽然 Ed25519 要更小也更快,但即便到了今天,某些 SSH 客户端还是并不支持这种算法。
但事实上你可以使用多个秘钥对来保证兼容性。


重启服务

进行任何更改后应该重启 sshd 服务,此操作不会中断已连接的会话。

systemctl restart sshd.service


修改配置

很多发行版的 Openssh 配置在 /etc/ssh 目录中,我们仅需要修改服务端,即 /etc/ssh/sshd_config 。

更改端口

相信拥有公网服务器的你都经历过 SSH 密码被尝试暴力破解,我们应该将 SSH 修改为非默认端口来降低被暴力破解的可能性。

/etc/ssh/sshd_config 中设置:

Port 你要的端口

默认设置应该被注释了:#Port 22

禁用密码认证

一旦你设置了更加安全的方法,那么马上禁用密码认证,这可以有效防止暴力破解。

/etc/ssh/sshd_config 中设置:

PasswordAuthentication no
ChallengeResponseAuthentication no
启用密钥认证

这应该是默认启用的

/etc/ssh/sshd_config 中设置:

PubkeyAuthentication yes
禁止 Root 登录

/etc/ssh/sshd_config 中设置:

PermitRootLogin no
只允许规定的账户登录

即使已使用秘钥,也只应该运行规定的用户登录。
/etc/ssh/sshd_config 中设置:

AllowUsers 用户名

AllowGroups 用户组
确保符合加密策略

参考自 CIS Red Hat Enterprise Linux 8 Benchmark V2.0.0
RHEL 自带一套加密策略,一般来说已经足够。
你可以将其调整至更高级别来获得更高的安全性,参阅
运行

grep -i '^\s*CRYPTO_POLICY=' /etc/sysconfig/sshd

不应该有任何输出。
如果有,执行:

sed -ri "s/^\s*(CRYPTO_POLICY\s*=.*)$/# \1/" /etc/sysconfig/sshd
systemctl reload sshd


私钥格式

 有很多种格式来保存私钥,如:“OpenSSH 格式” 、“PEM 格式”、“PuTTY PPK 格式”。 
几乎所有的软件都支持 PEM 格式,但只有较新的软件才支持 OpenSSH 格式,也有很多软件支持 PPK 格式。

如果你使用 PEM 格式,你将不能使用 Ed25519。
OpenSSH 格式相比于 PEM 格式更能抵抗暴力破解,且 Ed25519 只能保存在 OpenSSH 等更新格式中。
PuTTY PPK 有 1、2、3 三个版本,除版本 1 外均支持 Ed25519。
PEM 基本上就是 base64 编码的 DER ( Distinguished Encoding Rules ) ,可以放很多的东西,例如 PKCS1 或 PKCS8 密钥或证书、CSR 等等。

它们功能都一样,核心内容也相同,而且大部分秘钥可以来回转换。
但一些新加密算法(Ed25519)只能使用新格式储存。
具体使用哪种格式以你的“客户端”兼容为准。

如何辨别

以文本形式打开文件,带有此格式开头和结尾的即为 OpenSSH 格式私钥

-----BEGIN OPENSSH PRIVATE KEY-----
中间内容省略(BASE64 编码数据)
-----END OPENSSH PRIVATE KEY-----

以文本形式打开文件,带有此格式开头和结尾的即为加密 PEM 格式(PKCS #1)。此格式仅支持 RSA。

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: 加密方法,一串十六进制数字作为初始向量
中间内容省略(BASE64 编码数据)
-----END RSA PRIVATE KEY-----

以文本形式打开文件,带有此格式开头和结尾的即为未加密 PEM 格式(PKCS #1)。此格式仅支持 RSA。

-----BEGIN RSA PRIVATE KEY-----
中间内容省略(BASE64 编码数据)
-----END RSA PRIVATE KEY-----

以文本形式打开文件,带有此格式开头和结尾的即为加密 PEM 格式(PKCS #8)

-----BEGIN ENCRYPTED PRIVATE KEY-----
中间内容省略(BASE64 编码数据)
-----END ENCRYPTED PRIVATE KEY-----

以文本形式打开文件,带有此格式开头和结尾的即为未加密 PEM 格式(PKCS #8)

-----BEGIN PRIVATE KEY-----
中间内容省略(BASE64 编码数据)
-----END PRIVATE KEY-----

以文本形式打开文件,带有此格式开头的即为 PuTTY PPK 格式(中文替换为实际内容),具体格式参考这里

PuTTY-User-Key-File-版本号: 算法名称
Encryption: 加密类型
Comment: 注释
之后内容省略


公钥格式

不用了解别的格式,因为 OpenSSH 只使用邪恶的专用 OpenSSH 格式公钥

以文本形式打开文件,带有此格式开头和结尾的即为 OpenSSH 格式公钥

ssh-加密类型 之后内容省略(BASE64 编码数据)

生成公私秘钥对

理论上在本地生成密钥对,将公钥发送到服务器更加安全。而不是从服务器下载私钥到本地。
更多参数参考:Openssl genpkey 文档Openssl pkey 文档ssh-keygen 文档

RSA 4096 位

方法A

安全性应该比方法比高一点,但可能有奇怪的兼容性问题。

1.生成 PEM PKCS #8 格式私钥

openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -aes256 -out private.pem

根据提示输入私钥密码。

  • genpkey 生成私钥;
  • -algorithm 值 表示指定算法,此处为 RSA;
  • -pkeyopt 选项:值 调整秘钥生成选项,此处指示 RSA 秘钥为 4096 位;
  • -aes256 表示使用指定的算法 aes256-cbc 对私钥进行加密,更加安全,ssh-keygen 似乎不能直接指定此参数。如果你不使用密码保护私钥则无需此参数;
  • -out 表示输出文件;

2.从私钥中提取公钥(PEM PKCS #8)

openssl pkey -in private.pem -out public.pem -pubout

根据提示输入私钥密码。

  • pkey 公钥或私钥处理工具;
  • -in 指示输入文件;
  • -out 指示输出文件;
  • -pubout 指示输出公钥;

3.将 PEM PKCS #8 格式公钥转换为 OpenSSH 格式公钥

很不幸 OpenSSH 只使用邪恶的专用格式,我们需要转换它。

ssh-keygen -i -m PKCS8 -f public.pem > public.openssh
  • -i-m 指示的格式读取秘钥文件并输出到标准输出;
  • -m 指示密钥格式,此处为 PKCS8;
  • -f 指示秘钥文件名;
  • > 重定向输出;

方法B
1.生成 OpenSSH 格式密钥对

ssh-keygen -t rsa -b 4096 -f id_rsa

根据提示输入私钥密码。

  • -t 指定算法,此处为 rsa;
  • -b 指定秘钥长度,此处为 4096;
  • -f 指示秘钥文件名,此处为 id_rsa;

这会直接生成一对 OpenSSH 格式的公私钥,私钥 id_rsa 和公钥 id_rsa.pub



Ed25519(EdDSA) 256 位

方法A

安全性应该比方法比高一点,但可能有奇怪的兼容性问题,需要安装 sshpk

1.生成 PEM PKCS #8 格式私钥

openssl genpkey -algorithm ed25519 -aes256 -out private.pem

根据提示输入私钥密码。

  • genpkey 表示生成私钥;
  • -algorithm 值 表示指定算法,此处为 Ed25519;
  • -aes256 表示使用指定的算法 aes256-cbc 对私钥进行加密更加安全,ssh-keygen 似乎不能直接指定此参数。如果你不使用密码保护私钥则无需此参数;
  • -out 表示输出文件;

2.从私钥中提取公钥(PEM PKCS #8)

openssl pkey -in private.pem -out public.pem -pubout

根据提示输入私钥密码。

  • pkey 公钥或私钥处理工具;
  • -in 指示输入文件;
  • -out 指示输出文件;
  • -pubout 指示输出公钥;

3.将 PEM PKCS #8 格式公钥转换为 OpenSSH 格式公钥

很不幸,ssh-keygen 目前不支持将 PKCS #8 + Ed25519 的公钥转换为 OpenSSH 格式。

你可能需要 sshpk

sshpk-conv -t ssh public.pem
  • -t 指示转换类型,此处为 ssh;
  • public.pem 公钥文件名;


方法B
1.生成 OpenSSH 格式密钥对

ssh-keygen -t ed25519 -f id_ed25519

根据提示输入私钥密码。

  • -t 指定算法,此处为 Ed25519;
  • -f 指示秘钥文件名,此处为 id_Ed25519;

这会直接生成一对 OpenSSH 格式的公私钥,私钥 id_ed25519 和公钥 id_ed25519.pub



转换私钥格式

你实际使用的客户端可能支持的私钥格式很少,这里是一些转换例子。

PEM PKCS #1 

ssh-keygen -p -m PEM -f 私钥路径

此格式仅支持 RSA。
会要求输入密码,会保存于同名文件!

  • -p 请求更改私钥文件的密码,而不是创建新的私钥。
  • -m 指示密钥格式,此处为 PEM PKCS #1;
  • -f 指示秘钥文件名;

OpenSSH 格式私钥

ssh-keygen -p -f 私钥路径

会要求输入密码,会保存于同名文件!

  • -p 请求更改私钥文件的密码,而不是创建新的私钥。
  • -f 指示秘钥文件名;


部署公钥

默认情况下 OpenSSH 会读取 用户根目录/.ssh/authorized_key用户根目录/.ssh/authorized_key2
但很多发行版默认只读取 用户根目录/.ssh/authorized_key,如果要使用多个公钥,请检查此部分。

一般情况下将 OpenSSH 格式的公钥放置到 用户根目录/.ssh/authorized_key 并设置权限 600 或 644 即可。

接上一步

mv public.openssh ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

权限

OpenSSH 很严格(也许并不),如果目录权限不对的话也会拒绝登录。

  • .ssh 目录的权限必须是 700(只允许所有者读写执行)或 755;
  • .ssh/authorized_keys 文件权限必须是 600(只允许所有者读写)或 644;
  • 其他用户不能写入你的根目录,即你的根目录不能是 77x 或 7x7;

一键更改权限:

chmod go-w ~
chmod 700 ~/.ssh/
chmod 600 ~/.ssh/*
  • 移除群组和其他用户对你的根目录的写入权限;
  • 用户根目录/.ssh 文件夹设置为 700 权限;
  • 将 用户根目录/.ssh 文件夹中的所有文件设置为 600 权限;


诊断问题

客户端

登录时带有 -vvv 参数即可将日志级别调整为 DEBUG3
例如:ssh -vvv -p 端口 用户名@主机名/域名/IP

服务端
日志类别

/etc/ssh/sshd_config 中设置:

SyslogFacility 值

选项 SyslogFacility 指定从 sshd 记录消息时使用的 Facility 代码,可以是 DAEMONUSERAUTHAUTHPRIVLOCAL0LOCAL1LOCAL2LOCAL3LOCAL4LOCAL5LOCAL6LOCAL7。默认值为 AUTHPRIV

通常,所有与身份验证相关的消息都由 AUTHPRIV(或 AUTH)记录[旨在确保安全且不会被不受欢迎的人看到],而正常的操作消息则由 DAEMON 记录。

日志级别

/etc/ssh/sshd_config 中设置:

LogLevel 值

选项 LogLevel 指示从 sshd 记录消息时使用的详细级别,可以是 QUIETFATALERRORINFOVERBOSEDEBUGDEBUG1DEBUG2DEBUG3。默认值为 INFO

DEBUG 和 DEBUG1 是等价的。DEBUG2 和 DEBUG3 分别指定了更高级别的调试输出。

检查日志

查看 /var/log/messages 的最后 20 行输出: tail -n 20 /var/log/messages

安全日志

除了 sshd 的日志,还有系统安全日志,/var/log/secure


小知识

为什么不仅仅使用 RSA 加密

RSA 等非设计并非为了处理大量数据,而且很慢。
解决此问题的常用方法是用对称加密算法加密解密所需内容(如 AES),然后使用非对称加密算法(如 RSA)共享对称加密算法的秘钥。
这样一来,我们既可以享受对称加密算法的效率,又不用担心在非安全信道中进行秘钥交换。



FinalShell 特别提示

FinalShell 是一款 SSH 客户端工具。

FinalShell 3.9.7.6 导入 Openssh 格式 RSA 私钥将导致秘钥管理器白屏,请使用 PKCS#1 格式。
FinalShell 3.9.7.6 仍然不支持 Ed25519 秘钥,Openssh 格式导入将导致白屏、putty 格式将提示秘钥不可用、PKCS#8 格式将提示密码错误。


RHEL 9/OpenSSH 8.7 特别提示

OpenSSH 在 8.7 中默认禁用了ssh-rsa 主机密钥算法,仍然使用此算法的客户端可能无法连接。(RHEL 9 使用此版本,且安全策略禁用 SHA1)
若不支持的尝试客户端连接,则可以在 /var/log/secure 中看到:
userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]

若要恢复此方法,在 RHEL 系列中可以使用此命令
将子策略 SHA1 应用于 DEFAULT 加密策略:

 update-crypto-policies --set DEFAULT:SHA1

检查:

update-crypto-policies --show

来自 https://access.redhat.com/solutions/6966079  至  https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/security_hardening/index#proc_re-enabling-sha-1_using-the-system-wide-cryptographic-policies

参阅:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening

这会将系统策略设置为 DEFAULT:SHA1。
这会削弱系统的安全性。
注意:系统范围的加密策略适用于应用程序启动。
修改策略后建议重启系统。

#不建议使用 update-crypto-policies --set LEGACY 此命令还会启用其他弱加密算法。


其他系统可以在全局配置 /etc/ssh/sshd_config 中添加 PubkeyAcceptedKeyTypes=+ssh-rsa


SELinux 特别提示

有时,SELinux 会阻止 /usr/sbin/ssh 读取 authorized_key。

tail -n 20 /var/log/messages 可以看到:

Mar  1 07:13:00 localhost setroubleshoot[155320]: SELinux 正在阻止 /usr/sbin/sshd 对 read 文件 进行 authorized_keys 访问。 如需要完整的 SELinux 信息,请运行 sealert -l c853f4a7-0ea0-4afb-8308-707fe9daeda3
Mar  1 07:13:00 localhost setroubleshoot[155320]: SELinux 正在阻止 /usr/sbin/sshd 对 read 文件 进行 authorized_keys 访问。#012#012*****  插件 catchall (100. 置信度) 建议   *******************************************#012#012如果你相信 sshd应该允许_BASE_PATH read 访问 authorized_keys file默认情况下。#012然后 应该将这个情况作为 bug 报告。#012可以生成本地策略模块以允许此访问。#012做#012暂时允许此访问权限执行:#012# ausearch -c 'sshd' --raw | audit2allow -M my-sshd#012# semodule -X 300 -i my-sshd.pp#012

Mar  1 07:13:00 drum setroubleshoot: SELinux is preventing /usr/sbin/sshd from open access on the file authorized_keys. For complete SELinux messages. run sealert -l c853f4a7-0ea0-4afb-8308-707fe9daeda3

如果你会配置 SELinux 则无需关闭,或者执行以下命令将 SELinux 设置为许可模式:

sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

或是关闭 SElinux(需要重启):

sudo sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config

更多请参阅:https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/using_selinux/index

新机一键生成密钥对

RSA 4096 位

生成 PEM PKCS #8 格式私钥,并安装 OpenSSH 公钥,下载私钥 private.pem 即可。
由于要输入密钥密码,分两步执行。

openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -aes256 -out private.pem
openssl pkey -in private.pem -out public.pem -pubout
ssh-keygen -i -m PKCS8 -f public.pem > public.openssh

mkdir ~/.ssh/
mv public.openssh ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

chmod go-w ~
chmod 700 ~/.ssh/
chmod 600 ~/.ssh/*

私钥转换为 PKCS #1 格式:

ssh-keygen -p -m PEM -f private.pem
Ed25519(EdDSA) 256 位

生成 OpenSSH 格式私钥,并安装 OpenSSH 公钥,下载私钥 id_ed25519 即可。
由于要输入密钥密码,分两步执行。

ssh-keygen -t ed25519 -f id_ed25519
mkdir ~/.ssh/
mv id_ed25519.pub ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

chmod go-w ~
chmod 700 ~/.ssh/
chmod 600 ~/.ssh/*

留在当前文件夹中的 id_ed25519 即为私钥。



主要参考