现在主流浏览器都会鼓励网站添加HTTPS支持,如果还不与时俱进的话就会被它们在地址栏上各种羞辱。 个人博客买收费证书还是比较奢侈的,还好有 Let’s Encrypt 为我们提供了免费的解决方案。这里使用 acme.sh 脚本工具来处理证书申请。

安装 acme.sh

1
2
yee@ubuntu:~$ sudo apt install socat
yee@ubuntu:~$ curl https://get.acme.sh | sh

会自动创建计划任务,也可以自定义,比如:

1
0 0 * * * "/home/yee/.acme.sh"/acme.sh --cron --home "/home/yee/.acme.sh" > /dev/null

验证域名所有权

泛域名签名只能通过DNS API模式验证,项目自带了主流的域名服务商的集成,这里以NameSilo为例。

NameSilo API Manager页面生成API,然后:

 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
26
27
28
yee@ubuntu:~$ export Namesilo_Key="$NAMESILO_KEY"
yee@ubuntu:~$ alias acme.sh="/home/yee/.acme.sh/acme.sh"
yee@ubuntu:~$ acme.sh --issue --dns dns_namesilo --dnssleep 900 -d josta.me -d *.josta.me
[Wed Mar 14 11:13:26 CST 2018] Multi domain='DNS:josta.me,DNS:*.josta.me'
[Wed Mar 14 11:13:26 CST 2018] Getting domain auth token for each domain
[Wed Mar 14 11:13:27 CST 2018] Getting webroot for domain='josta.me'
[Wed Mar 14 11:13:28 CST 2018] Getting webroot for domain='*.josta.me'
[Wed Mar 14 11:13:28 CST 2018] Found domain api file: /home/yee/.acme.sh/dnsapi/dns_lexicon.sh
...
create_record: True
[Wed Mar 14 11:13:30 CST 2018] Sleep 960 seconds for the txt records to take effect
[Wed Mar 14 11:29:34 CST 2018] Verifying:josta.me
[Wed Mar 14 11:29:36 CST 2018] Success
[Wed Mar 14 11:29:36 CST 2018] Verifying:*.josta.me
[Wed Mar 14 11:29:39 CST 2018] Success
[Wed Mar 14 11:29:39 CST 2018] Removing DNS records.
[Wed Mar 14 11:29:39 CST 2018] Verify finished, start to sign.
[Wed Mar 14 11:29:40 CST 2018] Cert success.
-----BEGIN CERTIFICATE-----
MIIE/zCCA+egAwIBAgISA1V+J4qtPh412RQZ2hIZK1ipMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODAzMTQwMjI5MzlaFw0x
...
-----END CERTIFICATE-----
[Wed Mar 14 11:33:36 CST 2018] Your cert is in  /home/yee/.acme.sh/josta.me/josta.me.cer
[Wed Mar 14 11:33:36 CST 2018] Your cert key is in  /home/yee/.acme.sh/josta.me/josta.me.key
[Wed Mar 14 11:33:36 CST 2018] The intermediate CA cert is in  /home/yee/.acme.sh/josta.me/ca.cer
[Wed Mar 14 11:33:36 CST 2018] And the full chain certs is there:  /home/yee/.acme.sh/josta.me/fullchain.cer

可以把上面两个环境变量加入 /home/yee/.acme.sh/acme.sh.env 以便后续使用。

作者不推荐直接使用该目录下文件,所以还要执行一次安装:

1
2
3
yee@ubuntu:~$ acme.sh --install-cert -d josta.me \
        --key-file /etc/nginx/ssl/josta.me.key \
        --fullchain-file /etc/nginx/ssl/fullchain.cer

acme项目比较活跃,为了能及时用上最新版本,可以设置脚本的自动更新,非常简单:

1
yee@ubuntu:~$ acme.sh --upgrade --auto-upgrade

想取消的话可以执行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
yee@ubuntu:~$ acme.sh --upgrade --auto-upgrade 0

[Fri Aug 25 21:01:56 CST 2017] Installing from online archive.
[Fri Aug 25 21:01:56 CST 2017] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Fri Aug 25 21:01:58 CST 2017] Extracting master.tar.gz
[Fri Aug 25 21:01:58 CST 2017] Installing to /home/yee/.acme.sh
[Fri Aug 25 21:01:58 CST 2017] Installed to /home/yee/.acme.sh/acme.sh
[Fri Aug 25 21:01:58 CST 2017] Installing alias to '/home/yee/.bashrc'
[Fri Aug 25 21:01:58 CST 2017] OK, Close and reopen your terminal to start using acme.sh
[Fri Aug 25 21:01:58 CST 2017] Good, bash is found, so change the shebang to use bash as preferred.
[Fri Aug 25 21:01:58 CST 2017] OK
[Fri Aug 25 21:01:58 CST 2017] Install success!
[Fri Aug 25 21:01:58 CST 2017] Upgrade success!

此外,为了提升SSL评分,还需要解决Weak Diffie-Hellman问题,也即是要加大Diffie-Hellman交换密钥的预设质数的长度,运行:

1
yee@ubuntu:~$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

生成 dhparam.pem,等待几分钟就完成了(也可能几十分钟 :-( )。

Nginx配置调整

  • 修改Nginx站点设置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
server {
  listen 443;
  ssl on;
  ssl_certificate /etc/nginx/ssl/fullchain.cer;
  ssl_certificate_key /etc/nginx/ssl/josta.me.key;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;
  # 取消了SSL v3支持
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  # 配置HSTS(`always`参数要求Nginx版本>=1.7.5)
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  add_header X-Frame-Options "DENY";
  ...
  • 增加HTTP的301跳转:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
server {
  listen 80 default_server;
  listen [::]:80 default_server;
  server_name www.josta.me josta.me;
  server_tokens off;
  access_log /dev/null;
  if ($request_uri ~ \.(aspx|php|jsp|cgi)$) {
    return 403;
  }
  if ($request_uri ~ "^/announce.*") {
    return 403;
  }
  if ($request_uri ~ "^.*torrent.*") {
    return 403;
  }
  return 301 https://$host;
}

最后 sudo systemctl reload nginx 就可以看效果了。

成果检验

  • 检查HSTS状态:
1
yee@ubuntu:~$ curl -s -D- https://josta.me | grep Strict

输出 strict-transport-security: max-age=31536000; includeSubDomains; preload

  • 确认下SSL v3是否被关闭
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
yee@ubuntu:~$ curl -kI -v3 https://josta.me

* Rebuilt URL to: https://josta.me/
*   Trying 77.77.77.77...
* TCP_NODELAY set
* Connected to josta.me (77.77.77.77) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* SSLv3 (OUT), TLS handshake, Client hello (1):
* SSLv3 (IN), TLS alert, Server hello (2):
* error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0
curl: (35) error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

可以看到sslv3握手失败,满足预期

参考: