使用 Let's Encrypt 来部署 HTTPS
最近配置好了服务器,也买了域名,顺便也把 HTTPS 部署好,反正以后都是要部署的。
网上找了不少关于这方面的教程,选择了 Let’s Encrypt 这个免费好用的证书签发服务,而证书申请并没有使用官方的 Certbot 工具,而是使用了 acme-tiny,部署过程依照 Let’s Encrypt,免费好用的 HTTPS 证书,一路下来没什么问题 (中间粗心造成个小问题),最后也部署成功。
感谢作者写的这篇文章,这里只是作个记录,并没有新的内容。文章链接
目录说明
假设当前用户名是 user
# 创建以下两个目录
/home/user/www/ssl/ # ssl 生成文件目录
/home/user/www/challenges/ # 存放验证文件目录
生成证书等
切换到 /home/user/www/ssl/
目录
# 创建账号,用于 Let's Encrypt 识别身份
openssl genrsa 4096 > account.key
# 创建域名私钥,选择了 RSA 私钥
openssl genrsa 4096 > domain.key
# 根据私钥文件,生成 CSR 文件,在 CSR 中推荐至少把域名带 www 和不带 www 的两种情况都加进去,其它子域可以根据需要添加 (目前一张证书最多可以包含 100 个域名),注意 openssl 配置文件是否存在
openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:yoursite.com,DNS:www.yoursite.com")) > domain.csr
配置验证服务
切换到 /home/user/www/challenges/
目录
修改 nginx 配置
server {
server_name www.yoursite.com yoursite.com;
# 优先找验证文件
location ^~ /.well-known/acme-challenge/ {
alias /home/user/www/challenges/;
try_files $uri =404;
}
location / {
# 如果配置了路由的话,可能要修改下配置
rewrite ^/(.*)$ https://yoursite.com/$1 permanent;
}
}
可以试下验证文件目录是不是能访问,比如新建 index.html 文件,我当时把 /home/user/www/challenges/
写成 /home/user/www/challenges
,最后漏了斜线,不能访问该目录 (返回 404),以至于后面验证的时候不能找到验证文件。
服务器配置好了,重启 nginx,回到 /home/user/www/ssl/
目录,开始获取网站证书。
# 保存 acme-tiny 到 ssl 目录
wget -O acme_tiny.py https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py
# 指定账户私钥、CSR 以及验证目录,执行脚本
python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir ~/www/challenges/ > ./signed.crt
# 网站证书获取成功后,下载 Let's Encrypt 的中间证书
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
# 合并网站证书与中间证书
cat signed.crt intermediate.pem > chained.pem
# 为了后续能顺利启用 OCSP Stapling,获取根证书并与中间证书合在一起
wget -O - https://letsencrypt.org/certs/isrgrootx1.pem > root.pem
cat intermediate.pem root.pem > full_chained.pem
# 证书添加完了之后,nginx 添加证书配置并 reload
ssl_certificate /home/user/www/ssl/chained.pem;
ssl_certificate_key /home/user/www/ssl/domain.key;
这里可能会出现域名无法解析的情况。
证书自动更新
Let’s Encrypt 签发的证书只有 90 天有效期,创建 renew_cert.sh
并通过 chmod a+x renew_cert.sh
赋予执行权限来定期更新。
脚本文件内容
#!/bin/bash
CurrentPath=`pwd`
SSLPath='/home/user/www/ssl/'
cd $SSLPath
python acme_tiny.py --account-key account.key --csr domain.csr --acme-dir /home/user/www/challenges/ > signed.crt || exit
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > chained.pem
echo "password" | sudo -S service nginx reload
cd $CurrentPath
exit 0
添加定时任务,使用 crontab -e
命令,选择好编辑器之后,清除文件内容后加上以下语句,Ctrl + x
然后回车就行。
0 0 1 * * /home/user/www/ssl/renew_cert.sh >/dev/null 2>&1
添加完之后,可以使用 crontab -l
来查看是否添加成功,还有不知道要不要重启 (sudo service cron restart
)。
有个小问题,重载 nginx 配置需要 root 权限才能执行,所以上面的 reload 语句换了,不知道有没有更好的办法。
最后附上完整的 nginx 配置
2017-02-17
开启 HTTP/2 与新增一些安全配置。
2018-07-13
更改与新增一些设置,例如:Server Push。
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name hughsite.com imhugh.com;
charset utf-8;
server_tokens off;
root /var/www/html;
index index.html index.php;
# SSL configuration
# https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-with-http-2-support-on-ubuntu-16-04
ssl on;
ssl_certificate /home/user/www/ssl/chained.pem;
ssl_certificate_key /home/user/www/ssl/domain.key;
# openssl dhparam -out dhparams.pem 2048
# https://weakdh.org/sysadmin.html
ssl_dhparam /home/user/www/ssl/dhparams.pem;
# https://github.com/cloudflare/sslconfig/blob/master/conf
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# openssl rand 48 > session_ticket.key
ssl_session_tickets on;
ssl_session_ticket_key /home/user/www/ssl/session_ticket.key;
ssl_stapling on;
ssl_stapling_verify on;
# intermediate.pem + root.pem
ssl_trusted_certificate /home/user/www/ssl/full_chained.pem;
# SSL configuration end
if ($request_method !~ ^(GET|POST)$) {
return 444;
}
if ($host != 'hughsite.com') {
rewrite ^/(.*)$ https://hughsite.com/$1 permanent;
}
location ~* (robots\.txt|favicon\.ico)$ {
root /home/user/www/static;
expires 30d;
}
# Cache
location ~* \.(css|js)$ {
expires 30d;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
add_header X-Frame-Options deny;
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'self' data:; script-src 'unsafe-inline' 'unsafe-eval' 'self' https:; style-src 'unsafe-inline' https:; connect-src 'self'; img-src https: data:; child-src https:; media-src 'none'; object-src 'none';";
add_header Cache-Control no-cache;
http2_push_preload on;
}
}
server {
listen 80;
listen [::]:80;
server_name hughsite.com imhugh.com;
server_tokens off;
access_log /dev/null;
# SSL challenge
location ^~ /.well-known/acme-challenge/ {
alias /home/user/www/challenges/;
try_files $uri =404;
}
location / {
rewrite ^/(.*)? https://hughsite.com/$1 permanent;
}
}
server {
listen 80 default;
listen [::]:80 default;
return 444;
}