Nginx安装Let’s Encrypt 免费SSL证书

Let’s Encrypt 是个免费、自动化、开放的证书签发服务。 官网: https://letsencrypt.org/

可以使用Let’s Encrypt 官网提供的工具来申请证书,本文用了 acme-tiny 这个更为小巧的开源工具 https://github.com/diafygi/acme-tiny

Nginx支持ssl

Nginx必须有http_ssl_module,可通过nginx -V来查看。如果当初编译nginx时没有加入这个模块,则需要重新编译。

创建帐号和 CSR 文件

如果多个域名请注意修改相应前缀区分,避免造成证书混乱。

文章中出现的域名部分以 yurendu.com 为例。

mkdir ssl/

cd ssl

openssl genrsa 4096 > yurendu_com_account.key

openssl genrsa 4096 > yurendu_com_domain.key

# 此处可写多个域名
openssl req -new -sha256 -key yurendu_com_domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:yurendu.com,DNS:www.yurendu.com")) > yurendu_com_domain.csr

配置验证服务

CA 在签发 DV(Domain Validation)证书时,需要验证域名所有权。

传统 CA 的验证方式一般是往 admin@yurendu.com 发验证邮件,而 Let’s Encrypt 是在你的服务器上生成一个随机验证文件,再通过创建 CSR 时指定的域名访问,如果可以访问则表明你对这个域名有控制权。

首先创建用于存放验证文件的目录,例如:

mkdir /home/www/challenges/

然后修改你的Nginx 域名配置文件并重新载入服务

server {
    server_name www.yurendu.com yurendu.com;

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

    location / {
        rewrite ^/(.*)$ https://www.yurendu.com/$1 permanent;
    }
}

获取网站证书

下载脚本

wget https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py

执行

python acme_tiny.py --account-key ./yurendu_com_account.key --csr ./yurendu_com_domain.csr --acme-dir /home/www/challenges/ > ./yurendu_com_signed.crt

要求 Python 版本最低为 2.7, 2.6版本需要安装 argparse

如果出现以下错误,那应该是Nginx 配置问题,确定目录是否正确,域名是否正确绑定等。

ValueError: Wrote file to /home/xxx/www/challenges/oJbvpIhkwkBGBAQUklWJXyC8VbWAdQqlgpwUJkgC1Vg, but couldn't download http://www.yurendu.com/.well-known/acme-challenge/oJbvpIhkwkBGBAQUklWJXyC8VbWAdQqlgpwUJkgC1Vg

搞定网站证书后,还要下载 Let’s Encrypt 的中间证书。中间证书都是一样的,多个域名无需重复下载。

wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem

在 Nginx 配置中,需要把中间证书和网站证书合在一起:

cat yurendu_com_signed.crt intermediate.pem > yurendu_com_chained.pem

配置并重启Nginx

最终,修改 Nginx 中有关证书的配置并 reload 服务即可:

生成 dhparam (非必须)

openssl dhparam -out /root/ssl/dhparam.pem 2048

然后在Nginx配置文件中加入

ssl on;
ssl_certificate /root/ssl/yurendu_com_chained.pem;
ssl_certificate_key /root/ssl/yurendu_com_domain.key;
ssl_dhparam /root/ssl/dhparam.pem;

供参考的详细配置如下:

server {
  listen 443 ssl default deferred;
  server_name www.yurendu.com yurendu.com;
  ssl_certificate /root/ssl/yurendu_com_chained.pem;
  ssl_certificate_key /root/ssl/yurendu_com_domain.key;
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 5m;
  ssl_dhparam /root/ssl/dhparam.pem;
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

  # ... the rest of your configuration
}

配置 crontab 及自动更新

Let's encrypt 生成的证书有效期为3个月,我们可以写脚本自动续期。

新建 /root/ssl/renew.sh

#!/bin/bash

cd /root/ssl/

python acme_tiny.py --account-key ./yurendu_com_account.key --csr ./yurendu_com_domain.csr --acme-dir /home/www/challenges/ > ./yurendu_com_signed.crt || exit

cat yurendu_com_signed.crt intermediate.pem > yurendu_com_chained.pem

nginx -s reload

保存后 chmod 给其可执行权限。

crontab -e 在 crontab 中添加

0 0 1 * * /root/ssl/renew.sh >> /root/ssl/update_ssl.log