写在最前

本文是运维从零单排系列的第二篇,文中配置环境要求OpenSSL 1.0.2k及以上,Nginx 1.12.0及以上(由1.0.2k及以上版本的OpenSSL编译),Nginx和OpenSSL的安装可参考:
运维从零单排(一)—— 服务器环境配置(安装Nginx、Node.js、OpenSSL)

HTTPS已经几乎成为了现代网站的标配,开启HTTPS需要申请SSL证书。SSL证书主要有以下几种:

SSL证书类型

  1. DV证书(Domain Validation Certificate)
    只提供对数据的加密,但是对提供证书的个人和机构的身份不做验证,申请证书时,CA只验证域名信息,10分钟内即可签发,适合一般个人网站/博客等。
  2. OV证书(Organization Validation Certificate)
    OV SSL 提供了对个人或者机构的审核,申请证书时需要公司信息,1-2天签发。
  3. EV证书(Extended Validation Certificate)
    EV SSL证书遵循全球统一的严格身份验证标准,是目前业界安全级别最高的顶级 (Class 4级)SSL证书。适合金融证券、银行、支付等重点强调网站安全、企业可信形象的网站。

Let’s Encrypt 证书申请

Let’s Encrypt是一个免费的,自动化的,开放的证书颁发机构(CA),它是互联网安全研究小组提供(ISRG)的一个服务,有关它的介绍可以看这里:https://letsencrypt.org/about/

下面我们以www.zhounan.win为例,来申请Let’s Encrypt的免费SSL证书。

安装certbot

certbot是Let’s Encrypt官方推荐的获取证书所需的客户端

1
2
sudo yum install epel-release -y
sudo yum install certbot -y

申请证书

申请过程中需要验证域名归属,验证方式有两种:

  1. Standalone:需要停止当前的Web Server,让出80端口,由客户端内置的Web Server与Let’s Encrypt通信。
  2. Webroot:需要在域名根目录下创建一个临时目录,并要保证外网通过域名可以访问这个目录。
    如果使用第一种方法,更新证书时需要重启 Web 服务器。
    如果已经有Web Server(如Nginx),建议使用第二种方式来进行。

我们使用第二种方式来申请证书,修改Nginx的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name www.example.com;
root /var/www/html;
# 如果没有该文件夹则新建一个
# certbot 会自动在该文件夹下创建一个隐藏文件`.well-known/acme-challenge`
# 并通过请求这个文件来验证域名归属。
location ~ /.well-known/acme-challenge {
allow all;
}
}
# 重启Nginx
systemctl restart nginx.service

运行如下命令来申请证书

1
certbot certonly --webroot -w /var/www/html/ -d zhounan.win -d www.zhounan.win -d api.zhounan.win

具体参数用法可以参考这里:https://certbot.eff.org/#centosrhel7-nginx
注意Let’s Encrypt暂时不支持泛域名(*.zhounan.win)
执行上述命令后会依次要求输入邮箱、同意协议、选择是否共享邮箱,注意邮箱需要输入真实邮箱以便能接收到Let’s Encrypt的邮件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): test@163.com

Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf. You must agree
in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: N

完成之后稍等片刻即可看到如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/zhounan.win/fullchain.pem. Your cert will
expire on 2017-07-25. 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"
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.
- 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/zhounan.win/文件夹内。

生成dhparam.pem

为了更好的加密,我们进一步配置PFS(Perfect Forward Secrecy)

Forward Secrecy的概念很简单:客户端和服务器协商一个永不重用的密钥,并在会话结束时销毁它。服务器上的 RSA 私钥用于客户端和服务器之间的 Diffie-Hellman 密钥交换签名。从 Diffie-Hellman 握手中获取的预主密钥会用于之后的编码。因为预主密钥是特定于客户端和服务器之间建立的某个连接,并且只用在一个限定的时间内,所以称作短暂模式(Ephemeral)。如果使用Forward Secrecy,攻击者取得了一个服务器的私钥,他是不能解码之前的通讯信息的。这个私钥仅用于 Diffie Hellman 握手签名,并不会泄露预主密钥。Diffie Hellman 算法会确保预主密钥绝不会离开客户端和服务器,而且不能被中间人攻击所拦截。所有版本的 nginx 都依赖于 OpenSSL 给 Diffie-Hellman 的输入参数。如果不特别声明,将使用 OpenSSL 的默认设置,1024 位密钥,因为我们正在使用 2048 位证书,所以要有一个更强大的 DH。

我们可以使用下面的命令来生成它:

1
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Nginx配置

Mozilla为我们提供了一个SSL配置生成器:https://mozilla.github.io/server-side-tls/ssl-config-generator/
在这里选择了服务器和相应的版本之后,它会为我们提供一份安全的SSL配置,修改一下文件路径并加入到nginx的配置中即可。

下面是一份较为完整的nginx.conf配置文件,供参考。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#user  nobody;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

#access_log logs/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
gzip_vary on;

include /etc/nginx/conf.d/*.conf;

server {
listen 80 default_server;
listen [::]:80 default_server;
#永久重定向到https站点
return 301 https://$host$request_uri;
}

server {
# 开启HTTP2,需要OpenSSL版本大于1.0.2,Nginx版本大于1.9.12
listen 443 ssl http2;
listen [::]:443 ssl http2;

# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
# fullchain.pem和privkey.pem文件路径
ssl_certificate /etc/letsencrypt/live/zhounan.win/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/zhounan.win/privkey.pem;
# dhparam.pem文件路径
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

# modern configuration. tweak to your needs.
ssl_protocols TLSv1.2;
#可选的加密算法,越靠前的优先级越高
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
#在 SSLv3 或 TLSv1 握手过程一般使用客户端的首选算法,如果启用下面的配置,则会使用服务器端的首选算法.
ssl_prefer_server_ciphers on;

# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;

# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;

## verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /etc/letsencrypt/live/zhounan.win/chain.pem;

resolver <IP DNS resolver>;

location / {
# 你的项目路径
root /home/cbg;
index index.html index.htm;
}

#以下配置用于Let's Encrypt服务端和客户端校验目录配置
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/html;
}

# error_page 404 /404.html;

# redirect server error pages to the static page /50x.html

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

配置完成后,重启nginx,访问你的域名,域名前方出现绿色的小锁图标,即说明配置成功。

测试网站安全性

Qualys SSL Labs 提供了全面的 SSL 安全性测试,在该链接中填入你的网站地址,即可测试安全性。如果不是A+,可以根据测试结果的提示对配置进行相应修改。

证书自动续期

Let’s Encrypt的证书默认有效期只有90天,我们需要配置自动续期。
可以使用如下命令先模拟自动更新:

1
certbot renew --dry-run

如果出现如下提示则说明配置成功:

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

如果模拟更新正常,则可以使用crontab -e来启用自动任务:

1
crontab -e

增加配置:

1
0 1 1 * * /mnt/apps/letsencrypt/certbot-auto renew -renew-hook "/etc/init.d/nginx reload"

该配置含义为:每月1日凌晨1点执行任务

参考链接

http://www.restran.net/2017/01/24/nginx-letsencrypt-https/
https://www.mzlion.com/CentOS7-Nginx-SSL-Install-Let's-Encrypt.html
https://certbot.eff.org/#centosrhel7-nginx
https://www.pupboss.com/nginx-add-ssl/
http://blog.lzuer.net/2016/10/25/https/