共计 6933 个字符,预计需要花费 18 分钟才能阅读完成。
前言
在之前的文章中 使用Docker Caddy Nginx部署WordPress博客我尝试使用Caddy部署WordPress, 但因为caddy
中php_fastcgi
的问题最终采用caddy
> nginx
> wordpress
的连续反向代理的方案. 的确caddy
的自动管理https证书的方式是高效的. 但在接下来的这几日高强度使用 wordpress
的过程中碰到了许许多多莫名奇妙的问题. 最终导致我准备放弃使用 caddy
. 转而使用 certbot
管理证书, http服务器则直接使用 nginx
的方式. 其中 caddy
存在以下几个痛点.
caddy
的配置文档资料过于稀少官方配置文档更加强调简单化一键化配置对于在nginx
中经常使用的rewrite
location
伪静态
等配置几乎没有讲解.caddy
"可能"
会对国内搜索引擎的SEO优化造成困扰, 在caddy
中许多配置都是按照时下http 最新的标准进行默认配置的, 比如http
,会自动通过301
状态码跳转到https
,www
子域会跳转到@
子域等. 这些都是最新的http
所要推进践行的配置, 也是chrome
浏览器默认的方式. 但结合除开Google
之外的其他...
搜索引擎在实际使用中会发现, 对于这些时下最新推行践行的标准支持力度往往是不够的, 甚至会出现无法爬取的情况.
最终我选择切换回 nginx
, 或许 caddy
更适合在开发测试中使用.
Certbot
来自 Certbot Docs: Certbot 是 EFF 加密整个互联网努力的一部分。Web 上的安全通信依赖于 HTTPS,这需要使用数字证书来让浏览器验证 Web 服务器的身份(例如,这真的是 google.com 吗?)。Web 服务器从称为证书颁发机构 (CA) 的受信任第三方获取证书。Certbot 是一个易于使用的客户端,它从 Let’s Encrypt(由 EFF、Mozilla 等发起的开放证书颁发机构)获取证书,并将其部署到 Web 服务器。任何经历过建立安全网站的人都知道获取和维护证书有多么麻烦。Certbot 和 Let’s Encrypt 可以自动消除痛苦,让您通过简单的命令打开和管理 HTTPS。使用 Certbot 和 Let’s Encrypt 是免费的。
总的来说Certbot提供了命令行界面的方式可以通过提供一个邮箱地址的方式自动为你申请Let’s Encrypt免费证书, 通常Let’s Encrypt证书的有效期为3个月, Certbot也提供了 renew
命令自动续期证书. 相对与caddy
的全自动管理的方式, certbot
更像是半自动管理需要一些手动干预, 但这也为你自己进行一些兼容性配置提供了入口. 你可以使用 nginx
对你的网站进行部署而 certbot
仅仅只是一个管理证书的工具.
Certbot命令解析
在申请证书的过程中必须的也是唯一一个的流程是使用一种方式让证书颁发方去确认,你要申请证书的域名是你控制的.证书的目的也是访问你的域名的用户使用浏览器通过一种算法验证的机制确认访问到的是你的的站点. 验证方式主要分两种 – 通过http请求验证,会向 http://你的域名/.well-known/acme-challenge/
发送一条请求。 – 通过dns解析,需要在域名解析中增加一条_acme-challenge.你的域名的TXT记录用于验证。
在Certbot Docs 用户指南可以看到Certbot大致可以下几种方式进行验证申请.
--manual
,手动配置,你可以自己选择http验证还是dns验证都行,需要自己纯手工去配置http验证文件或是dns解析,个人没有做测试。--standalone
,走http验证,这种方式你就算是没有nginx等服务,仍然可以验证域名并获取证书,它会虚拟一个服务来进行验证,但需要保证你的80端口别被占用。--nginx
,走http验证,如果不用docker且用nginx感觉是最方便的,会自动修改nginx的配置文件,增加http请求的访问配置及后续的ssl配置,但由于我们certbot是在docker容器内,所以要修改另一个nginx容器的配置文件及控制启停不太好实现,所以不采用这种方式。查资料时发现有人会将nginx 和 certbot通过dockerfile打包到一个镜像内,这样此命令就比较方便的执行了,感觉是个好办法,但个人没有测试了解。--dns-xxx
,走dns解析验证,这个也很方便,配置好后会自动在域名解析中增加一条记录进行域名验证,但官方目前只支持国外的一些dns服务商,国内的阿里云、dnspod等貌似也有第三方的插件,可以自己去github上找下。如果想申请通配符泛域名的,则只能通过此方式,目前个人没有测试。--webroot
,走http验证,已有类似nginx等服务的情况下可以用此方法,会自动在你配置的目录下生成http验证文件,与standalone相比好处是不需要预留80端口,也就是原有的网站不需要停止就可以申请证书,而缺点就是你还需要在nginx等服务中手动配置/.well-known/acme-challenge/使其对应到自动生成的验证文件上。以下是 webroot 申请证书命令.
# 命令语法
certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN]
# webroot 申请证书命令
certbot certonly --webroot -w /var/www/certbot -d {你要申请证书的域名} --email {你的邮箱} --agree-tos
其中 -w
即 --webroot-path
参数的缩写工具会再这个参数指定的目录生成 .well-known/acme-challenge
文件, --agree-tos
为同意ACME服务器的订户协议, --email
证书信息重要帐户通知的电子邮件地址. -d
后面填写你要申请证书的域名可以指定多个 -d
也就是同时申请多个域名的证书. 但根据我的实验一般不建议这样申请因为如果使用多个 -d
申请下来的证书是一个证书绑定多个域名的形式,这样对于吊销证书,甚至删除证书会带来麻烦. 通过webroot的方式运行后,会在两个地方产生文件: – 一个是在命令行配置的–webroot-path指定目录下产生临时文件,用于http验证。 – 一个是验证成功会产生存放证书的文件,默认是在/etc/letsencrypt/live/你的域名目录下,需要注意的是这些证书是个软链接,对应着../archive下,所以我们在做volume映射时不要只映射到live这个目录,而是要映射/etc/letsencrypt这个目录,否则无法找到相关的证书文件。
Nginx配置HTTP访问
此处Nginx用来配置初始时申请证书, 颁发商需要通过http访问 .well-known/acme-challenge
文件用来确认申请的域名的归属权. 以下是Nginx 初始配置中的server
节点配置
server {
listen 80;
listen [::]:80;
server_name {你的域名};
server_tokens off;
# ACME-challenge 配置http验证可访问
location ^~ /.well-known/acme-challenge/ {
#此目录都是nginx容器内的目录,
root /var/www/certbot;
}
#其他请求从http跳转到https
location / {
return 301 https://{你的域名}$request_uri;
}
}
配置目录结构 Docker Compose
首先你需要一个开放80以及443端口的服务器有一个公网可以访问的ipv4的地址,并且确认你需要部署的域名已经解析到了这个公网地址. 在服务器上新建一个目录创建以下目录结构. 其中 nenufm.com.conf
即为上面 nginx
配置所示的配置. 并且 nenufm.com.conf
文件名也可以是任意名字后缀为conf
就可以了,通常是你域名的名字作为文件名方便查找.
├── etc
│ ├── nginx
│ │ └── nginx.conf # nginx 主配置文件
│ │ └── conf.d # 配置导入目录
│ │ └── nenufm.com.conf //配置文件
└── docker-compose.yml //docker-compse文件
nginx.conf 中填写如下配置
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;
worker_connections 65535;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 2048;
types_hash_bucket_size 64;
client_max_body_size 16M;
# MIME
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'
'"$http3" "$http_x_forwarded_for"';
# Load configs
include /etc/nginx/conf.d/*.conf;
}
docker-compose.yml中进行如下配置
version: '2.4'
services:
nginx:
image: nginx:alpine
restart: always
container_name: server_nginx
environment:
- TZ=Asia/Shanghai
ports:
- 80:80
- 443:443
volumes:
- ./etc/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./etc/nginx/conf.d:/etc/nginx/conf.d
- ./data/certbot/letsencrypt:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
certbot:
image: certbot/certbot:latest
container_name: server_certbot
volumes:
- ./data/certbot/letsencrypt:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
- ./data/certbot/log:/var/log
# 容器默认命令续期证书
command: renew
上面的配置中我习惯将配置放在当前文件夹下的etc
目录data
则存放容器在运行时产生的数据文件. 其中 ./data/certbot/www
存放容器 certbot
在申请证书时所产生的 .well-known/acme-challenge
文件并映射到 nginx
的 /var/www/certbot
目录. ./data/certbot/letsencrypt
则映射 certbot
的 /etc/letsencrypt
目录存放申请的证书.
申请证书并配置Https访问
此处需要注意由于 letsencrypt的速率限制 在申请证书的尽量先测试自己的配置是没有错误, 例如端口是否开放, 域名是否已经解析到了服务器情况. 要是直接使用正式环境进行申请, 如果存在意外情况很容易被限制申请.
- 首先启动Nginx
docker compose up -d nginx
- 测试证书发放
# --dry-run是只测试不实际生成; --webroot-path对应着certbot内的http验证目录;-d后面是域名;--rm是运行后接着删除,certbot容器不需要一直开启,只是启动下生成证书即可
docker compose run --rm certbot certonly --webroot --webroot-path /usr/share/certbot/www/ -d {你要申请证书的域名} --email {你的邮箱} --agree-tos --dry-run
# out: 如果提示The dry run was successful则说明成功,可以去掉--dry-run参数来进行实际的获取证书
- 在上方测试成功后实际申请证书 即 去掉
--dry-run
参数
docker compose run --rm certbot certonly --webroot --webroot-path /usr/share/certbot/www/ -d {你要申请证书的域名} --email {你的邮箱} --agree-tos
# out: 如果提示The dry run was successful则说明成功
- 如果上方正常,则会在certbot容器下的
/etc/letsencrypt
下也是nginx
容器下的/etc/letsencrypt
下产生证书,此时,只需要配置nginx配置ssl即可,还是在文件nenufm.com.conf
中修改为如下配置
server {
listen 80;
listen [::]:80;
server_name {你的域名};
server_tokens off;
# ACME-challenge 配置http验证可访问
location /.well-known/acme-challenge/ {
#此目录都是nginx容器内的目录,
root root /var/www/certbot;
}
#http跳转到https
location / {
return 301 https://{你的域名}$request_uri;
}
}
#ssl配置
server {
listen 443 ssl;
server_name {你的域名};
# 日志
access_log /var/log/nginx/access.log combined buffer=512k flush=3s;
error_log /var/log/nginx/error.log warn;
#证书信息,这里的路径是nginx容器内的
ssl_certificate /etc/letsencrypt/live/{你的域名}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{你的域名}/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/{你的域名}/chain.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# 反向代理服务
location / {
proxy_pass http://{你要反向代理的服务};
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
- 重新载入Nginx配置
# 首先测试Nginx配置是否合法
docker compose exec nginx nginx -t
# out:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 配置成功执行以下命令重新载入配置
docker compose exec nginx nginx -s reload
# or 或者重启容器
docker compose restart nginx
- 然后你就可以通过https访问你的站点啦.
总结
本文讨论了在 docker
中使用 certbot
申请免费证书的过程. 相对于 caddy
的自动管理的方式 certbot
方式相对来说较为繁琐. 但对于长期运行维护的网站, nginx的配置方法更加兼容可调整, certbot
申请的证书有效期为三个月, 也就是说三个月后需要定时重新续期证书.在这个例子中可以用命令 docker compose run --rm certbot renew
续期证书. certbot renew
此命令尝试续订之前获得的所有在 30 天内过期的证书, 并且这个命令被设计来可频繁调用的, 它检查证书过期时间是在本地进行检查.网上有许多使用 crontab
定时任务定期运行命令续期证书, 并重启nginx的配置方法.
只是暂时我不打算采用这种方式进行自动化部署, 站点运行需要稳定性. 之后我会继续考量定时任务的稳定性问题.