Ausgabe

Ausgabe

Ausgabe

Ich verwende die DNS-01-Challenge von letsencrypt certbot, aber sie stellt keine Zertifikate aus, die tiefer als eine Subdomain-Ebene liegen.

benannt.conf

# grep -A 3 ^key /etc/bind/named.conf.local
key "certbot." {
    algorithm hmac-sha512;
    secret    "[REDACTED]";
};
# grep -A 2 example.tld /etc/bind/named.conf.local
zone    "example.tld"              {
    type            master;
    file            "/var/cache/bind/fdb.example.tld.signed";
    allow-transfer  { pub-ns-acl; };
    update-policy   {
        grant   certbot. name _acme-challenge.example.tld. txt;
    };
};

Einzelne Subdomäne

Ich weiß, dass meine Schlüssel richtig konfiguriert sind, weil ich Zertifikate für eine einzelne Subdomäne ausstellen kann, auch wenn es sich um ein Platzhalterzeichen handelt:

# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136/cred.ini --preferred-challenges=dns [email protected] --agree-tos -d *.example.tld -d example.tld --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-rfc2136, Installer None
Cert not due for renewal, but simulating renewal for dry run
Renewing an existing certificate

IMPORTANT NOTES:
 - The dry run was successful.

Doppelte Subdomain

Hier funktioniert es nicht.

# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136/cred.ini --preferred-challenges=dns [email protected] --agree-tos -d example.tld -d *.example.tld -d www.www.example.tld --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-rfc2136, Installer None                                                                                           

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -                                                                       
You have an existing certificate that contains a portion of the domains you requested (ref: /etc/letsencrypt/renewal/example.tld.conf)                                                                                               

It contains these names: *.example.tld, example.tld

You requested these names for the new certificate: *.example.tld, example.tld, www.www.example.tld.

