Let’s Encrypt renew出现“Challenge failed for domain xxxx”

作者 | 2019年6月5日

某个私用的子域名上了Let’s Encrypt签发的证书,上周用renew命令延长证书有效期时出现下面的错误:

Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/xxx.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator standalone, Installer None
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for xxxx
Waiting for verification...
----------------------------------------
Exception happened during processing of request from ('::ffff:66.133.109.36', 45260, 0, 0)
Traceback (most recent call last):
  File "/usr/lib64/python2.7/SocketServer.py", line 295, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/usr/lib64/python2.7/SocketServer.py", line 321, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib64/python2.7/SocketServer.py", line 334, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.7/site-packages/acme/standalone.py", line 207, in __init__
    BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
  File "/usr/lib64/python2.7/SocketServer.py", line 651, in __init__
    self.finish()
  File "/usr/lib64/python2.7/SocketServer.py", line 710, in finish
    self.wfile.close()
  File "/usr/lib64/python2.7/socket.py", line 279, in close
    self.flush()
  File "/usr/lib64/python2.7/socket.py", line 303, in flush
    self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe
----------------------------------------
Challenge failed for domain xxxx
http-01 challenge for xxxx
Cleaning up challenges
Attempting to renew cert (xxxx) from /etc/letsencrypt/renewal/xxxx.conf produced an unexpected error: Some challenges have failed.. Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/xxxx/fullchain.pem (failure)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/xxxx/fullchain.pem (failure)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 renew failure(s), 0 parse failure(s)

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: xxxx
   Type:   unauthorized
   Detail: Invalid response from
   http://xxxx/.well-known/acme-challenge/vQ5zuvoh188fvC0GE8U5KnJ5yQMf1GamNzJCW-Ho-D8
   [xxx.xxx.xxx.xxx]: "<html>\n<head>\n<meta
   http-equiv=\"Content-Type\" content=\"textml;charset=UTF-8\" />\n
   <style>body{background-color:#FFFFFF}</style>"

   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.

一直以来用的好好的,这次突然撂挑子不干了,简直莫名其妙。根据错误信息排查潜在问题,都没毛病:

  1. ip已经正确解析,错误信息和log中可以验证这点;
  2. 绑定80端口也没问题,已经停止了Nginx;
  3. certbot已经升级到了最新版本;
  4. 用Python3.6的pip重新安装certbot并尝试,问题依旧。

自行尝试加上网找方案,一番折腾无果后暂时放弃了。心想也许是服务端抽风,说不定过几天就好了。

今天再次尝试续期证书,还是同样的问题。意识到这应该不是服务端问题,不然肯定引起大面积反应了,配置或者使用上有新问题的可能性更大。

用”invalid response”等关键字搜不出什么有用的结果。过了一会后忽然想到抛出异常中的 “::ffff:66.133.109.36” 这个ip是什么玩意?复制ip上Google查询,竟然直接找到了问题原因:用于验证域名的80端口被阿里云拦截了,certbot收到的返回与预期不符合,于是提示“challenge failed for domain xxxx”的错误。至于这个ip,应该是阿里云上层防火墙的公共ip。

接着自己尝试用80端口访问域名,果然出现拦截的页面,画风如下:

域名拦截提示

知道了问题的原因,找解决办法还是挺容易的。个人能想到的解决办法有三种:

  1. 域名备案;这个不用说了吧,说多了也不好;
  2. 暂时将域名解析到境外服务器,获取到证书更新后再解析回来;
  3. 域名验证从http改成dns。

前两个办法比较简单,本文不再多说。这里说一下用dns方式验证域名的步骤。

dns验证有两个重要参数:

  • –preferred-challenges,验证方式,填写dns或dns-01;
  • –server, 验证服务器,填写https://acme-v02.api.letsencrypt.org/directory

还有一个--manual参数,但在“非交互式”环境下,必须同时指定--manual-auth-hook选项。什么是“非交互式”环境?renew就是非交互式环境,因为不需要外部输出和参与;而certonly(一般)是交互式的,因为执行过程中需要用户输入必要的信息。

本人没看auth-hook脚本的写法,所以用dns方式renew证书是不可行了。那就干脆重新再获取一次证书吧!输入命令:

certbot renew -d 域名 --preferred-challenges dns --manual --server https://acme-v02.api.letsencrypt.org/directory

命令会询问是否同意记录当前机器的ip,选“y”(当然个人认为选n也没问题)。接着命令会输出dns记录的验证信息,按照指定值在dns控制台加上 TXT 记录。dns记录添加完成后等半分钟左右让记录生效,然后按回车键,记录无误的话证书就生成了。

感谢伟大的防火墙和备案机制,又让我长见识了!

参考

  1. Invalid response use certbot in ubuntu
  2. Let’s Encrypt 免费通配符 SSL 证书申请教程
打赏 赞(12)
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

Let’s Encrypt renew出现“Challenge failed for domain xxxx”” 有 2 条评论

  1. 头像zhangsan

    博主你好,我也遇到这个问题了,阿里云未备案而无法申请证书, 我这边因为某种原因在DNS控制台添加TXT记录一直失败, 所以想请教一下你说的解决方案中第二点,暂时将域名解析到境外服务器,获取到证书更新后再解析回来;这一点该如何实施。麻烦了

    回复
    1. tlanyantlanyan 作者

      第一步: 解析dns到境外服务器;
      第二步: 用certbot命令申请证书:certbot certonly –standalone -d xxx.com
      第三步: 拷贝/etc/letsencrypt/archive下的证书和密钥文件到国内服务器;
      第四步: 配置国内服务器的Nginx/tomcat/Apache等web软件

      回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注