rthalley / dnspython

a powerful DNS toolkit for python
http://www.dnspython.org
Other
2.46k stars 519 forks source link

Creating PTR Record is successful but returns error "The TSIG signature fails to verify" #1132

Closed mmn01-sky closed 2 months ago

mmn01-sky commented 2 months ago

Describe the bug I am managing DNS entries on a Microsoft DNS Server using Kerberos for authentication and GSS-TSIG.

When creating the DNS A Record, it creates successfully and resolves as expected.

➜ dig mous.one.zero.local  @one.zero.local

; <<>> DiG 9.10.6 <<>> mous.one.zero.local @one.zero.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2206
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;mous.one.zero.local.           IN      A

;; ANSWER SECTION:
mous.one.zero.local.    300     IN      A       10.131.176.91

;; Query time: 22 msec
;; SERVER: 10.20.188.242#53(10.20.188.242)
;; WHEN: Fri Sep 20 15:51:45 BST 2024
;; MSG SIZE  rcvd: 64

However when creating the corresponding PTR record, I successfully create the PTR however I receive an error. I double checked with our Windows DNS team who confirmed the PTR did not previously exist until I ran my script which successfully created it on their end.

Full log and python script have been attached for completeness.

Python error message when attempting to create the PTR:

2024-09-21 10:11:50.972 - dns-runner - MainThread - ERROR - Failed to Update DNS Record. Error:
The TSIG signature fails to verify.
Stacktrace:
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/dns/tsig.py", line 121, in verify
    return self.gssapi_context.verify_signature(self.data, expected)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib64/python3.12/site-packages/gssapi/_utils.py", line 165, in check_last_err
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib64/python3.12/site-packages/gssapi/sec_contexts.py", line 215, in verify_signature
    return rmessage.verify_mic(self, message, mic)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "gssapi/raw/message.pyx", line 137, in gssapi.raw.message.verify_mic
gssapi.raw.exceptions.BadMICError: Major (393216): A token had an invalid Message Integrity Check (MIC), Minor (2249944333): Packet was replayed in wrong direction

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/vdc/dns_runner/python/main3.py", line 209, in main
    response_ptr_record = dns.query.tcp(update_ptr, soa_server_ip)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/dns/query.py", line 1028, in tcp
    (r, received_time) = receive_tcp(
                         ^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/dns/query.py", line 951, in receive_tcp
    r = dns.message.from_wire(
        ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/dns/message.py", line 1367, in from_wire
    m = reader.read()
        ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/dns/message.py", line 1264, in read
    self._get_section(MessageSection.ADDITIONAL, adcount)
  File "/usr/local/lib/python3.12/site-packages/dns/message.py", line 1208, in _get_section
    self.message.tsig_ctx = dns.tsig.validate(
                            ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/dns/tsig.py", line 310, in validate
    ctx.verify(rdata.mac)
  File "/usr/local/lib/python3.12/site-packages/dns/tsig.py", line 124, in verify
    raise BadSignature
dns.tsig.BadSignature: The TSIG signature fails to verify.

PTR record being created and resolving as expected:

➜ dig -x 10.131.176.91 @one.zero.local      

; <<>> DiG 9.10.6 <<>> -x 10.131.176.91 @one.zero.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26802
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;91.176.131.10.in-addr.arpa.    IN      PTR

;; ANSWER SECTION:
91.176.131.10.in-addr.arpa. 300 IN      PTR     mous.one.zero.local.

;; Query time: 23 msec
;; SERVER: 10.20.188.242#53(10.20.188.242)
;; WHEN: Fri Sep 20 15:51:23 BST 2024
;; MSG SIZE  rcvd: 88

To Reproduce I have attached my python script and log output (with args) for completeness (a lot of help from https://github.com/rthalley/dnspython/pull/530) but below is my function for creating the DNS records.

def main(selected_dns_config, record_type, operation, vm_hostname, vm_ip_address):
    try:

        dns_config = load_dns_config(selected_dns_config)
        logging.info(f"DNS Config Loaded:\n{json.dumps(dns_config, indent=4)}")

        kerberos_authenticate(dns_config['keytab'], dns_config['krbccache'], dns_config['principal'])
        soa_server, soa_server_ip = get_dns_server(dns_config['dns_server'])

        keyring, keyname = gss_tsig_init_ctx(soa_server, soa_server_ip)
        update_a = dns.update.Update(zone=dns_config['zone'], keyring=keyring, keyname=keyname, keyalgorithm=dns.tsig.GSS_TSIG)

        update_ptr = dns.update.Update(zone=dns_config['reverse_zone'], keyring=keyring, keyname=keyname, keyalgorithm=dns.tsig.GSS_TSIG)

        reverse_vm_ip_address = dns.reversename.from_address(vm_ip_address)

        if operation == "add":
            update_a.add(vm_hostname, 300, record_type, vm_ip_address)
            logging.info(f"Prepared DNS A record create query:\n{update_a.to_text()}")
            response_a_record = dns.query.tcp(update_a, soa_server_ip)
            logging.info(f"Successfully added A record. Output:\n{response_a_record}")

            update_ptr.add(reverse_vm_ip_address, 300, 'PTR', vm_hostname)
            logging.info(f"Prepared DNS PTR record create query:\n{update_ptr.to_text()}")
            response_ptr_record = dns.query.tcp(update_ptr, soa_server_ip)
            logging.info(f"Successfully added PTR record. Output:\n{response_ptr_record}")

        elif operation == "delete":
            update_a.delete(vm_hostname, record_type)
            logging.info(f"Prepared DNS A record delete query:\n{update_a.to_text()}")
            response_a_record = dns.query.tcp(update_a, soa_server_ip)
            logging.info(f"Successfully deleted A record. Output:\n{response_a_record}")

            update_ptr.delete(reverse_vm_ip_address, 'PTR')
            logging.info(f"Prepared DNS PTR record delete query:\n{update_ptr.to_text()}")
            response_ptr_record = dns.query.tcp(update_ptr, soa_server_ip)
            logging.info(f"Successfully deleted PTR record. Output:\n{response_ptr_record}")

    except Exception as e:
        stacktrace = traceback.format_exc()
        logging.error(f"Failed to Update DNS Record. Error:\n{e}\nStacktrace:\n{stacktrace}")
        sys.exit(1)

script-and-logs.zip

Context (please complete the following information):

rthalley commented 2 months ago

The problem here is that dnspython doesn't like the TSIG signature on the response to the update. I don't have any ability to debug GSS-TSIG issues. It's very strange that you don't see this error for the A update, as you'd think if there were a bug in dnspython's GSS-TSIG response calculation that would fail too.

rthalley commented 2 months ago

Also, if I google I see people getting the same kind of error with nsupdate sometimes, so I suspect you might just have to catch this error and suppress it.

mmn01-sky commented 2 months ago

@rthalley thanks for your response. I'll try enable some debug logging (somewhere) and see if I can get to the root of the issue.

Do you know if it's possible using dnspython to enable the automatic creation of the PTR when creating the A records against a Windows DNS Server? The Windows DNS Server does support this feature, i'm just not sure how to invoke it from dnspython or nsupdate.

rthalley commented 2 months ago

Re automatic PTR update: this looks to be a server-side thing, in particular some setting in the Microsoft DHCP server. So AFAIK there is no way for the client to ask for this service.

mmn01-sky commented 2 months ago

Just to add, I receive the same error when creating the PTR via nsupdate so it's definitely not a dnspython issue.

Thanks for your support. I'll close down this ticket.

If I do find a solution, I'll be sure to post it as a comment.