fix: wait for public DNS resolvers before returning from verify_dns
Previously verify_dns_record_external returned True as soon as the TXT record was found on authoritative NS (ns1/ns2.reg.ru). But Let's Encrypt validates through recursive resolvers (like 8.8.8.8) which may have cached the old DNS response. For wildcard+base domain certs, the second challenge token was invisible to LE due to stale cache on recursive DNS. Now the function requires the record to be visible on at least one public recursive resolver (1.1.1.1/8.8.8.8/9.9.9.9) before returning success, ensuring Let's Encrypt can verify the challenge.
This commit is contained in:
+29
-17
@@ -1569,20 +1569,28 @@ class LetsEncryptManager:
|
||||
self.logger.warning(" Не удалось определить authoritative NS, используем публичные DNS")
|
||||
|
||||
public_resolvers = ["1.1.1.1", "8.8.8.8", "9.9.9.9"]
|
||||
found_on_auth = False
|
||||
|
||||
for attempt in range(attempts):
|
||||
try:
|
||||
# 1) Проверка через authoritative NS (самый точный источник)
|
||||
for ns in auth_ns:
|
||||
ns_output = self._query_txt_nslookup(full_domain, ns)
|
||||
if expected_value in ns_output:
|
||||
self.logger.info(f" ✅ Попытка {attempt + 1}/{attempts}: TXT найден на authoritative NS {ns}")
|
||||
for line in ns_output.split('\n'):
|
||||
if 'text =' in line.lower() or expected_value[:20] in line:
|
||||
self.logger.info(f" {line.strip()}")
|
||||
return True
|
||||
if not found_on_auth:
|
||||
for ns in auth_ns:
|
||||
ns_output = self._query_txt_nslookup(full_domain, ns)
|
||||
if expected_value in ns_output:
|
||||
self.logger.info(f" ✅ Попытка {attempt + 1}/{attempts}: TXT найден на authoritative NS {ns}")
|
||||
for line in ns_output.split('\n'):
|
||||
if 'text =' in line.lower() or expected_value[:20] in line:
|
||||
self.logger.info(f" {line.strip()}")
|
||||
found_on_auth = True
|
||||
break
|
||||
|
||||
# 2) Проверка через публичные резолверы
|
||||
# ВАЖНО: Let's Encrypt валидирует через рекурсивные резолверы, а не
|
||||
# authoritative NS напрямую. Поэтому ОБЯЗАТЕЛЬНО ждём, пока запись
|
||||
# станет видна через публичные DNS (8.8.8.8 и т.д.), иначе при
|
||||
# wildcard + base domain сертификатах кэш рекурсивных резолверов
|
||||
# может содержать старую версию без нового токена.
|
||||
found_public = False
|
||||
for resolver in public_resolvers:
|
||||
res_output = self._query_txt_nslookup(full_domain, resolver)
|
||||
@@ -1597,16 +1605,20 @@ class LetsEncryptManager:
|
||||
if found_public:
|
||||
return True
|
||||
|
||||
# 3) Fallback на системный resolver
|
||||
default_output = self._query_txt_nslookup(full_domain)
|
||||
if expected_value in default_output:
|
||||
self.logger.info(f" ✅ Попытка {attempt + 1}/{attempts}: TXT найден через системный DNS")
|
||||
for line in default_output.split('\n'):
|
||||
if 'text =' in line.lower() or expected_value[:20] in line:
|
||||
self.logger.info(f" {line.strip()}")
|
||||
return True
|
||||
# 3) Fallback: если нет authoritative NS, проверяем системный resolver
|
||||
if not auth_ns:
|
||||
default_output = self._query_txt_nslookup(full_domain)
|
||||
if expected_value in default_output:
|
||||
self.logger.info(f" ✅ Попытка {attempt + 1}/{attempts}: TXT найден через системный DNS")
|
||||
for line in default_output.split('\n'):
|
||||
if 'text =' in line.lower() or expected_value[:20] in line:
|
||||
self.logger.info(f" {line.strip()}")
|
||||
return True
|
||||
|
||||
self.logger.info(f" ⏳ Попытка {attempt + 1}/{attempts}: DNS запись не найдена, ждём...")
|
||||
if found_on_auth:
|
||||
self.logger.info(f" ⏳ Попытка {attempt + 1}/{attempts}: TXT есть на authoritative NS, ждём распространения на публичные DNS...")
|
||||
else:
|
||||
self.logger.info(f" ⏳ Попытка {attempt + 1}/{attempts}: DNS запись не найдена, ждём...")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.info(f" ⚠️ Попытка {attempt + 1}/{attempts}: Ошибка nslookup - {e}")
|
||||
|
||||
Reference in New Issue
Block a user