问题描述
之前使用certbot通过Let's Encrypt进行的SSL认证,也设置了自动续期,见这个链接
。
但是一段时间以后发现SSL的证书过期了,显然是之前设置的自动续期没有生效导致的90天之后证书过期了。
问题分析
为什么会过期呢?我先使用sudo certbot renew --dry-run尝试了下自动续期证书,结果失败了。
报错如下所示
2025-05-28 02:45:04,355:DEBUG:certbot._internal.display.obj:Notifying user:
Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems:
Domain: www.honest-zh.me
Type: unauthorized
Detail: 47.95.215.1: Invalid response from http://www.honest-zh.me/.well-known/acme-challenge/gpxJ6sTFrbU5vUvQ_O15zvraOCdaV29PJ5PtDRvDUBA: 403
Hint: The Certificate Authority failed to verify the temporary nginx configuration changes made by Certbot. Ensure the listed domains point to this nginx server and that it is accessible from the internet.
报错是没法访问到http://www.honest-zh.me/.well-known/acme-challenge/ 下的gpxJ6sTFrbU5vUvQ_O15zvraOCdaV29PJ5PtDRvDUBA,HTTP请求的返回是 403,返回被拒绝。
我第一反应的是这个http的访问在我的nginx设置里直接被重定义到了HTTPS(SSL已经过期自然没法用HTTPS访问了),于是我先修改了 /etc/nginx/sites-available/路径下的default文件,暂时取消了重定向。问题依然存在。
我得清楚这一串是个什么东西,应该是在nginx域名工作路径下的一个文件?于是我查了一下,发现是certbot用于临时存放ACME的HTTP-01挑战的文件,用于域名所有权验证。当使用 Certbot申请证书时,Certbot 会在服务器上创建随机文件:/.well-known/acme-challenge/随机文件名,文件内容包含特定验证字符串。Let’s Encrypt 服务器尝试通过 HTTP 访问该文件http://www.honest-zh.me/.well-known/acme-challenge/随机字符串 ,若能成功访问到文件且内容匹配,则证明你控制该域名,随后会颁发证书。
于是我先看了一下Nginx对应的目录/var/www/html/之下的是否有这个文件,发现文件不存在(但是不存在不应该是404吗,当时没有细想)。
于是先创建了对应的目录mkdir /var/www/html/.well-known/acme-challenge
然后检查了路径的权限配置是否正确。chmod 755,设置完成以后发现问题还是一样的?
这下可就奇怪了,我又排查了防火墙 ufw status,防火墙直接是关闭的。这意味着80和443端口都不会被防火墙拦截。那么是什么拒绝了访问这个随机文件呢,还是文件直接没有创建成功?
我试着在nginx的用户组www-data:www-data先创建一个文件然后试一下通过curl访问是否成功。
在该角色的权限下文件创建没有问题,但是curl返回404。使用sudo tail -n 50 /var/log/nginx/error.log,查看nginx的错误日志,发现工作路径没在我创建的文件路径下。(nginx没设置root啊,默认的在/usr/share/nginx/html/)
open() "/usr/share/nginx/html/.well-known/acme-challenge/test.html" failed (2: No such file or directory), client: 47.95.215.1, server: www.honest-zh.me, request: "HEAD /.well-known/acme-challenge/test.html HTTP/1.1", host: "www.honest-zh.me"
重新设置nginx的域名工作路径以后,curl返回200,访问成功了。
root@iZ2zea95mj7zitqaxms4msZ:/var/www/html/.well-known/acme-challenge# curl -I http://www.honest-zh.me/.well-known/acme-challenge/test.html
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Wed, 28 May 2025 13:48:17 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 27 May 2025 18:23:45 GMT
Connection: keep-alive
ETag: "68360331-d"
Accept-Ranges: bytes
再次尝试自动续期,还不对???还是403的访问拒绝?!(这时候反应过来了,是403而不是404,说明是在别的地方被拒绝了)我尝试使用浏览器访问http://www.honest-zh.me/.well-known/acme-challenge/test.html,映入眼帘的阿里云域名暂时无法访问的报错,因为我没有备案。我尝试直接通过IP地址访问这个文件http://47.95.215.1/.well-known/acme-challenge/test.html,获得到了我想看到的,亲切的hello world!。
到这里真相就大白了,因为我没有做域名备案,域名解析之后的请求被阿里云拒绝了,所以Let’s encrypt的挑战文件没有办法被成功访问,导致了证书认证出错!!
小结
因为暂时没法备案域名,我先手动的通过DNS认证了一下,先保证之后可以访问。
sudo certbot certonly --manual --preferred-challenges dns -d www.honest-zh.me
会有一个这样的描述
Please deploy a DNS TXT record under the name
_acme-challenge.honest-zh.me with the following value:
XXXXXXXXXXXXXXXXXXXXXXX
Before continuing, verify the record is deployed.
这里需要复制这一串字符,在云服务的DNS的管理哪里添加一条内容
类型 主机记录 记录值(value)
TXT _acme-challenge.www XXXXXXXXXXXXXXXXXXXXXXX
通过命令nslookup -q=TXT _acme-challenge.www.honest-zh.me监测是否成功设置。
为了实现正确的Let’s encrypt认证,我接下来可能需要的是:
- 完成域名备案
- 重新考虑http的跳转问题,限制重定向到https的范围(会影响ssl过期以后获取认证,也过期是否影响不知道)
后续工作情况
我抽出时间重新进行了ICP备案,之后计划使用certbot进行自动的SSL证书续期。certbot可以自动管理他申请的证书并通过systemd的定时任务触发证书的续签。只需要执行:
sudo certbot --nginx
sudo certbot renew --dry-run
注意:certbot使用的证书必须是由certbot申请的,我们需要先删除原来的证书再修改nginx的网站配置(此时已经没有证书不能用ssh)。 删除旧证书:
sudo certbot delete --cert-name www.honest-zh.me备份旧的网站,原有的配置改为:server { listen 80; server_name www.honest-zh.me honest-zh.me; root /var/www/honest-zh.me; index index.html index.htm; location ^~ /.well-known/acme-challenge/ { root /var/www/html; allow all; } location / { try_files $uri $uri/ $uri.html =404; } }
还遇到一个问题:
honest@iZ2zea95mj7zitqaxms4msZ:/etc/nginx/sites-available$ sudo certbot --nginx Saving debug log to /var/log/letsencrypt/letsencrypt.log Which names would you like to activate HTTPS for? We recommend selecting either all domains, or all domains in a VirtualHost/server block. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: honest-zh.me 2: www.honest-zh.me - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate numbers separated by commas and/or spaces, or leave input blank to select all options shown (Enter 'c' to cancel): 1,2 Requesting a certificate for honest-zh.me and www.honest-zh.me Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems: Domain: honest-zh.me Type: dns Detail: no valid A records found for honest-zh.me; no valid AAAA records found for honest-zh.me Hint: The Certificate Authority failed to verify the temporary nginx configuration changes made by Certbot. Ensure the listed domains point to this nginx server and that it is accessible from the internet. Some challenges have failed. Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
两个域名里全域名honest-zh.me没有设置dns解析,再阿里云上设置一下即可。
完成第一次证书申请之后,恢复之前的nginx网页配置,我的配置如下:
server {
listen 80;
server_name www.honest-zh.me honest-zh.me;
#return 301 https://$server_name$request_uri;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name www.honest-zh.me honest-zh.me;
# 静态文件目录
root /var/www/honest-zh.me;
index index.html index.htm;
# SSL 配置
ssl_certificate /etc/letsencrypt/live/honest-zh.me/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/honest-zh.me/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# 安全头部
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# ACME 验证
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
}
# 主要静态文件处理
location / {
try_files $uri $uri/ $uri.html =404;
}
# 处理 Jekyll 的 _site 结构
location ~ ^/(assets|images|js|css|fonts)/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 视频文件特殊处理
location /assets/video/ {
alias /home/honest/blog/honestblog/assets/video/;
add_header Accept-Ranges bytes;
expires 30d;
}
# 保护目录
location ~ ^/(admin|草稿|树洞) {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files $uri $uri/ =404;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# 日志配置
access_log /var/log/nginx/blog.access.log;
error_log /var/log/nginx/blog.error.log;
}