Do you want to expand and replace this existing certificate with the new certificate?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(E)xpand/(C)ancel: E
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for www.www.example.tld
Cleaning up challenges
Encountered exception during recovery: 
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 75, in handle_authorizations
    resp = self._solve_challenges(aauthzrs)
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 139, in _solve_challenges
    resp = self.auth.perform(all_achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 57, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 76, in _perform
    self._get_rfc2136_client().add_txt_record(validation_name, validation, self.ttl)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 135, in add_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/error_handler.py", line 108, in _call_registered
    self.funcs[-1]()
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 323, in _cleanup_challenges
    self.auth.cleanup(achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 76, in cleanup
    self._cleanup(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 79, in _cleanup
    self._get_rfc2136_client().del_txt_record(validation_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 170, in del_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED
Received response from server: REFUSED

Ausführliche Ausgabe:

# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/letsencrypt/rfc2136/cred.ini --preferred-challenges=dns [email protected] --agree-tos -d example.tld -d *.example.tld -d www.www.example.tld --dry-run -vvv
Root logging level set at -10
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requested authenticator dns-rfc2136 and installer None
Single candidate plugin: * dns-rfc2136
Description: Obtain certificates using a DNS TXT record (if you are using BIND for DNS).
Interfaces: IAuthenticator, IPlugin
Entry point: dns-rfc2136 = certbot_dns_rfc2136.dns_rfc2136:Authenticator
Initialized: <certbot_dns_rfc2136.dns_rfc2136.Authenticator object at 0x7fda4a6974e0>
Prep: True
Selected authenticator <certbot_dns_rfc2136.dns_rfc2136.Authenticator object at 0x7fda4a6974e0> and installer None
Plugins selected: Authenticator dns-rfc2136, Installer None
Picked account: <Account(RegistrationResource(body=Registration(key=None, contact=(), agreement=None, status=None, terms_of_service_agreed=None, only_return_existing=None, external_account_binding=None), uri='https://acme-staging-v02.api.letsencrypt.org/acme/acct/12742232', new_authzr_uri=None, terms_of_service=None), [REDACTED], Meta(creation_dt=datetime.datetime(2020, 3, 11, 1, 14, 11, tzinfo=<UTC>), creation_host='localhost'))>
Sending GET request to https://acme-staging-v02.api.letsencrypt.org/directory.
Starting new HTTPS connection (1): acme-staging-v02.api.letsencrypt.org:443
https://acme-staging-v02.api.letsencrypt.org:443 "GET /directory HTTP/1.1" 200 724
Received response:
HTTP 200
Server: nginx
Date: Fri, 21 Aug 2020 15:47:02 GMT
Content-Type: application/json
Content-Length: 724
Connection: keep-alive
Cache-Control: public, max-age=0, no-cache
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "cwHOlqiOgc0": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
  "keyChange": "https://acme-staging-v02.api.letsencrypt.org/acme/key-change",
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf",
    "website": "https://letsencrypt.org/docs/staging-environment/"
  },
  "newAccount": "https://acme-staging-v02.api.letsencrypt.org/acme/new-acct",
  "newNonce": "https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce",
  "newOrder": "https://acme-staging-v02.api.letsencrypt.org/acme/new-order",
  "revokeCert": "https://acme-staging-v02.api.letsencrypt.org/acme/revoke-cert"
}
Renewal conf file /etc/letsencrypt/renewal/www.otherdomain.tld.conf is broken. Skipping.
Traceback was:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/cert_manager.py", line 383, in _search_lineages
    candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
  File "/usr/lib/python3/dist-packages/certbot/storage.py", line 463, in __init__
    self._check_symlinks()
  File "/usr/lib/python3/dist-packages/certbot/storage.py", line 522, in _check_symlinks
    "expected {0} to be a symlink".format(link))
certbot.errors.CertStorageError: expected /etc/letsencrypt/live/www.otherdomain.tld/cert.pem to be a symlink


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
You have an existing certificate that contains a portion of the domains you
requested (ref: /etc/letsencrypt/renewal/example.tld.conf)

It contains these names: *.example.tld, example.tld

You requested these names for the new certificate: *.example.tld, example.tld,
www.www.example.tld.

Do you want to expand and replace this existing certificate with the new
certificate?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(E)xpand/(C)ancel: E
Renewing an existing certificate
Requesting fresh nonce
Sending HEAD request to https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce.
https://acme-staging-v02.api.letsencrypt.org:443 "HEAD /acme/new-nonce HTTP/1.1" 200 0
Received response:
HTTP 200
Server: nginx
Date: Fri, 21 Aug 2020 15:47:03 GMT
Connection: keep-alive
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: [REDACTED]
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800


Storing nonce: [REDACTED]
JWS payload:
b'{\n  "identifiers": [\n    {\n      "type": "dns",\n      "value": "*.example.tld"\n    },\n    {\n      "type": "dns",\n      "value": "example.tld"\n    },\n    {\n      "type": "dns",\n      "value": "www.www.example.tld"\n    }\n  ]\n}'
Sending POST request to https://acme-staging-v02.api.letsencrypt.org/acme/new-order:
{
  "protected": "[REDACTED]",
  "signature": "[REDACTED]",
  "payload": "[REDACTED]"
}
https://acme-staging-v02.api.letsencrypt.org:443 "POST /acme/new-order HTTP/1.1" 201 621
Received response:
HTTP 201
Server: nginx
Date: Fri, 21 Aug 2020 15:47:03 GMT
Content-Type: application/json
Content-Length: 621
Connection: keep-alive
Boulder-Requester: 12742232
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
Location: https://acme-staging-v02.api.letsencrypt.org/acme/order/[REDACTED]/[REDACTED]
Replay-Nonce: [REDACTED]
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "status": "pending",
  "expires": "2020-08-28T15:44:16Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "*.example.tld"
    },
    {
      "type": "dns",
      "value": "example.tld"
    },
    {
      "type": "dns",
      "value": "www.www.example.tld"
    }
  ],
  "authorizations": [
    "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/[REDACTED]",
    "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/[REDACTED]",
    "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/[REDACTED]"
  ],
  "finalize": "https://acme-staging-v02.api.letsencrypt.org/acme/finalize/[REDACTED]/[REDACTED]"
}
Storing nonce: [REDACTED]
JWS payload:
b''
Sending POST request to https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/95814309:
{
  "protected": "[REDACTED]",
  "signature": "[REDACTED]",
  "payload": ""
}
https://acme-staging-v02.api.letsencrypt.org:443 "POST /acme/authz-v3/[REDACTED] HTTP/1.1" 200 472
Received response:
HTTP 200
Server: nginx
Date: Fri, 21 Aug 2020 15:47:03 GMT
Content-Type: application/json
Content-Length: 472
Connection: keep-alive
Boulder-Requester: 12742232
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: [REDACTED]
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "identifier": {
    "type": "dns",
    "value": "example.tld"
  },
  "status": "valid",
  "expires": "2020-09-17T22:29:52Z",
  "challenges": [
    {
      "type": "dns-01",
      "status": "valid",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/[REDACTED]/MUKGGw",
      "token": "[REDACTED]",
      "validationRecord": [
        {
          "hostname": "example.tld"
        }
      ]
    }
  ],
  "wildcard": true
}
Storing nonce: [REDACTED]
JWS payload:
b''
Sending POST request to https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/[REDACTED]:
{
  "protected": "[REDACTED]",
  "signature": "[REDACTED]",
  "payload": ""
}
https://acme-staging-v02.api.letsencrypt.org:443 "POST /acme/authz-v3/[REDACTED] HTTP/1.1" 200 452
Received response:
HTTP 200
Server: nginx
Date: Fri, 21 Aug 2020 15:47:04 GMT
Content-Type: application/json
Content-Length: 452
Connection: keep-alive
Boulder-Requester: 12742232
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: [REDACTED]-vau0I
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "identifier": {
    "type": "dns",
    "value": "example.tld"
  },
  "status": "valid",
  "expires": "2020-09-17T22:29:52Z",
  "challenges": [
    {
      "type": "dns-01",
      "status": "valid",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/[REDACTED]/[REDACTED]",
      "token": "[REDACTED]",
      "validationRecord": [
        {
          "hostname": "example.tld"
        }
      ]
    }
  ]
}
Storing nonce: [REDACTED]
JWS payload:
b''
Sending POST request to https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/97803217:
{
  "protected": "[REDACTED]",
  "signature": "[REDACTED]",
  "payload": ""
}
https://acme-staging-v02.api.letsencrypt.org:443 "POST /acme/authz-v3/97803217 HTTP/1.1" 200 812
Received response:
HTTP 200
Server: nginx
Date: Fri, 21 Aug 2020 15:47:04 GMT
Content-Type: application/json
Content-Length: 812
Connection: keep-alive
Boulder-Requester: 12742232
Cache-Control: public, max-age=0, no-cache
Link: <https://acme-staging-v02.api.letsencrypt.org/directory>;rel="index"
Replay-Nonce: [REDACTED]
X-Frame-Options: DENY
Strict-Transport-Security: max-age=604800

