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个方面:

  1. 有证书管理中心时(比如vault),nginx镜像在启动的时候,从vault获取证书。如果获取不到,但是证书又是必要的,就生成证书,同步到vault,以便镜像重启时可以获取。这个方案的技术细节可参考该文:https://developer.epages.com/blog/tech-stories/managing-lets-encrypt-certificates-in-vault/
  2. 没有证书管理中心,直接动态生成,具体看下这个镜像,它就是这么做的: https://github.com/SteveLTN/https-portal

这些处理完全可以做成一个新的nginx镜像以便重复使用。具体的镜像如何处理将另开文章讨论。

安装certbot阿里云DNS插件出错?

pip install certbot-dns-aliyun

执行上面命令时报出下面的错误:

‘extras_require‘ must be a dictionary whose values are strings or lists of strings containing valid project/version requirement specifiers. #1268

解决方法:

pip install --user --upgrade setuptools

参考

相关推荐