说明:以下脚本在 Ubuntu 18.04 运行通过,大部分脚本执行需要管理员权限。
# 安装必备工具包
apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# 添加docker阿里云源,相对官方源速度更快
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 安装最新版社区版docker
apt update -y
apt install -y docker-ce
# 如果想要安装指定版本的docker-ce,如下:
# 查看有哪些版本
apt-cache madison docker-ce
# 安装指定版本
apt install -y docker-ce=[版本]
# 设置阿里云docker源
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://uon07it7.mirror.aliyuncs.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker
# 下载安装docker-compose
curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
Let's Encrypt 免费证书签发过程包含以下三个阶段:
Certbot
,Certbot
是签发/更新证书的客户端程序;Certbot
获取SSL/TLS
证书,证书有效期为3个月
;Certbot
更新证书。如果证书有效期小于 30 天,Certbot
会更新证书;不论是第一次申请证书,还是更新证书,Certbot
都会发起一次ACME请求,来验证你是否拥有该域名。如果验证通过,Certbot
就会将新证书安装到本地服务,其实就是将证书保存在一个目录中。证书一般包含两个文件(包含公钥、私钥以及证书等信息),web 服务器需要配置使用这两个证书文件,来实现 HTTPS 访问。
ACME
验证过程如下:
Certbot
向Let's Encrypt
发起证书申请;Let's Encrypt
返回Certbot
一个唯一的token
;Certbot
配置 web 服务器,使token
可以通过 url:http://example.com/.well-known/acme-challenge/{token}
访问;Let's Encrypt CA
访问上述 url,如果获取到的token
和它发送给Certbot
的token
一致,就可以证明你拥有该域名;注意:
Certbot
需要配置 web 服务器的相应权限。以 nginx 为例,Certbot
需要权限将token
写入.well-known/acme-challenge
目录。
为了方便维护、升级,推荐使用 docker 来运行Certbot
。整个过程可以分为两部分:首次申请证书和更新证书。
创建 web 服务目录:
mkdir -p /letsencrypt/site #这里以静态网页为例,也可以设为反向代理。
创建 docker-compose 文件:
nano /letsencrypt/docker-compose.yml
docker-compose.yml 内容如下:
version: '3.1'
services:
demo-site:
container_name: 'demo-site'
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./site:/usr/share/nginx/html
networks:
- docker-network
networks:
docker-network:
driver: bridge
创建 nginx 配置文件:
nano /letsencrypt/nginx.conf
nginx.conf 内容如下:
server {
listen 80;
server_name example.com www.example.com;
location ~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
root /usr/share/nginx/html;
index index.html;
}
启动 web 服务器:
cd /letsencrypt && docker-compose up -d
运行Certbot
申请证书:
docker run --rm -it \
-v /letsencrypt/certbot/etc/letsencrypt:/etc/letsencrypt \ # 证书申请工作目录
-v /letsencrypt/certbot/var/log/letsencrypt:/var/log/letsencrypt \ # 日志记录
-v /letsencrypt/site:/data/letsencrypt \ # ACME验证token目录,与nginx服务器共享
certbot/certbot \
certonly --webroot \ # 指定ACME验证方式:token文件验证
--email [email protected] --agree-tos --no-eff-email \ # 申请者邮件
--webroot-path=/data/letsencrypt \ # ACME验证token文件放置目录
-d example.com -d www.example.com # 指定要申请证书的域名列表
如果脚本正常运行,可以在
/letsencrypt/certbot/etc/letsencrypt/live
下找到example.com
文件夹,其中包含申请成功的证书文件:fullchain.pem
和privkey.pem
。
停止 web 服务器:
cd /letsencrypt && docker-compose down
更新 docker-compose 配置:
version: '3.1'
services:
demo-site:
container_name: 'demo-site'
image: nginx:alpine
ports:
- "80:80" # 保留80端口,用于证书更新
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./site:/usr/share/nginx/html
- ./certbot/etc/letsencrypt/live:/letsencrypt/live # 当前证书目录
- ./certbot/etc/letsencrypt/archive:/letsencrypt/archive # 历史证书目录
- ./dhparam-2048.pem:/letsencrypt/dhparam-2048.pem # 使用2048位DH(Diffie-Hellman)参数
networks:
- docker-network
networks:
docker-network:
driver: bridge
生成 2048 位的 DH 参数文件命令如下:
openssl dhparam -out /letsencrypt/dhparam-2048.pem 2048
live 目录的证书会 soft link 到 archive 目录,而 docker 对 soft link 支持不好,因此需要同时映射 live 和 archive 目录。
更新 nginx 配置,启用 HTTPS:
server {
listen 80;
server_name example.com www.example.com;
# 重定向到https
location / {
rewrite ^ https://$host$request_uri? permanent;
}
# 高优先级,仅用于更新证书
location ~ /.well-known/acme-challenge {
allow all;
root /data/letsencrypt;
}
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
server_tokens off;
ssl on;
ssl_certificate /letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /letsencrypt/live/example.com/privkey.pem;
ssl_buffer_size 8k;
ssl_dhparam /letsencrypt/dhparam-2048.pem; # 使用2048位DH参数,加强安全
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
root /usr/share/nginx/html;
index index.html;
}
DH 以及 OCSP 内容请参考:Strong SSL Security On nginx
重新启动 web 服务器:
cd /letsencrypt && docker-compose up -d
设置更新脚本:
touch /letsencrypt/renew.sh
chmod +x /letsencrypt/renew.sh
nano /letsencrypt/renew.sh
renew.sh 脚本内容如下:
#!/bin/bash
docker run -it --rm \
-v /letsencrypt/certbot/etc/letsencrypt:/etc/letsencrypt \
-v /letsencrypt/certbot/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /letsencrypt/certbot/var/log/letsencrypt:/var/log/letsencrypt \
-v /letsencrypt/site:/data/letsencrypt \
certbot/certbot \
renew --webroot -w /data/letsencrypt --quiet && docker kill --signal=HUP demo-site
最后一行脚本说明:在更新完证书后,通知 nginx 重新加载配置。
通过 crontab 设置定时任务:
crontab -e
添加一行,每周执行一次更新脚本:
0 1 * * 0 /letsencrypt/renew.sh
可以通过ssllabs.com验证证书,如果按照上述配置,应该可以获得A+
评价。
当然,还可以进一步通过securityheaders.io校验网站安全性,对于 nginx 可以添加以下配置:
server {
# ....
location / {
#security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
#CSP
add_header Content-Security-Policy "frame-src 'self'; default-src 'self'; script-src 'self' 'unsafe-inline' https://maxcdn.bootstrapcdn.com https://ajax.googleapis.com; img-src 'self'; style-src 'self' https://maxcdn.bootstrapcdn.com; font-src 'self' data: https://maxcdn.bootstrapcdn.com; form-action 'self'; upgrade-insecure-requests;" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
# ....
}
本文参考How to Set Up Free SSL Certificates from Let's Encrypt using Docker and Nginx,对 Docker 环境下如何使用 Let's Encrypt 自动获取/更新 SSL 证书做了一个简明攻略。
如果你正在使用 K8S,ingress nginx和traefik都对let‘s encrypt
提供了很好的支持,配合helm来安装部署,也更为简单方便。