{
  "identifier": {
    "type": "dns",
    "value": "www.www.example.tld"
  },
  "status": "pending",
  "expires": "2020-08-28T15:44:16Z",
  "challenges": [
    {
      "type": "http-01",
      "status": "pending",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/[REDACTED]/[REDACTED]",
      "token": "[REDACTED]"
    },
    {
      "type": "dns-01",
      "status": "pending",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/[REDACTED]/[REDACTED]",
      "token": "[REDACTED]"
    },
    {
      "type": "tls-alpn-01",
      "status": "pending",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/[REDACTED]/[REDACTED]",
      "token": "[REDACTED]"
    }
  ]
}
Storing nonce: [REDACTED]
Performing the following challenges:
dns-01 challenge for www.www.example.tld
No authoritative SOA record found for _acme-challenge.www.www.example.tld
No authoritative SOA record found for www.www.example.tld
No authoritative SOA record found for www.example.tld
Received authoritative SOA response for example.tld
Encountered exception:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 75, in handle_authorizations
    resp = self._solve_challenges(aauthzrs)
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 139, in _solve_challenges
    resp = self.auth.perform(all_achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 57, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 76, in _perform
    self._get_rfc2136_client().add_txt_record(validation_name, validation, self.ttl)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 135, in add_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED

Calling registered functions
Cleaning up challenges
No authoritative SOA record found for _acme-challenge.www.www.example.tld
No authoritative SOA record found for www.www.example.tld
No authoritative SOA record found for www.example.tld
Received authoritative SOA response for example.tld
Encountered exception during recovery: 
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 75, in handle_authorizations
    resp = self._solve_challenges(aauthzrs)
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 139, in _solve_challenges
    resp = self.auth.perform(all_achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 57, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 76, in _perform
    self._get_rfc2136_client().add_txt_record(validation_name, validation, self.ttl)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 135, in add_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/certbot/error_handler.py", line 108, in _call_registered
    self.funcs[-1]()
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 323, in _cleanup_challenges
    self.auth.cleanup(achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 76, in cleanup
    self._cleanup(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 79, in _cleanup
    self._get_rfc2136_client().del_txt_record(validation_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 170, in del_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED
Exiting abnormally:
Traceback (most recent call last):
  File "/usr/bin/certbot", line 11, in <module>
    load_entry_point('certbot==0.31.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1365, in main
    return config.func(config, plugins)
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 1250, in certonly
    lineage = _get_and_save_cert(le_client, config, domains, certname, lineage)
  File "/usr/lib/python3/dist-packages/certbot/main.py", line 116, in _get_and_save_cert
    renewal.renew_cert(config, domains, le_client, lineage)
  File "/usr/lib/python3/dist-packages/certbot/renewal.py", line 310, in renew_cert
    new_cert, new_chain, new_key, _ = le_client.obtain_certificate(domains, new_key)
  File "/usr/lib/python3/dist-packages/certbot/client.py", line 353, in obtain_certificate
    orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names)
  File "/usr/lib/python3/dist-packages/certbot/client.py", line 389, in _get_order_and_authorizations
    authzr = self.auth_handler.handle_authorizations(orderr, best_effort)
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 75, in handle_authorizations
    resp = self._solve_challenges(aauthzrs)
  File "/usr/lib/python3/dist-packages/certbot/auth_handler.py", line 139, in _solve_challenges
    resp = self.auth.perform(all_achalls)
  File "/usr/lib/python3/dist-packages/certbot/plugins/dns_common.py", line 57, in perform
    self._perform(domain, validation_domain_name, validation)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 76, in _perform
    self._get_rfc2136_client().add_txt_record(validation_name, validation, self.ttl)
  File "/usr/lib/python3/dist-packages/certbot_dns_rfc2136/dns_rfc2136.py", line 135, in add_txt_record
    .format(dns.rcode.to_text(rcode)))
certbot.errors.PluginError: Received response from server: REFUSED
Received response from server: REFUSED

Beobachtungen, mögliche Lösung

Eine Zeile, die mir ins Auge fiel, war diese:

No authoritative SOA record found for _acme-challenge.www.www.example.tld

Certbot versucht wahrscheinlich, ein nsupdate auf der Subdomäne durchzuführen _acme-challenge.www.www.example.tld, update-policylässt dies jedoch nicht zu:

grant certbot. name _acme-challenge.example.tld. txt;

Die Frage ist also:Welche Update-Richtlinienregel kann ich verwenden, um nsupdate auf zuzulassen _acme-challenge.*.*.example.tld? Gibt es eine Regel, die weiteres Subdomaining wie abdeckt _acme-challenge.*.*.*.example.tld?

Antwort1

Nachdem ich mehr als einen Tag damit verbracht hatte, fand ich es, als ich die Frage schrieb.

Da hinter der Subdomäne ganz links keine Platzhaltererweiterung verfügbar ist, können Sie den update-policyPlatzhalterregeltyp auch nicht auf diese Weise verwenden. Das heißt, es funktioniert nicht für _acme-domain.*.example.tld, aber schon für *.www.example.tld.

Da ich bereits weiß, dass die Subdomäne ganz links ist , ist ein Platzhalter unnötig. Das Beste, was ich tun kann, ist, das explizit für alle Subdomänen _acme-challengefestzulegen :update-policy

update-policy {
    grant certbot. name _acme-challenge.example.tld. txt;
    grant certbot. name _acme-challenge.www.example.tld. txt;
};

Quelle:Bind9 Docs: Dynamische Update-Richtlinien

Antwort2

Sie können nicht direkt tun, was Sie versucht haben, aber wenn Sie es nicht explizit in einer Update-Richtlinie auflisten möchten,DÜRFENkommen Sie mit einigen CNAME-Tricks davon und sorgen Sie für eine Trennung von Rollen/Zugriff.

Erstens:

update-policy {
  grant key_update_acme wildcard *.acme.dyn.example.tld. TXT;
}

Und erklären Sie dann in allen Ihren Zonen:

_acme-challenge.www.example.tld. IN CNAME www.example.tld.acme.dyn.example.tld.
_acme-challenge.example.tld. IN CNAME example.tld.acme.dyn.example.tld.
(etc, for each subdomain you want)

Der Schlüssel key_update_acmedarf NUR zum Aktualisieren von TXT-Einträgen innerhalb der Subdomäne von verwendet werden *.acme.dyn.example.tld..

Certbot unterstützt dies jedoch nicht nativ, daher müssen Sie einen anderen ACME-Client verwenden.

verwandte Informationen