利用let's encrypt为服务器部署一套可定时自动更新的https证书

1.let’s encrypt

官网:https://letsencrypt.org/

         Let's Encrypt 的最大贡献是它的 ACME 协议,第一份全自动服务器身份验证协议,以及配套的基础设施和客户端。这是为了解决一直以来 HTTPS TLS X.509 PKI 信任模型,即证书权威(Certificate Authority, CA)模型缺陷的一个起步。

        在客户端-服务器数据传输中,公私钥加密使得公钥可以明文传输而依然保密数据,但公钥本身是否属于服务器,或公钥与服务器是否同属一个身份,是无法简单验证的。证书权威模型通过引入事先信任的第三方,由第三方去验证这一点,并通过在服务器公钥上签名的方式来认证服务器。第三方的公钥则在事先就约定并离线准备好,以备访问时验证签名之用。这个第三方就称为证书权威,简称CA。相应的,CA验证过的公钥被称为证书。

        问题是,如果服务器私钥泄露,CA无法离线使对应的证书无效化,只能另外发布无效记录供客户端查询。也就是说,在私钥泄露到CA发布无效记录的窗口内,中间人可以肆意监控服-客之间的传输。如果中间人设法屏蔽了客户端对无效记录的访问,那么直到证书过期,中间人都可以进行监控。而由于当前CA验证和签发证书大多手动,证书有效期往往在一年到三年。

        Let's Encrypt 签发的证书有效期只有90天,甚至希望缩短到60天。有效期越短,泄密后可供监控的窗口就越短。为了支撑这么短的有效期,就必须自动化验证和签发。因为自动化了,长远而言,维护反而比手动申请再安装要简单。

来自:https://www.zhihu.com/question/36710815/answer/124385878

2.自动签发实现方案

        由于官方实现方案的不靠谱,所以采用了Github上现有的 xdtianyu 同学提供的脚本会十分方便。

       使用教程可以参考:

https://github.com/xdtianyu/scripts/blob/master/lets-encrypt/README-CN.md

下载脚本到本地:

wget https://raw.githubusercontent.com/xdtianyu/scripts/master/lets-encrypt/letsencrypt.conf
wget https://raw.githubusercontent.com/xdtianyu/scripts/master/lets-encrypt/letsencrypt.sh
chmod +x letsencrypt.sh

配置文件,需要指出其中的坑:

let's encrypt的签发过程中需要对域名进行鉴权,所以会向域名所指向的根目录写入鉴权文件,也就是在根目录新建目录“.well-known/acme-challenge/”向其中写入一个随机文件,再访问:<域名>/.well-known/acme-challenge/{随机文件}以验证域名所有权。

所以,letsencrypt.conf文件配置说明:

ACCOUNT_KEY="letsencrypt-account.key"//账户密钥,可以改成自定义的标示,当然也可以不改
DOMAIN_KEY="example.com.key"//域名私钥 
DOMAIN_DIR="/var/www/example.com"//域名鉴权目录
DOMAINS="DNS:example.com,DNS:whatever.example.com"//要申请证书的域名,可以一个,可以多个
#ECC=TRUE
#LIGHTTPD=TRUE 

其中“DOMAIN_DIR”设置最好是配置一个单独的路径去做这个鉴权的事情:

比如此处需要给aishan100.com申请https证书,那么

DOMAIN_DIR="/var/www/challenges/aishan100.com"//域名鉴权目录

然后nginx的aishan100.com的站点配置中要加个目录映射:

location /.well-known/acme-challenge/{
  alias /var/www/challenges/aishan100.com/.well-known/acme-challenge/;
  try_files $uri = 404;
}

然后重启nginx。

接着就可以按照教程继续:

./letsencrypt.sh letsencrypt.conf

如果项目路径没有配置成功,运行以上这个命令会报错:

Verifying pvm-api-test.sunallies.com...
Traceback (most recent call last):
  File "/tmp/acme_tiny.py", line 198, in <module>
    main(sys.argv[1:])
  File "/tmp/acme_tiny.py", line 194, in main
    signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca)
  File "/tmp/acme_tiny.py", line 123, in get_crt
    wellknown_path, wellknown_url))
ValueError: Wrote file to /home/www/challenges/.well-known/acme-challenge/5Qe_91IDlAczbzpBe5yN5uMHtYECgMesB-BBt2Z6s7Y, but couldn't download http://pvm-api-test.sunallies.com/.well-known/acme-challenge/5Qe_91IDlAc

那就要好好核对一下DOMAIN_DIR配置咯!

将会生成如下几个文件

lets-encrypt-x1-cross-signed.pem
example.chained.crt          # 即网上搜索教程里常见的 fullchain.pem
example.com.key              # 即网上搜索教程里常见的 privkey.pem 
example.crt
example.csr

在 nginx 里添加 ssl 相关的配置

ssl_certificate     /path/to/cert/example.chained.crt;
ssl_certificate_key /path/to/cert/example.key;

cron 定时任务

每个月自动更新一次证书,可以在脚本最后加入 service nginx reload等重新加载服务。

0 0 1 * * /etc/nginx/certs/letsencrypt.sh /etc/nginx/certs/letsencrypt.conf >> /var/log/lets-encrypt.log 2>&1