Nginx 配置 SSL

安装 Certbot

Certbot 是用来申请 SSL 证书的。Certbot 的安装方式有几种,具体可以在 Certbot 官网上查询。
笔者尝试了通过 pip 进行安装,直接执行 pip install certbot 即可安装好 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
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

这里建议使用 certbotcertbot-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 的条件为:

  • 有效的 SSL 证书
  • 将所有 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 项目。