SSL证书的申请、自动续期与常见问题
背景
网络上已经有很多关于Let's encrypt
免费申请证书的文章,但是基本只谈最基础用法,立刻用起来是没问题了。但是在实际使用中,碰到了一些问题则比较头疼。整理以后,大概出在以下几个方面:
- 单域名和泛域名的申请方式上有区别;
- 单域名和泛域名的证书如何自动续期;
- 如果提前验证自动续期是正常的;
- 选择文件验证还是DNS验证;
- 如何与docker有效配合;
本文的价值在于阐述了解决这些问题的具体思路。
安装工具
申请Let's encrypt
证书,既可以使用certbot
官方工具,也可以使用acme.sh
第三方工具,各有各的好处,主要看偏好,本文使用certbot
演示。
安装指南 :直接进《certbot官方安装指南》,选择nginx和自己的操作系统,就会生成对应的安装指南。
文件验证与DNS验证
在给域名申请证书时,Let's encrypt
需要先验证你有对域名的控制权。这个验证方式有2种:文件验证和DNS验证。
文件验证的原理是在你的网站下面放一个验证文件(/.well-known/acme-challenge/
目录下),certbot
通过域名直接访问到这个验证文件,能访问到说明是你的网站,也就可以申请证书。
DNS验证的原理是你需要创建一条TXT
的记录,设置指定值,certbot
检查到这条记录就完成验证。
两种方式各有其适用场景,对于自己可以控制的域名,直接通过DNS
验证是最便捷的,不需要任何nginx
的辅助就可以完成申请和续期。但是很多时候,我们是给客户提供服务,并没有域名的控制权,就只能使用文件验证的方式。假如使用了DNS
的验证方式,客户首次申请是要手动改DNS
记录,等到过期后,也不能自动续期,需要再次手动调整DNS
记录。
为保证域名正常续期,本文的单域名证书的申请上,使用的是文件验证的方式。
单域名证书的申请与自动续期
申请证书
对于单域名,我们统一使用文件验证的方式,因此要先写好nginx
的配置文件:
server {
listen 80;
server_name www.papamk.com;
root /var/www/html;
server_tokens off;
location /.well-known/acme-challenge/ {
root /var/www/html;
access_log off;
}
return 301 https://www.papamk.com$request_uri;
}
访问curl http://www.papamk.com
将会返回如下结果:
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
接着开始申请证书:
certbot certonly --webroot -w /var/www/html -d www.papamk.com
编辑nginx
配置文件,给HTTPS
增加证书的相关配置,重点是以下2名:
ssl_certificate /etc/letsencrypt/live/www.papamk.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.papamk.com/privkey.pem;
完整的nginx
配置如下:
server {
listen 443 ssl;
server_name www.papamk.com;
root /var/www/html;
index index.php index.html index.htm;
access_log /dev/stdout;
error_log /dev/stderr;
ssl_certificate /etc/letsencrypt/live/portfolia.papamk.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/portfolia.papamk.com/privkey.pem;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
internal;
try_files $uri /index.php =404;
fastcgi_intercept_errors on;
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
以上配置使用
php
作为示例。
更新证书与自动续期
certbot renew --deploy-hook 'nginx -s reload'
当证书在30天内过期时,就会自动更新,更新成功会重新加载nginx
。
放在crontab中,每天凌晨1点更新。
0 1 * * * certbot renew --deploy-hook 'nginx -s reload'
泛域名证书的申请与自动续期
泛域名证书只支持DNS验证的方式,所以如果不能直接修改DNS记录,泛域名证书过期后就会需要手动更新。要解决这个问题,需要使用对应的DNS插件,国外的主流DNS插件都有内置支持,如果是国内。我基本都使用阿里云,官方certbot
没有对应的DNS插件,但是第三方有,传送门:
https://github.com/tengattack/certbot-dns-aliyun
安装certbot阿里云dns插件
pip install certbot-dns-aliyun
创建aliyun.ini
,写入授权信息(授权账号需要有AliyunDNSFullAccess
)
certbot_dns_aliyun:dns_aliyun_access_key = 12345678
certbot_dns_aliyun:dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef
修改下权限:
chmod 600 aliyun.ini
接下来就可以开始使用了。
申请证书
certbot certonly -a certbot-dns-aliyun:dns-aliyun --certbot-dns-aliyun:dns-aliyun-credentials aliyun.ini -d '*.yourdomain.com' --server https://acme-v02.api.letsencrypt.org/directory
更新证书与自动续期
certbot renew -a certbot-dns-aliyun:dns-aliyun --certbot-dns-aliyun:dns-aliyun-credentials aliyun.ini --server https://acme-v02.api.letsencrypt.org/directory --deploy-hook 'nginx -s reload'
放到crontab
每天自动检查:
0 1 * * * certbot renew -a certbot-dns-aliyun:dns-aliyun --certbot-dns-aliyun:dns-aliyun-credentials aliyun.ini --server https://acme-v02.api.letsencrypt.org/directory --deploy-hook 'nginx -s reload'
这里的每天检查与单域名的每日检查2选1即可。
常见问题
证书存放位置?
证书一般存放在如下路径:/etc/letsencrypt/live/
。
每个域名一个文件夹,可以看到www.yourdomain.com
的目录。
如何提前验证自动续期后可以重新加载nginx?
先进浏览器,看下当前证书的过期时间,然后执行强制更新:
certbot renew --force-renew --deploy-hook 'nginx -s reload'
再次进浏览器,查看当前证书的过期时间,如果日期有变,说明nginx
重新加载了。
如何保证过期前证书就得到更新?
certbot renew
会对30天
以内的证书更新,只要保证cron
有执行每天定时更新一次就没问题。第一次使用时,证书在30以内要注意下这个问题,确保它们得到了更新。
域名解析到内网,申请证书不成功?
根据文件验证的原理,certbot
无法访问到你的内网,也就验证不通过。使用DNS
验证可以申请成功。
Docker镜像的nginx如何获取证书与更新?
在许多文章中,都是通过volume
将主机的/etc/letsencrypt
挂载到容器中,但是在生产环境时,由于应用变成了有状态应用,这种方式有很大的局限性。从A机器迁移到B机器时,将会立刻报错,必须手动解决完证书问题。推荐的做法是将证书做成动态获取的,具体来说要考虑以下2个方面:
- 有证书管理中心时(比如
vault
),nginx
镜像在启动的时候,从vault
获取证书。如果获取不到,但是证书又是必要的,就生成证书,同步到vault
,以便镜像重启时可以获取。这个方案的技术细节可参考该文:https://developer.epages.com/blog/tech-stories/managing-lets-encrypt-certificates-in-vault/ - 没有证书管理中心,直接动态生成,具体看下这个镜像,它就是这么做的: https://github.com/SteveLTN/https-portal
这些处理完全可以做成一个新的nginx
镜像以便重复使用。具体的镜像如何处理将另开文章讨论。
安装certbot阿里云DNS插件出错?
pip install certbot-dns-aliyun
执行上面命令时报出下面的错误:
解决方法:
pip install --user --upgrade setuptools