Optionally sign initial SOA query (#9672)

* Optionally sign initial SOA query

Added configuration file option to enable signing of the initial SOA query when determining the authoritative nameserver for the zone. Default is disabled.

* Better handling of sign_query configuration and fix lint issues

* Update str casting to match 5503d12395

* Update certbot/CHANGELOG.md

Co-authored-by: alexzorin <alex@zorin.au>

* Update certbot/CHANGELOG.md

Co-authored-by: alexzorin <alex@zorin.au>

* Update dns_rfc2136.py

Updated with feedback from certbot/certbot#9672

---------

Co-authored-by: alexzorin <alex@zorin.au>
This commit is contained in:
Phil Martin
2023-04-25 11:25:57 +10:00
committed by GitHub
co-authored by alexzorin
parent b0d0a83277
commit f378ec4a0f
4 changed files with 55 additions and 6 deletions
@@ -26,8 +26,8 @@ Credentials
Use of this plugin requires a configuration file containing the target DNS
server and optional port that supports RFC 2136 Dynamic Updates, the name
of the TSIG key, the TSIG key secret itself and the algorithm used if it's
different to HMAC-MD5.
of the TSIG key, the TSIG key secret itself, the algorithm used if it's
different to HMAC-MD5, and optionally whether to sign the initial SOA query.
.. code-block:: ini
:name: credentials.ini
@@ -44,6 +44,9 @@ different to HMAC-MD5.
AmKd7ak51vWKgSl12ib86oQRPkpDjg==
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA512
# TSIG sign SOA query (optional, default: false)
dns_rfc2136_sign_query = false
The path to this file can be provided interactively or using the
``--dns-rfc2136-credentials`` command-line argument. Certbot records the
@@ -194,4 +197,34 @@ AmKd7ak51vWKgSl12ib86oQRPkpDjg==";
};
};
Another solution is to add `dns_rfc2136_sign_query = true` to the configuration file and then
add the key to the `match-clients` list within the external zone view. All queries signed with
this key should then be directed to this view, regardless of source IP.
.. code-block:: none
:caption: Split-view BIND configuration with key-based ACLs
key "keyname." {
algorithm hmac-sha512;
secret "4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs \
AmKd7ak51vWKgSl12ib86oQRPkpDjg==";
};
// adjust internal-addresses to suit your needs
acl internal-address { 127.0.0.0/8; 10.0.0.0/8; 192.168.0.0/16; 172.16.0.0/12; };
acl certbot-keys { key keyname.; }
view "external" {
match-clients { acl certbot-keys; !internal-addresses; any; };
zone "example.com." IN {
type master;
file "named.example.com";
update-policy {
grant keyname. name _acme-challenge.example.com. txt;
};
};
};
"""
@@ -90,12 +90,14 @@ class Authenticator(dns_common.DNSAuthenticator):
def _get_rfc2136_client(self) -> "_RFC2136Client":
if not self.credentials: # pragma: no cover
raise errors.Error("Plugin has not been prepared.")
return _RFC2136Client(cast(str, self.credentials.conf('server')),
int(cast(str, self.credentials.conf('port')) or self.PORT),
cast(str, self.credentials.conf('name')),
cast(str, self.credentials.conf('secret')),
self.ALGORITHMS.get(self.credentials.conf('algorithm') or '',
dns.tsig.HMAC_MD5))
dns.tsig.HMAC_MD5),
(self.credentials.conf('sign_query') or '').upper() == "TRUE")
class _RFC2136Client:
@@ -103,13 +105,15 @@ class _RFC2136Client:
Encapsulates all communication with the target DNS server.
"""
def __init__(self, server: str, port: int, key_name: str, key_secret: str,
key_algorithm: dns.name.Name, timeout: int = DEFAULT_NETWORK_TIMEOUT) -> None:
key_algorithm: dns.name.Name, sign_query: bool,
timeout: int = DEFAULT_NETWORK_TIMEOUT) -> None:
self.server = server
self.port = port
self.keyring = dns.tsigkeyring.from_text({
key_name: key_secret
})
self.algorithm = key_algorithm
self.sign_query = sign_query
self._default_timeout = timeout
def add_txt_record(self, record_name: str, record_content: str, record_ttl: int) -> None:
@@ -217,8 +221,9 @@ class _RFC2136Client:
request = dns.message.make_query(domain, dns.rdatatype.SOA, dns.rdataclass.IN)
# Turn off Recursion Desired bit in query
request.flags ^= dns.flags.RD
# Use our TSIG keyring
request.use_tsig(self.keyring, algorithm=self.algorithm)
# Use our TSIG keyring if configured
if self.sign_query:
request.use_tsig(self.keyring, algorithm=self.algorithm)
try:
try: