SSH 是服务器登录工具,提供密码登录和密钥登录。
但是,SSH 还有第三种登录方法,那就是证书登录。很多情况下,它是更合理、更安全的登录方法,本文就介绍这种登录方法。
一、非证书登录的缺点
密码登录和密钥登录,都有各自的缺点。
密码登录需要输入服务器密码,这非常麻烦,也不安全,存在被暴力破解的风险。
密钥登录需要服务器保存用户的公钥,也需要用户保存服务器公钥的指纹。这对于多用户、多服务器的大型机构很不方便,如果有员工离职,需要将他的公钥从每台服务器删除。
二、证书登录是什么?
证书登录就是为了解决上面的缺点而设计的。它引入了一个证书颁发机构(Certificate1 authority,简称 CA),对信任的服务器颁发服务器证书,对信任的用户颁发用户证书。
登录时,用户和服务器不需要提前知道彼此的公钥,只需要交换各自的证书,验证是否可信即可。
证书登录的主要优点有两个:(1)用户和服务器不用交换公钥,这更容易管理,也具有更好的可扩展性。(2)证书可以设置到期时间,而公钥没有到期时间。针对不同的情况,可以设置有效期很短的证书,进一步提高安全性。
三、证书登录的流程
SSH 证书登录之前,如果还没有证书,需要生成证书。具体方法是:(1)用户和服务器都将自己的公钥,发给 CA;(2)CA 使用服务器公钥,生成服务器证书,发给服务器;(3)CA 使用用户的公钥,生成用户证书,发给用户。
有了证书以后,用户就可以登录服务器了。整个过程都是 SSH 自动处理,用户无感知。
第一步,用户登录服务器时,SSH 自动将用户证书发给服务器。
第二步,服务器检查用户证书是否有效,以及是否由可信的 CA 颁发。
第三步,SSH 自动将服务器证书发给用户。
第四步,用户检查服务器证书是否有效,以及是否由信任的 CA 颁发。
第五步,双方建立连接,服务器允许用户登录。
四、生成 CA 的密钥
证书登录的前提是,必须有一个 CA,而 CA 本质上就是一对密钥,跟其他密钥没有不同,CA 就用这对密钥去签发证书。
虽然 CA 可以用同一对密码签发用户证书和服务器证书,但是出于安全性和灵活性,最好用不同的密钥分别签发。所以,CA 至少需要两对密钥,一对是签发用户证书的密钥,假设叫做user_ca
,另一对是签发服务器证书的密钥,假设叫做host_ca
。
使用下面的命令,生成user_ca
。
# 生成 CA 签发用户证书的密钥 $ ssh-keygen -t rsa -b 4096 -f ~/.ssh/user_ca -C user_ca
上面的命令会在~/.ssh
目录生成一对密钥:user_ca
(私钥)和user_ca.pub
(公钥)。
这个命令的各个参数含义如下。
-t rsa
:指定密钥算法 RSA。-b 4096
:指定密钥的位数是4096位。安全性要求不高的场合,这个值可以小一点,但是不应小于1024。-f ~/.ssh/user_ca
:指定生成密钥的位置和文件名。-C user_ca
:指定密钥的识别字符串,相当于注释,可以随意设置。
使用下面的命令,生成host_ca
。
# 生成 CA 签发服务器证书的密钥 $ ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca
上面的命令会在~/.ssh
目录生成一对密钥:host_ca
(私钥)和host_ca.pub
(公钥)。
现在,~/.ssh
目录应该至少有四把密钥。
~/.ssh/user_ca
~/.ssh/user_ca.pub
~/.ssh/host_ca
~/.ssh/host_ca.pub
五、CA 签发服务器证书
有了 CA 以后,就可以签发服务器证书了。
签发证书,除了 CA 的密钥以外,还需要服务器的公钥。一般来说,SSH 服务器(通常是sshd
)安装时,已经生成密钥/etc/ssh/ssh_host_rsa_key
了。如果没有的话,可以用下面的命令生成。
$ sudo ssh-keygen -f /etc/ssh/ssh_host_rsa_key -b 4096 -t rsa
上面命令会在/etc/ssh
目录,生成ssh_host_rsa_key
(私钥)和ssh_host_rsa_key.pub
(公钥)。然后,需要把服务器公钥ssh_host_rsa_key.pub
,复制或上传到 CA 所在的服务器。
上传以后,CA 就可以使用密钥host_ca
为服务器的公钥ssh_host_rsa_key.pub
签发服务器证书。
$ ssh-keygen -s host_ca -I host.example.com -h -n host.example.com -V +52w ssh_host_rsa_key.pub
上面的命令会生成服务器证书ssh_host_rsa_key-cert.pub
(服务器公钥名字加后缀-cert
)。这个命令各个参数的含义如下。
-s
:指定 CA 签发证书的密钥。-I
:身份字符串,可以随便设置,相当于注释,方便区分证书,将来可以使用这个字符串撤销证书。-h
:指定该证书是服务器证书,而不是用户证书。-n host.example.com
:指定服务器的域名,表示证书仅对该域名有效。如果有多个域名,则使用逗号分隔。用户登录该域名服务器时,SSH 通过证书的这个值,分辨应该使用哪张证书发给用户,用来证明服务器的可信性。-V +52w
:指定证书的有效期,这里为52周(一年)。默认情况下,证书是永远有效的。建议使用该参数指定有效期,并且有效期最好短一点,最长不超过52周。ssh_host_rsa_key.pub
:服务器公钥。
生成证书以后,可以使用下面的命令,查看证书的细节。
$ ssh-keygen -L -f ssh_host_rsa_key-cert.pub
最后,为证书设置权限。
$ chmod 600 ssh_host_rsa_key-cert.pub
六、CA 签发用户证书
下面,再用 CA 签发用户证书。这时需要用户的公钥,如果没有的话,客户端可以用下面的命令生成一对密钥。
$ ssh-keygen -f ~/.ssh/user_key -b 4096 -t rsa
上面命令会在~/.ssh
目录,生成user_key
(私钥)和user_key.pub
(公钥)。
然后,将用户公钥user_key.pub
,上传或复制到 CA 服务器。接下来,就可以使用 CA 的密钥user_ca
为用户公钥user_key.pub
签发用户证书。
$ ssh-keygen -s user_ca -I [email protected] -n user -V +1d user_key.pub
上面的命令会生成用户证书user_key-cert.pub
(用户公钥名字加后缀-cert
)。这个命令各个参数的含义如下。
-s
:指定 CA 签发证书的密钥-I
:身份字符串,可以随便设置,相当于注释,方便区分证书,将来可以使用这个字符串撤销证书。-n user
:指定用户名,表示证书仅对该用户名有效。如果有多个用户名,使用逗号分隔。用户以该用户名登录服务器时,SSH 通过这个值,分辨应该使用哪张证书,证明自己的身份,发给服务器。-V +1d
:指定证书的有效期,这里为1天,强制用户每天都申请一次证书,提高安全性。默认情况下,证书是永远有效的。user_key.pub
:用户公钥。
生成证书以后,可以使用下面的命令,查看证书的细节。
$ ssh-keygen -L -f user_key-cert.pub
最后,为证书设置权限。
$ chmod 600 user_key-cert.pub
七、服务器安装证书
CA 生成服务器证书ssh_host_rsa_key-cert.pub
以后,需要将该证书发回服务器,可以使用下面的scp
命令,将证书拷贝过去。
$ scp ~/.ssh/ssh_host_rsa_key-cert.pub [email protected]:/etc/ssh/
然后,将下面一行添加到服务器配置文件/etc/ssh/sshd_config
。
HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
上面的代码告诉 sshd,服务器证书是哪一个文件。
重新启动 sshd。
$ sudo systemctl restart sshd # 或者 $ sudo service sshd restart
八、服务器安装 CA 公钥
为了让服务器信任用户证书,必须将 CA 签发用户证书的公钥user_ca.pub
,拷贝到服务器。
$ scp ~/.ssh/user_ca.pub [email protected]:/etc/ssh/
上面的命令,将 CA 签发用户证书的公钥user_ca.pub
,拷贝到 SSH 服务器的/etc/ssh
目录。
然后,将下面一行添加到服务器配置文件/etc/ssh/sshd_config
。
TrustedUserCAKeys /etc/ssh/user_ca.pub
上面的做法是将user_ca.pub
加到/etc/ssh/sshd_config
,这会产生全局效果,即服务器的所有账户都会信任user_ca
签发的所有用户证书。
另一种做法是将user_ca.pub
加到服务器某个账户的~/.ssh/authorized_keys
文件,只让该账户信任user_ca
签发的用户证书。具体方法是打开~/.ssh/authorized_keys
,追加一行,开头是@cert-authority principals="..."
,然后后面加上user_ca.pub
的内容,大概是下面这个样子。
@cert-authority principals="user" ssh-rsa AAAAB3Nz...XNRM1EX2gQ==
上面代码中,principals="user"
指定用户登录的服务器账户名,一般就是authorized_keys
文件所在的账户。
重新启动 sshd。
$ sudo systemctl restart sshd # 或者 $ sudo service sshd restart
至此,SSH 服务器已配置为信任user_ca
签发的证书。
九、客户端安装证书
客户端安装用户证书很简单,就是从 CA 将用户证书user_key-cert.pub
复制到客户端,与用户的密钥user_key
保存在同一个目录即可。
十、客户端安装 CA 公钥
为了让客户端信任服务器证书,必须将 CA 签发服务器证书的公钥host_ca.pub
,加到客户端的/etc/ssh/ssh_known_hosts
文件(全局级别)或者~/.ssh/known_hosts
文件(用户级别)。
具体做法是打开ssh_known_hosts
或known_hosts
文件,追加一行,开头为@cert-authority *.example.com
,然后将host_ca.pub
文件的内容(即公钥)粘贴在后面,大概是下面这个样子。
@cert-authority *.example.com ssh-rsa AAAAB3Nz...XNRM1EX2gQ==
上面代码中,*.example.com
是域名的模式匹配,表示只要服务器符合该模式的域名,且签发服务器证书的 CA 匹配后面给出的公钥,就都可以信任。如果没有域名限制,这里可以写成*
。如果有多个域名模式,可以使用逗号分隔;如果服务器没有域名,可以用主机名(比如host1,host2,host3
)或者 IP 地址(比如11.12.13.14,21.22.23.24
)。
然后,就可以使用证书,登录远程服务器了。
$ ssh -i ~/.ssh/user_key [email protected]
上面命令的-i
参数用来指定用户的密钥。如果证书与密钥在同一个目录,则连接服务器时将自动使用该证书。
十一、废除证书
废除证书的操作,分成用户证书的废除和服务器证书的废除两种。
服务器证书的废除,用户需要在known_hosts
文件里面,修改或删除对应的@cert-authority
命令的那一行。
用户证书的废除,需要在服务器新建一个/etc/ssh/revoked_keys
文件,然后在配置文件sshd_config
添加一行,内容如下。
RevokedKeys /etc/ssh/revoked_keys
revoked_keys
文件保存不再信任的用户公钥,由下面的命令生成。
$ ssh-keygen -kf /etc/ssh/revoked_keys -z 1 ~/.ssh/user1_key.pub
上面命令中,-z
参数用来指定用户公钥保存在revoked_keys
文件的哪一行,这个例子是保存在第1行。
如果以后需要废除其他的用户公钥,可以用下面的命令保存在第2行。
$ ssh-keygen -ukf /etc/ssh/revoked_keys -z 2 ~/.ssh/user2_key.pub
十二、参考链接
- SSH Emergency Access, Carl Tashian
- Using OpenSSH Certificate Authentication, Red Hat Enterprise Linux Deployment Guide
- How to SSH Properly, Gus Luxton
(完)
hmy 说:
证书方式的ssh还有一个作用,可以限制用户执行有限的命令。 直接写死在证书里面。
2020年7月 8日 13:32 | # | 引用
荒原之梦 说:
虽然我用过证书登陆服务器,但是看了阮老师的教程才知道原来的我只是一知半解而已,谢谢阮老师 :)
2020年7月 8日 13:41 | # | 引用
kl0 说:
全文看完,证书登陆也没有解决 ·非证书登录的缺点· 。反而变得更复杂了。最简单的ansible就可以管理多个机器,跑个命令就revoke了,简单直接和高效。
2020年7月 8日 16:37 | # | 引用
JavasBoy 说:
SSH 证书登录方式值得推广普及
2020年7月 8日 16:53 | # | 引用
ponder 说:
把文章写得简明,是一种很强的能力。
2020年7月 8日 18:22 | # | 引用
reatang 说:
我都是直接把公钥丢上去的秘钥登录。
2020年7月 8日 20:50 | # | 引用
天宇 说:
没用过证书登录,一般都用密钥登录登录,但同时公司也用vpn解决员工离职后的问题,直接从vpn中删除离职员工权限,自然没办法登录服务器了。
2020年7月 9日 09:09 | # | 引用
王小右 说:
我一直用的账号密码登陆的,还是要学学密钥方式登陆。
2020年7月 9日 23:20 | # | 引用
liuxiang 说:
如果我的10000台服务器都信任了某个 CA(x), 某用户(u) 获得了CA(x) 颁发的证书,那是不是说用户(u) 就可以登陆我这 10000 台机器了呢?如果我想限制用户(u) 指能登陆其中的10台该怎么操作呢?在CA(x)颁给用户(u) 的证书中指定?
2020年7月10日 18:12 | # | 引用
pandolia 说:
好像 Xshell 无法配置用证书登录。
2020年7月11日 18:07 | # | 引用
玛雅牛 说:
# 生成 CA 签发服务器证书的密钥
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/host_ca -C host_ca
这一行要加入~/.ssh/
2020年7月15日 01:42 | # | 引用
无色橘子 说:
不知道什么鬼,一直提示permission denied,重新设置过服务器和本地秘钥对和证书文件的权限600,还是一直提示=。=
2020年8月 6日 21:08 | # | 引用
SSH server 说:
It seems that Xshell cannot be configured to log in with a certificate.
2020年8月 9日 04:27 | # | 引用
Bob Linus 说:
SSH证书登录适用于多用户、多服务器的场景,如果使用密钥方式,每一对客户端/服务器都要单独预置公钥;而使用证书方式,只需要统一预置一次CA公钥,每一对客户端/服务器自身的公钥在网络协议中传递。
2020年8月21日 11:43 | # | 引用
乱码 说:
这篇博客可以和阮老师的相互借鉴:http://www.artishes.com/index.php/2020/08/29/https/
2020年8月31日 10:04 | # | 引用
fighter 说:
可是很多ssh客户端并不支持证书登录诶。。
2021年1月 5日 22:08 | # | 引用
tank 说:
感觉多此一举,用个堡垒机就完事了,用户不用提供任何公钥私钥,服务器也没必要提供公钥私钥
2021年3月31日 16:01 | # | 引用
小昕 说:
我用了两台raspberry做服务器,一台raspberry做CA服务器,一台mac做客户机,结果mac每次登录都直接要求输入密码,做了两次了,还是没有成功。。。。。
2021年7月15日 17:28 | # | 引用
lijinzhe 说:
xshell7(Free for Home/School)是可以支持的。
dos下(OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2)也是支持的。
除此之外,windows下还有哪些客户端可以使用呢?不知道putty和mobaXterm是否支持?想找一个可以支持SSH、sftp证书登录的免费的客户端。
2021年9月18日 13:27 | # | 引用
outman 说:
不在证书层面做。证书只解决认证问题。你这个是授权问题。
2021年12月 9日 10:48 | # | 引用
secondbanana 说:
证书登陆解决了不需要服务器和用户间保存对方公钥,解耦。引入了CA这一公信力的‘中介’
2022年5月10日 13:26 | # | 引用
happydanye 说:
这里面ca证书似乎没看到用途。正常直接把用户的公钥添加到authorized_keys里面就可以了。用户登录的时候严验证一下指纹,或者把服务器证书添加到knowhosts中即可。
ca证书这种方法似乎可以限制证书的时间和执行的命令?但是阮老师这里没怎么讲。阮老师讲的这些用法,似乎直接用之前的办法都能实现。
2023年8月15日 12:31 | # | 引用
佐蓝 说:
然后就变成和CA做了双边的关联,感觉还是没解决
2023年12月15日 15:20 | # | 引用
mib008 说:
我想知道,这个全套流程可否用openssl命令完成?
2024年9月10日 09:03 | # | 引用
周星星 说:
“revoked_keys文件保存不再信任的用户公钥,由下面的命令生成”
这是文章中废除证书的第二部分
为啥是用户公钥?不是说证书的出现就是服务不用保存用户的公钥吗?现在又有了,不知道我的理解对不对
2024年9月27日 17:13 | # | 引用