阅读本篇文章需要先完成上一篇:古法手作。
在上一篇文章中,我们用古法手作的方式从CA取得证书并安装到web服务器,能用是能用,但是这证书的整个生命周期管理全是纯手动,既麻烦又容易出错。这篇文章我们将使用ACME协议来自动化,做到证书自动申请、续期、安装。
C ACME协议:
自动证书管理环境通常包括两部分:
一部分是ACME服务端,由证书颁发机构控制,比如我们熟知的Let’s Encrypt和zerossl就是ACME服务端,不过他们签发的是公共证书;在本系列文章里,Vault是ACME服务端。
另一部分是ACME客户端,一般由web服务器/负载均衡器或独立程序控制,比如Caddy、Certbot、acme.sh等。
ACME客户端会发送证书请求到ACME服务端,其中包含ACME客户端要用的“common name”,一般也是web服务器监听的FQDN。而ACME服务器则须验证ACME客户端是否对相应域名有控制权。它会向ACME客户端发起“挑战”(challenge)。“挑战”的方法最常用的是http01,即ACME服务器告诉ACME客户端一些随机内容,要求对方将此内容放置于web服务器内;然后ACME服务器会启动web客户端去ACME客户端的web服务器访问,能访问到刚才的内容即可确定“挑战”成功。对于不能开放TCP 80端口的ACME客户端,“挑战”也可以使用DNS解析的方法完成。
D ACME服务端
先看看是否开启了ACME服务:
vault pki health-check ca-int-x1/
显示AIA信息:
vault read ca-int-x1/config/cluster
以上要是发现没有配置过AIA路径的就得:
vault write ca-int-x1/config/cluster \
path=$VAULT_ADDR/v1/ca-int-x1 \
aia_path=$VAULT_ADDR/v1/ca-int-x1
ℹ️ ACME客户端须信任Vault服务捆绑的数字证书(假如是自签名),详情见上一篇文章。
调整中间CA的参数:
vault secrets tune \
-passthrough-request-headers=If-Modified-Since \
-allowed-response-headers=Last-Modified \
-allowed-response-headers=Location \
-allowed-response-headers=Replay-Nonce \
-allowed-response-headers=Link \
ca-int-x1
开启ACME服务端:
vault write ca-int-x1/config/acme enabled=true
E ACME客户端
ACME客户端有好几家呢,看过前文的老读者朋友们都知道我最喜欢Caddy了。它作为http服务器同时也有ACME客户端的能力。
Caddy
Caddy集成了ACME客户端功能,在本系列第一篇文章中我们就用这个功能来获取免费的公共证书,但是现在我们用它来与我们自己的ACME服务端交互。
这次我使用一台运行Debian的Linux机器,运行Caddy,它的FQDN是web-dev-02.lab.miyunda.com,还是先准备一个网页文件夹
sudo mkdir -p /caddyroot/web-dev-02
sudo nano /caddyroot/web-dev-02/index.html
其内容为:
<!DOCTYPE html>
<html>
<head>
<title>Hello nerd</title>
</head>
<body>
<p>我是web-dev-02</p>
</body>
</html>
修改Caddy的配置文件
sudo nano /etc/caddy/Caddyfile
比起上一篇文章,在配置文件中,我们不再提供证书在文件系统中的路径,而是告诉Caddy去找ACME服务器:
{
acme_ca https://vault-dev.miyunda.com/v1/ca-int-x1/acme/directory
}
web-dev-02.lab.miyunda.com {
root * /caddyroot/web-dev-02
file_server
}
令新配置生效:
sudo systemctl reload caddy # 或者 sudo systemctl start caddy
查看日志文件:
sudo journalctl -xeu caddy | less
日志文件里面有值得我们关注的信息,下面展示了ACME服务“挑战”Caddy,方式为“http-01”,也就是ACME服务器访问了Caddy临时运行的web服务,以此确定了Caddy对这个FQDN web-dev-02.lab.miyunda.com的使用权。
Jun 13 21:22:46 web-dev-02 systemd[1]: Started caddy.service - Caddy.
░░ Subject: A start job for unit caddy.service has finished successfully
░░ Defined-By: systemd
░░ Support: https://www.debian.org/support
░░
░░ A start job for unit caddy.service hasfinished successfully.
░░
░░ The job identifier is 938.
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.093515,"msg":"serving initial configuration"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.0936847,"logger":"tls.obtain","msg":"acquiring lock","identifier":"web-dev-02.lab.miyunda.com"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.094313,"logger":"tls.obtain","msg":"lock acquired","identifier":"web-dev-02.lab.miyunda.com"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.0947223,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"web-dev-02.lab.miyunda.com"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.095472,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0004fdea0"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.0956464,"logger":"tls","msg":"cleaning storage unit","description":"FileStorage:/var/lib/caddy/.local/share/caddy"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.0957217,"logger":"tls","msg":"finished cleaning storage units"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.1640933,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["web-dev-02.lab.miyunda.com"],"ca":"https://vault-dev.miyunda.com/v1/ca-int-x1/acme/directory","account":""}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.1642356,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["web-dev-02.lab.miyunda.com"],"ca":"https://vault-dev.miyunda.com/v1/ca-int-x1/acme/directory","account":""}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.1676006,"logger":"http.acme_client","msg":"trying to solve challenge","identifier":"web-dev-02.lab.miyunda.com","challenge_type":"http-01","ca":"https://vault-dev.miyunda.com/v1/ca-int-x1/acme/directory"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.1703296,"logger":"http","msg":"served key authentication","identifier":"web-dev-02.lab.miyunda.com","challenge":"http-01","remote":"192.168.3.130:56076","distributed":false}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.4230063,"logger":"http.acme_client","msg":"validations succeeded; finalizing order","order":"https://vault-dev.miyunda.com/v1/ca-int-x1/acme/order/a3a5762c-7d6e-7ca9-0690-e571344156d5"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.4378805,"logger":"http.acme_client","msg":"successfully downloaded available certificate chains","count":1,"first_url":"https://vault-dev.miyunda.com/v1/ca-int-x1/acme/order/a3a5762c-7d6e-7ca9-0690-e571344156d5/cert"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.4382524,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"web-dev-02.lab.miyunda.com"}
Jun 13 21:22:46 web-dev-02 caddy[4573]: {"level":"info","ts":1718284966.4383457,"logger":"tls.obtain","msg":"releasing lock","identifier":"web-dev-02.lab.miyunda.com"}
可以用浏览器访问web服务器看看,也可以用命令行试试:
echo | openssl s_client -showcerts -servername web-dev-02.lab.miyunda.com -connect web-dev-02.lab.miyunda.com:443 2>/dev/null | openssl x509 -inform pem -noout -text
acme.sh
有的同学就喜欢用nginx,但它又不具备ACME能力,那还能不能享受ACME的便利呢?你的nginx不支持ACME,但是我们给它加上acme.sh来弥补这一点。acme.sh是一个开源的项目,旨在帮助不支持ACME协议的web服务器完成ACME服务器的“挑战”,下载证书、更新证书并安装。另外如Apache什么的,acme.sh对它的支持与nginx是类似的。
还是一个Debian机器,名字叫web-deb-03.lab.miyunda.com,给它安装nginx:
sudo apt install nginx
systemctl status nginx
在这台机器上安装acme.sh并启动http“挑战”,第4、5两行是为当前用户添加文件夹权限,因为acme.sh脚本需要在这里放置http-01挑战的随机内容。
sudo apt install curl -y
curl https://get.acme.sh | sh -s email=[email protected]
source ~/.bashrc
sudo chown root:$USER /var/www/html
sudo chmod g+wx /var/www/html
acme.sh --issue --webroot /var/www/html -d web-dev-03.lab.miyunda.com \
--server https://vault-dev.miyunda.com/v1/ca-int-x1/acme/directory
输出信息也描述了ACME挑战的过程。
[Fri Jun 14 09:07:36 PM CST 2024] Using CA: https://vault-dev.miyunda.com/v1/ca-int-x1/acme/directory
[Fri Jun 14 09:07:36 PM CST 2024] Single domain='web-dev-03.lab.miyunda.com'
[Fri Jun 14 09:07:37 PM CST 2024] Getting webroot for domain='web-dev-03.lab.miyunda.com'
[Fri Jun 14 09:07:37 PM CST 2024] Verifying: web-dev-03.lab.miyunda.com
[Fri Jun 14 09:07:37 PM CST 2024] Processing, The CA is processing your order, please just wait. (1/30)
[Fri Jun 14 09:07:40 PM CST 2024] Success
[Fri Jun 14 09:07:40 PM CST 2024] Verify finished, start to sign.
[Fri Jun 14 09:07:40 PM CST 2024] Lets finalize the order.
[Fri Jun 14 09:07:40 PM CST 2024] Le_OrderFinalize='https://vault-dev.miyunda.com/v1/ca-int-x1/acme/order/caa7b310-2244-7a86-1def-618a255f0609/finalize'
[Fri Jun 14 09:07:41 PM CST 2024] Downloading cert.
[Fri Jun 14 09:07:41 PM CST 2024] Le_LinkCert='https://vault-dev.miyunda.com/v1/ca-int-x1/acme/order/caa7b310-2244-7a86-1def-618a255f0609/cert'
[Fri Jun 14 09:07:41 PM CST 2024] Cert success.
-----BEGIN CERTIFICATE-----
MIIEszCCApugAwIBAgIUbPwZdAz7+vMgW1FFnt6jQaHBEJswDQYJKoZIhvcNAQEL
BQAwSzESMBAGA1UECxMJTWFya2V0aW5nMTUwMwYDVQQDEyx2YXVsdC1kZXYubWl5
...
cnIh1/1vixCSC3fjD3s//zwK+szqisssoI6isIH7i0qjakJ2VqtPDMNZYLD7v0Y1
e7qHKGmejw==
-----END CERTIFICATE-----~
创建相应的站点:
sudo mkdir /var/www/web-dev-03.lab.miyunda.com
sudo nano /var/www/web-dev-03.lab.miyunda.com/index.html # 内容自己编
sudo nano /etc/nginx/sites-enabled/web-dev-03.lab.miyunda.com.conf
server {
listen 443 ssl;
server_name web-dev-03.lab.miyunda.com;
ssl_certificate /etc/nginx/ssl/web-dev-03.lab.miyunda.com/web-dev-03.lab.miyunda.com.crt;
ssl_certificate_key /etc/nginx/ssl/web-dev-03.lab.miyunda.com/web-dev-03.lab.miyunda.com.key;
root /var/www/web-dev-03.lab.miyunda.com;
index index.html;
}
允许当前用户写入——更新证书时就有权限写文件了:
sudo mkdir -p /etc/nginx/ssl/web-dev-03.lab.miyunda.com
sudo chown root:$USER /etc/nginx/ssl/web-dev-03.lab.miyunda.com
sudo chmod g+wx /etc/nginx/ssl/web-dev-03.lab.miyunda.com
告诉acme.sh 如何安装证书——以后证书更新了它会自动安装:
acme.sh --installcert -d web-dev-03.lab.miyunda.com \
--keypath /etc/nginx/ssl/web-dev-03.lab.miyunda.com/web-dev-03.lab.miyunda.com.key \
--fullchainpath /etc/nginx/ssl/web-dev-03.lab.miyunda.com/web-dev-03.lab.miyunda.com.crt \
--reloadcmd 'sudo systemctl reload nginx'
这时证书已经安装好了,可以用浏览器访问试试,也可以用命令行:
curl -kv https://web-dev-03.lab.miyunda.com
我们可以看到acme.sh给自己添加了定时任务,每天一次检查是否要续期、更新。
crontab -l
42 11 * * * "~/.acme.sh"/acme.sh --cron --home "~/.acme.sh" > /dev/null
喜欢让它们夜里做事的话可以把第二个数字改成“02”:
crontab -e
以上我们介绍了两种使用ACME客户端的方法,两种方法都可以做到“一劳永逸”、“配置后不管”。
好了,看过就等于会了。下一篇文章还是对我们内部的CA使用ACME,但是Kubernetes。
返场:
我们在过家家的时候往往会觉得“这CA不好,我不要了”而删除之再弄个新的,这时需要让Caddy忘记以前的信息。要不它还会傻傻地联系以前的CA。
sudo rm -rf /var/lib/caddy/.local/share/caddy/acme
sudo rm -rf /var/lib/caddy/.local/share/caddy/certificates
sudo systemctl reload caddy
评论