OpenSSH 安全登录实践
设置新服务器后应该做的第一件事,强化你的 SSH 安全。
SSH 普遍用于连接远程机器上的 Shell,SSH 会话最重要的部分是建立安全连接。
现代客户端仅支持 SSH 2.0,因为 1.0 被发现有缺陷,为了使 SSH 会话正常工作,客户端和服务器都必须支持相同版本的 SSH 协议。
我们这里只讲 OpenSSH,所用系统为 Red Hat Enterprise Linux 8.6。
程序种类
ssh (SSH client)
用于登录远程机器并在远程机器上执行命令的程序。它旨在通过不安全的网络在两个不受信任的主机之间提供安全的加密通信。
可以理解为客户端。
sshd (OpenSSH Daemon)
ssh 的守护程序。它通过不安全的网络在两个不受信任的主机之间提供安全的加密通信。
可以理解为服务端。
秘钥种类
Host Key 主机秘钥
用于证明主机的身份,第一次连接时会让你确认,避免中间人攻击。
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 并不在其中。
密码学很复杂,这里就不过多叙述了,长话短说吧。
你应该选择 4096 位 RSA
或 Ed25519(EdDSA)
,若要保证最佳兼容性,请选择 4096 RSA。
虽然 Ed25519 要更小也更快,但即便到了今天,某些 SSH 客户端还是并不支持这种算法。
但事实上你可以使用多个秘钥。
重启服务
进行任何更改后应该重启 sshd 服务,此操作不会中断已连接的会话。
systemctl restart sshd.service
更改端口
相信拥有公网服务器的你都经历过 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 用户组
私钥格式
有很多种格式来保存私钥,如:“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编码信息
生成秘钥对
理论上在本地生成密钥对,将公钥发送到服务器更加安全。而不是从服务器下载私钥到本地。
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 位;-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
指示秘钥文件名;>
重定向输出;
部署公钥
默认情况下 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 代码,可以是 DAEMON
、USER
、AUTH
、AUTHPRIV
、LOCAL0
、LOCAL1
、LOCAL2
、LOCAL3
、LOCAL4
、LOCAL5
、LOCAL6
、LOCAL7
。默认值为 AUTHPRIV
。
通常,所有与身份验证相关的消息都由 AUTHPRIV
(或 AUTH
)记录[旨在确保安全且不会被不受欢迎的人看到],而正常的操作消息则由 DAEMON
记录。
服务端日志级别
在 /etc/ssh/sshd_config
中设置:
LogLevel 值
选项 LogLevel 指示从 sshd 记录消息时使用的详细级别,可以是 QUIET
、FATAL
、ERROR
、INFO
、VERBOSE
、DEBUG
、DEBUG1
、DEBUG2
、DEBUG3
。默认值为 INFO
。
DEBUG 和 DEBUG1 是等价的。DEBUG2 和 DEBUG3 分别指定了更高级别的调试输出。
检查日志
查看 /var/log/messages 的最后 20 行输出: tail -n 20 /var/log/messages
小知识
为什么不仅仅使用 RSA 加密
RSA 等非设计并非为了处理大量数据,而且很慢。
解决此问题的常用方法是用对称加密算法加密解密所需内容(如 AES),然后使用非对称加密算法(如 RSA)共享对称加密算法的秘钥。
这样一来,我们既可以享受对称加密算法的效率,又不用担心在非安全信道中进行秘钥交换。