前提
- 首先要保证你的openssl的版本高于1.0.2
➜ ~ openssl version
OpenSSL 1.0.2g 1 Mar 2016
- 其次,你的nginx要高于1.9.5(高于此版本才能支持http2.0), Ubuntu16.04使用apt安装时,安装的是1.10.3版本,所以OK
➜ ~ nginx -v
nginx version: nginx/1.10.3 (Ubuntu)
安装验证SSL的证书
我使用的是Let's Encrypt的免费证书(以下简称LE),而LE组织提供一个自动化安装配置工具就是Certbot,因此接下来将以Certbot为例来进行安装和配置
安装Certbot
最新的资料请查看Certbot官网
# 此处针对的是Ubuntu系统Nginx
$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx
使用Certbot
Certbot主要帮助我们实现如下功能:
- 自动生成证书
- 自动配置nginx的配置文件(主要是配置证书位置)
- 验证网站(即通过webroot的方式来验证该域名是否确实属于你)
通常情况下,我们只需要使用如下命令,certbot就会自动帮助我们配置nginx,并自动获取和验证证书。但是保险起见在使用该命令之前最好先将nginx的配置文件备份一下,然后再执行命令:
$ sudo certbot --nginx
执行该命令会出现交互式的提示界面,按照提示操作即可
但是在这里会有一个坑,就是必须打开443端口,之前我在阿里云的安全组中没有打开443端口,因此导致报如下错误:
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for www.xxx.com
Waiting for verification...
Cleaning up challenges
Failed authorization procedure. www.xxx.com (tls-sni-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Timeout
IMPORTANT NOTES:
- The following errors were reported by the server:
Domain: www.xxx.com
Type: connection
Detail: Timeout
To fix these errors, please make sure that your domain name was
entered correctly and the DNS A/AAAA record(s) for that domain
contain(s) the right IP address. Additionally, please check that
your computer has a publicly routable IP address and that no
firewalls are preventing the server from communicating with the
client. If you're using the webroot plugin, you should also verify
that you are serving files from the webroot path you provided.
在阿里云上添加相应安全规则,打开端口443然后就可以认证通过了
配置crontab自动更新证书
之后需要在crontab上加上相应的命令来自动更新证书:
crontab -e
# 添加如下两行
# 这里是设置每月的1号凌晨3点开始更新证书,随后3点05分重新reload nginx
00 03 1 * * /usr/bin/certbot renew --quiet
05 03 1 * * /usr/sbin/nginx -s reload
nginx最佳实践
同时,为了https有更好的性能以及更加安全的配置,可以通过Mozilla的工具来自动生成nginx最佳实践配置。我的实际配置如下:
## WEB
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/domainname/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domainname/privkey.pem; # managed by Certbot
# Diffie-Hellman parameter for DHE ciphersuites, this is for key exchange
ssl_dhparam /etc/letsencrypt/live/domainname/dhparam.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
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;
resolver dns22.hichina.com dns21.hichina.com;
server_name domainname;
set $root /home/git/projects/newton/build;
root $root;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:5000/api/;
proxy_redirect default;
}
location /page/ {
proxy_pass http://127.0.0.1:5000/page/;
proxy_redirect default;
}
}
下面分别讲解下
- 使用如下代码是为了让web server监听443端口,即https端口. 同时打开http2的开关。要想支持http2需要OpenSSL的版本达到1.0.2(通过
openssl version
来检查openssel版本)且nginx版本要高于1.0.9(通过nginx -v
来查询nginx的版本号),Ubuntu16.04在通过APT安装时自动安装1.10.3的版本,所以满足条件
listen 443 ssl http2;
listen [::]:443 ssl http2;
- 下面这部分代码是certbot自动添加的,用于指明证书的位置
ssl_certificate /etc/letsencrypt/live/domainname/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domainname/privkey.pem; # managed by Certbot
- 下面这段代码是用于key exchange时DH算法的,由于Nginx默认使用的DH秘钥长度为1024位,不足够安全,所以如果没有如下的配置则会在SSL Labs的安全测试中只能得到B+的得分,而要想得到A+的得分,就必须生成2048位以上长度的秘钥。关于key exchange是怎么一回事可以参考阮一峰的文章, 关于Diffie-Hellman算法是怎么回事,可以参考这个Youtube视频
# Diffie-Hellman parameter for DHE ciphersuites, this is for key exchange
ssl_dhparam /etc/letsencrypt/live/domainname/dhparam.pem;
注意这里的dhparam.pem是需要自己生成的,且需要跟之前证书的位置为同一个目录。生成dhparam.pem的方法如下
$ sudo openssl dhparam -out /etc/letsencrypt/live/domainname/dhparam.pem 2048
- 下面是https的优化参数,不做解释,直接粘贴过来
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
- 另外指令
ssl_ciphers
用于指定加密套件,避免使用不安全的加密套件 - 通常在使用http请求网站的时候,我们会通过发送301来重定向到https链接,使用443端口建立连接,但是这样必定会使得请求的时间加长,为了避免这种情况,我们可以通过HSTS的方式来让浏览器在一段时间内固定使用https访问网站,不论其是http请求还是https请求,这就避免了重定向。HSTS实际是在返回的response的头中添加了header
Strict-Transport-Security
,其nginx配置如下:
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
- 我们的浏览器通常在https请求时需要检测证书状态,其中一种方式就是通过对OCSP Server请求来获取证书的状态,因此为了减少这种不必要的请求,可以通过OCSP Stabling来提高性能, 关于OCSP Stapling可以参考这里
ssl_stapling on;
ssl_stapling_verify on;
resolver dns22.hichina.com dns21.hichina.com;
检测http2是否生效
最简单的办法是使用chrome浏览器inspect工具当中,选择Network,再右键单击Colume header,选择protocol,让column可以显示protocol,如图
然后我们就可以看到,http请求使用的是否是http2.0协议,如下,显示h2表示使用的是http2.0协议,说明配置成功