Nginx 配置 SSL

安装 CertBot

CertBot 是用来申请 SSL 证书的。其安装方式可以在 Certbot 官网上查询。
你可以选择以下几种不同的方法。

通过 GitHub 仓库

首先,克隆 Certbot 在 GitHub 上的仓库到本地:

1
git clone https://github.com/certbot/certbot.git

在 certbot 文件夹下,有一个 shell 脚本,名为 certbot-auto。你可以将其软链接为 certbot,方便进行接下来的步骤。具体操作是,在你的家目录 ~ 下的.bashrc.bash_profile 文件中追加以下内容:

1
alias certbot=/path/to/your/certbot/certbot-auto

然后执行 source ~/.bashrcsource ~/.bash_profile,这样就可以直接使用 certbot 命令进行接下来的操作了。

通过包管理工具

当然,你也可以使用包管理工具安装 CertBot。以 yum 为例:

1
2
3
yum -y install yum-utils
yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional
yum install certbot

其它系统的安装方式同样可以在官网上找到。
不过经笔者测试,这种安装方式可能会产生错误,因此不建议使用。

通过 pip

直接执行 pip install certbot 即可。

其它

安装完成后,可以使用

1
sudo certbot -h

查看安装是否成功。如果报错,也可以尝试使用其它安装方式。
如果你使用了 Nginx 服务器,还可以执行

1
yum install certbot-nginx

安装 Certbot 的 Nginx 拓展。

配置 CertBot

执行

1
sudo certbot --nginx

即可自动完成配置。不过由于涉及到修改 nginx.conf,建议手动进行配置:

1
sudo certbot certonly --webroot -w /path/to/your/webroot -d example.com,www.example.com

/path/to/your/webroot 替换为 nginx 的根目录,example.com 替换为你的域名,多个域名用逗号隔开。
如果要申请泛域名证书,将上面的命令改为:

1
sudo certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns -d example.com,*.example.com

泛域名证书还需要验证 TXT 记录,比较麻烦。可以参考这篇文章:Let's Encrypt 现已正式支持泛域名证书
如果一切顺利,会输出以下内容:

1
2
3
4
5
6
7
8
9
10
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/example.com/fullchain.pem. Your cert
will expire on 20XX-XX-XX. To obtain a new or tweaked version of
this certificate in the future, simply run certbot again. To
non-interactively renew *all* of your certificates, run "certbot
renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

生成的证书将会存放在 /etc/letsencrypt/live/ 目录下。

配置 Nginx

接下来配置 nginx.conf:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
listen [::]:80; #监听ipv6,若无必要可以删去
#如果硬性要求全部走https协议,注释上两行
listen 443 ssl;
listen [::]:443 ssl; #监听ipv6,若无必要可以删去
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
#其余内容省略,无需更改

同理,将 example.com 替换为你的域名。如果还要支持 HTTP/2,请确保编译 Nginx 时设置了参数 --with-http_v2_module,然后在这个配置文件中,将 listen 443 ssl; 改为 listen 443 ssl http2;,ipv6 同理。
更改完成后,执行 nginx -s reload 重新加载配置,这时就可以使用 https 访问了。

更新证书

先在命令行模拟证书更新:

1
sudo certbot renew --dry-run

模拟更新成功的效果如下:

1
2
3
4
5
6
7
8
9
10
-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.com.conf
-------------------------------------------------------------------------------
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/example.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)

如果模拟成功,就可以使用

1
sudo certbot renew >> /var/log/le-renew.log

手动进行更新(注:证书只有在有效期小于一个月的时候才会更新,否则 CertBot 会提示无需更新)。
当然,你也可以通过 Crontab 来启用定时任务,自动更新证书。在命令行执行:

1
sudo crontab -e

在打开的文件中添加配置:

1
30 2 * * 1 /path/to/your/certbot renew >> /var/log/le-renew.log

这里建议使用 certbot 或 certbot-auto 的绝对地址,避免出现问题。这样,在每周一半夜 2 点 30 分就会执行 renew 任务,自动更新证书。
如果一切 OK,那么我们的配置到此结束!

关于 HSTS

如果你能够确保在未来足够长的时间内保持证书的有效性,则可以配置 HSTS。HSTS 是 HTTP Strict Transport Security 的缩写,它可以阻止基于 SSLStrip 的中间人攻击,这将增加访问的安全性。在 http 头中做出如下设置即可开启 HSTS:

1
Strict-Transport-Security: max-age=expireTime [; includeSubDomains] [; preload]

其中 expireTime 是过期时间,一旦用户通过 https 访问你开启了 HSTS 的站点,那么在 expireTime 之内再次访问,浏览器将强制启用 https—— 这样可以防止发送不安全的流量。但是,如果你的证书此时过期了,那么用户将无法访问你的网站,因为 http 访问会被浏览器禁止。
更进一步,你可以把自己的网站提交到 HSTS preload list 中。这是 Chrome 浏览器中的 HSTS 预载入列表,在该列表中的网站,使用 Chrome 浏览器访问时,会自动转换成 HTTPS。Firefox、Safari、Edge 浏览器也在采用这个列表。
加入 HSTS preload list 的条件为:

  • 有效的证书(如果使用 SHA-1 证书,必须是 2016 年前就会过期的)
  • 将所有 HTTP 流量重定向到 HTTPS
  • 确保所有子域名启用 HTTPS,特别是 www 子域名

同时输出的 HSTS 响应头部需要满足以下条件:

  • max-age 至少为一年,31536000 秒
  • 必须指定 includeSubdomains 参数
  • 必须支持 preload 参数

所以,一个典型满足 HSTS preload list 的响应头部为:

1
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload;

通过配置 Nginx,利用 add_header 加上这个响应头,然后就可以在 HSTS preload list 的官方网站上进行申请了。从申请到审核通过,时间在几天到几周不等。申请后,你可以随时查询最新的状态,也可以在 Chrome 浏览器的地址框中输入 chrome://net-internals/#hsts 查看。从审核通过到正式加入到 Chrome 的 stable release 版本中还需要一段时间,因为还要经过 canary、dev、beta 以及 stable progression 等步骤。


参考文章:
NGINX Docs | NGINX SSL Termination
手把手教你在 Nginx 上使用 CertBot

本文更新于 2018 年 7 月 28 日:
在较新版本中,ssl on 不再被使用,否则会报错:[warn] the "ssl" directive is deprecated, use the "listen ... ssl" directive instead;将推荐的 CertBot 安装方式由包管理工具改为 Git 项目。