nsupdate: Use provided TSIG key for all queries (#63174)

In addition to signing update queries also use the TSIG key to sign
lookup queries. By doing that we allow a hidden master to not only to
be looked down network wise, but also TSIG wise.

A bonus benefit of threating update queries and lookup queries more
the same is that will allow for all queries to be refactored into a
shared helper method. Currently we have a bit too much duplicated code
within the module.
This commit is contained in:
Andreas Olsson 2019-12-27 16:02:25 +01:00 committed by René Moser
parent c2fed8603c
commit b733178bac
2 changed files with 28 additions and 15 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- nsupdate - Use provided TSIG key to not only sign update queries but also lookup queries

View file

@ -200,21 +200,6 @@ class RecordManager(object):
def __init__(self, module): def __init__(self, module):
self.module = module self.module = module
if module.params['zone'] is None:
if module.params['record'][-1] != '.':
self.module.fail_json(msg='record must be absolute when omitting zone parameter')
self.zone = self.lookup_zone()
else:
self.zone = module.params['zone']
if self.zone[-1] != '.':
self.zone += '.'
if module.params['record'][-1] != '.':
self.fqdn = module.params['record'] + '.' + self.zone
else:
self.fqdn = module.params['record']
if module.params['key_name']: if module.params['key_name']:
try: try:
self.keyring = dns.tsigkeyring.from_text({ self.keyring = dns.tsigkeyring.from_text({
@ -232,6 +217,21 @@ class RecordManager(object):
else: else:
self.algorithm = module.params['key_algorithm'] self.algorithm = module.params['key_algorithm']
if module.params['zone'] is None:
if module.params['record'][-1] != '.':
self.module.fail_json(msg='record must be absolute when omitting zone parameter')
self.zone = self.lookup_zone()
else:
self.zone = module.params['zone']
if self.zone[-1] != '.':
self.zone += '.'
if module.params['record'][-1] != '.':
self.fqdn = module.params['record'] + '.' + self.zone
else:
self.fqdn = module.params['record']
if self.module.params['type'].lower() == 'txt' and self.module.params['value'] is not None: if self.module.params['type'].lower() == 'txt' and self.module.params['value'] is not None:
self.value = list(map(self.txt_helper, self.module.params['value'])) self.value = list(map(self.txt_helper, self.module.params['value']))
else: else:
@ -248,11 +248,15 @@ class RecordManager(object):
name = dns.name.from_text(self.module.params['record']) name = dns.name.from_text(self.module.params['record'])
while True: while True:
query = dns.message.make_query(name, dns.rdatatype.SOA) query = dns.message.make_query(name, dns.rdatatype.SOA)
if self.keyring:
query.use_tsig(keyring=self.keyring, algorithm=self.algorithm)
try: try:
if self.module.params['protocol'] == 'tcp': if self.module.params['protocol'] == 'tcp':
lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port']) lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
else: else:
lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port']) lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
except (dns.tsig.PeerBadKey, dns.tsig.PeerBadSignature) as e:
self.module.fail_json(msg='TSIG update error (%s): %s' % (e.__class__.__name__, to_native(e)))
except (socket_error, dns.exception.Timeout) as e: except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg='DNS server error: (%s): %s' % (e.__class__.__name__, to_native(e))) self.module.fail_json(msg='DNS server error: (%s): %s' % (e.__class__.__name__, to_native(e)))
if lookup.rcode() in [dns.rcode.SERVFAIL, dns.rcode.REFUSED]: if lookup.rcode() in [dns.rcode.SERVFAIL, dns.rcode.REFUSED]:
@ -400,15 +404,22 @@ class RecordManager(object):
def ttl_changed(self): def ttl_changed(self):
query = dns.message.make_query(self.fqdn, self.module.params['type']) query = dns.message.make_query(self.fqdn, self.module.params['type'])
if self.keyring:
query.use_tsig(keyring=self.keyring, algorithm=self.algorithm)
try: try:
if self.module.params['protocol'] == 'tcp': if self.module.params['protocol'] == 'tcp':
lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port']) lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
else: else:
lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port']) lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
except (dns.tsig.PeerBadKey, dns.tsig.PeerBadSignature) as e:
self.module.fail_json(msg='TSIG update error (%s): %s' % (e.__class__.__name__, to_native(e)))
except (socket_error, dns.exception.Timeout) as e: except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg='DNS server error: (%s): %s' % (e.__class__.__name__, to_native(e))) self.module.fail_json(msg='DNS server error: (%s): %s' % (e.__class__.__name__, to_native(e)))
if lookup.rcode() != dns.rcode.NOERROR:
self.module.fail_json(msg='Failed to lookup TTL of existing matching record.')
current_ttl = lookup.answer[0].ttl current_ttl = lookup.answer[0].ttl
return current_ttl != self.module.params['ttl'] return current_ttl != self.module.params['ttl']