跳转到主要内容

OpenSSH 安全登录实践笔记

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


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

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


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


程序种类

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 位

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 指示秘钥文件名;
  • > 重定向输出;

 

Ed25519(EdDSA) 256 位

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

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

根据提示输入私钥密码。

  • genpkey 表示生成私钥;
  • -algorithm 值 表示指定算法,此处为 RSA;
  • -pkeyopt 选项:值 调整秘钥生成选项,此处指示 RSA 秘钥为 4096 位;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 格式

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

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

 

转换私钥格式

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

PEM PKCS #1

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

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

  • -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

小知识

为什么不仅仅使用 RSA 加密

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



主要参考