How To Secure Nginx with Let's Encrypt on Ubuntu 16.04

缘起

想做个微信小程序,把娃们的网站,在微信里展现给老人看,而做微信小程序,要求网站是 https 的,我也舍不得买证书,于是就打算使用 Let’s Encrypt 提供的免费证书。

环境

  • Ubuntu 16.04.1 LTS
  • NGINX 1.10
  • kernel 4.9.4-040904-generic

步骤

安装软件

1
2
3
apt-get -y install letsencrypt;
# letsencrypt 是 Let's Encrypt 官方软件
# certbot 在 Ubuntu 16.04 上的名字

获取证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
letsencrypt certonly \
--webroot \
-w /opt/www/blog.xiaoyuer.cn \
-d blog.xiaoyuer.cn;
letsencrypt certonly
--webroot \
-w /opt/www/blog.lukeyang.us \
-d blog.lukeyang.us;
# 这里 blog.xiaoyuer.cn 和 blog.lukeyang.us 是娃的网站
# 其实支持在一条命令里用多个 -w 参数配合 -d 参数,
# 为什么没有这样做而是单独一条命令一个域名这么做呢,
# 主要是不想把所有证书放在一起......
#
# 获取证书过程中会弹出个窗口让输入邮件地址,
# 输入一个常用的即可

Generate Strong Diffie-Hellman Group(optional)

1
2
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048;
# 密钥交换时使用更强的 2048 位密钥

配置 NGINX 的 ssl 参数(optional)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cat <<EOF >/etc/nginx/snippets/ssl-params.conf
# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
#resolver 8.8.8.8 8.8.4.4 valid=300s;
#resolver_timeout 5s;
# Disable preloading HSTS for now. You can use the commented out header line that includes
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/ssl/certs/dhparam.pem;
EOF

# 如果没有做上一步,最后 ssl_dhparam 那句请注释掉

NGINX 的虚拟机配置

1
vim /etc/nginx/sites-enabled/blog.xiaoyuer.cn;

server {} 配置块中添加如下内容:

1
2
3
4
5
6
7
8
listen 443 ssl http2 default_server;
include snippets/ssl-params.conf;
ssl_certificate /etc/letsencrypt/live/blog.xiaoyuer.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.xiaoyuer.cn/privkey.pem;

location ~ /.well-known {
allow all;
}

同样的,

1
vim /etc/nginx/sites-enabled/blog.lukeyang.us;

server {} 配置块中添加如下内容:

1
2
3
4
5
6
7
8
listen 443 ssl http2;
include snippets/ssl-params.conf;
ssl_certificate /etc/letsencrypt/live/blog.lukeyang.us/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.lukeyang.us/privkey.pem;

location ~ /.well-known {
allow all;
}

注意

这个配置里的 listen 443 ssl httpd2 一行并没有 default_server 字样,那是一个端口因为只能有一个 default_server,前面那个虚机已经在 443 端口上指定了 default_server,所以这里不能重复指定了

重启 NGINX 服务

1
systemctl restart nginx.service;

配置自动更新证书

由于 Let’s Encrypt 的证书会在三个月后过期,但是官方工具提供了自动更新的功能,我们只需要用 cron 定时调用即可。

1
2
3
4
cat <<EOF >/etc/cron.d/renew_ssl
25 3 * * 3 root /usr/bin/letsencrypt renew>/var/log/le-renew.log"
35 3 * * 3 root /bin/systemctl reload nginx
EOF

这里的逻辑是每周(三)检查一次是否需要更新证书,如果需要,则自动更新证书。检查完,再做一次 nginx 的 reload 操作,重新载入新证书(如果有的话)。

参考