InfraRunBook
    Back to articles

    Securing BIND9 Zone Transfers with TSIG Keys – Complete Production Guide

    DNS
    Published: Mar 25, 2026
    Updated: Mar 25, 2026

    Learn how to configure TSIG (Transaction Signature) keys on BIND9 to authenticate and secure DNS zone transfers between primary and secondary servers in production.

    Securing BIND9 Zone Transfers with TSIG Keys – Complete Production Guide

    What Is TSIG and Why Does It Matter for DNS Security

    Transaction SIGnature (TSIG) is a mechanism defined in RFC 2845 that provides cryptographic authentication for DNS messages, including zone transfers (AXFR/IXFR), dynamic DNS updates, and server-to-server notify messages. Without TSIG, any host that can reach your DNS server on port 53 can request a complete dump of your zone data — exposing every hostname, IP address, mail server record, and internal service you operate.

    In production environments, unprotected zone transfers represent a serious reconnaissance risk. An attacker who successfully completes an unauthorized AXFR receives a full map of your network topology at no cost. TSIG eliminates this threat by requiring both sides of a DNS transaction to prove possession of a shared HMAC secret before any data is exchanged.

    BIND9 supports TSIG using HMAC-MD5 (legacy, avoid), HMAC-SHA1 (deprecated), HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512. For all new deployments HMAC-SHA256 or HMAC-SHA512 should be used exclusively. This guide uses HMAC-SHA256 throughout.

    How TSIG Authentication Works

    TSIG operates at the DNS protocol layer by appending a special resource record of type TSIG to each DNS message. The TSIG record contains:

    • The algorithm identifier (e.g.,
      hmac-sha256
      )
    • The current Unix timestamp (prevents replay attacks)
    • A fudge value representing the acceptable clock skew window (default 300 seconds)
    • The HMAC signature computed over the full DNS message payload
    • The name of the key used for signing

    When the receiving server processes the message, it independently computes the HMAC using its own copy of the shared secret. If the computed signature matches the one in the TSIG record, and the timestamp falls within the fudge window, the message is authenticated and processed. If either check fails, the server returns a REFUSED or NOTAUTH response and logs the failure.

    This design means two things must be true for TSIG to function reliably: both servers must hold byte-for-byte identical key secrets, and their system clocks must remain synchronized. This makes a working NTP or chrony configuration a hard prerequisite before deploying TSIG.

    Lab Environment and Prerequisites

    This guide uses the following production-representative environment:

    • Primary DNS server: sw-infrarunbook-01 at 192.168.10.10, running BIND9 9.18.x on Ubuntu 22.04
    • Secondary DNS server: sw-infrarunbook-02 at 192.168.10.11, running BIND9 9.18.x on Ubuntu 22.04
    • Zone under management: solvethenetwork.com
    • Administrative user: infrarunbook-admin (sudo access on both hosts)

    Before proceeding, verify clock synchronization on both servers:

    timedatectl status

    Confirm you see

    System clock synchronized: yes
    . If not, install and start chrony before continuing:

    apt install chrony -y
    systemctl enable --now chrony
    chronyc tracking

    Also confirm BIND9 is installed and operational on both hosts:

    systemctl status named

    Step 1 – Generate the TSIG Key on the Primary Server

    BIND9 ships with

    tsig-keygen
    , a dedicated utility that generates a cryptographically random base64-encoded secret and outputs a complete BIND9 key{} block ready for inclusion in your configuration. Run this on sw-infrarunbook-01:

    tsig-keygen -a hmac-sha256 solvethenetwork-transfer-key

    The output will look similar to the following (your secret value will differ):

    key "solvethenetwork-transfer-key" {
            algorithm hmac-sha256;
            secret "4xK9mB2vLpQrN7wYjCdTzUeAoHsGfI3R8WnXyZuJ5Ek=";
    };

    Create a dedicated directory for TSIG key files and save the output directly to a file:

    mkdir -p /etc/bind/tsig-keys
    chown root:bind /etc/bind/tsig-keys
    chmod 750 /etc/bind/tsig-keys
    
    tsig-keygen -a hmac-sha256 solvethenetwork-transfer-key \
        > /etc/bind/tsig-keys/solvethenetwork-transfer.key
    
    chown root:bind /etc/bind/tsig-keys/solvethenetwork-transfer.key
    chmod 640 /etc/bind/tsig-keys/solvethenetwork-transfer.key

    The

    640
    permission ensures the
    bind
    process can read the key while no other unprivileged user can access the secret. The key directory itself is set to
    750
    for the same reason.

    Security note: Never commit TSIG key files to a version control repository. Never reuse the same key across environments. Treat the base64 secret with the same sensitivity as a password.

    Step 2 – Configure the Primary BIND9 Server

    On sw-infrarunbook-01, open

    /etc/bind/named.conf
    and add an include directive to load the key file:

    include "/etc/bind/tsig-keys/solvethenetwork-transfer.key";

    Next, add a

    server
    block that instructs BIND9 to automatically sign all outgoing messages destined for the secondary server IP with this key. Add this block to
    /etc/bind/named.conf
    :

    server 192.168.10.11 {
        keys { solvethenetwork-transfer-key; };
    };

    This server block covers notify messages, AXFR responses, and IXFR responses sent to sw-infrarunbook-02. Any DNS message leaving the primary toward 192.168.10.11 will be signed.

    Now update your zone declaration in

    /etc/bind/named.conf.local
    to require TSIG authentication for all inbound zone transfer requests:

    zone "solvethenetwork.com" {
        type primary;
        file "/etc/bind/zones/db.solvethenetwork.com";
        allow-transfer { key solvethenetwork-transfer-key; };
        also-notify { 192.168.10.11; };
        notify yes;
    };

    The

    allow-transfer { key solvethenetwork-transfer-key; }
    directive tells BIND9 to accept AXFR and IXFR requests only when they carry a valid TSIG signature from a client that holds this exact key. Any unsigned request, or a request signed with a different key, will receive a REFUSED response.

    Reload BIND9 to apply the new configuration:

    systemctl reload named

    Confirm there are no configuration errors:

    named-checkconf
    journalctl -u named --since "1 minute ago"

    Step 3 – Copy the Key File to the Secondary Server

    The secondary must have an identical copy of the key file. Transfer it securely using scp from sw-infrarunbook-01:

    scp /etc/bind/tsig-keys/solvethenetwork-transfer.key \
        infrarunbook-admin@192.168.10.11:/tmp/solvethenetwork-transfer.key

    On sw-infrarunbook-02, move the key into place and set permissions:

    mkdir -p /etc/bind/tsig-keys
    chown root:bind /etc/bind/tsig-keys
    chmod 750 /etc/bind/tsig-keys
    
    mv /tmp/solvethenetwork-transfer.key \
        /etc/bind/tsig-keys/solvethenetwork-transfer.key
    
    chown root:bind /etc/bind/tsig-keys/solvethenetwork-transfer.key
    chmod 640 /etc/bind/tsig-keys/solvethenetwork-transfer.key

    Verify the file contents on the secondary match the primary exactly. Use md5sum to compare:

    # On sw-infrarunbook-01
    md5sum /etc/bind/tsig-keys/solvethenetwork-transfer.key
    
    # On sw-infrarunbook-02
    md5sum /etc/bind/tsig-keys/solvethenetwork-transfer.key

    Both hashes must be identical. A mismatch here will cause BADSIG errors once BIND9 is configured.

    Step 4 – Configure the Secondary BIND9 Server

    On sw-infrarunbook-02, open

    /etc/bind/named.conf
    and add the key include and server block:

    include "/etc/bind/tsig-keys/solvethenetwork-transfer.key";
    
    server 192.168.10.10 {
        keys { solvethenetwork-transfer-key; };
    };

    Define the zone as a secondary in

    /etc/bind/named.conf.local
    . Use the
    primaries
    directive with an explicit key reference:

    zone "solvethenetwork.com" {
        type secondary;
        file "/var/cache/bind/db.solvethenetwork.com";
        primaries { 192.168.10.10 key solvethenetwork-transfer-key; };
        allow-notify { 192.168.10.10; };
    };

    The

    primaries
    directive with the key reference ensures that refresh SOA queries, AXFR requests, and IXFR requests sent from the secondary to the primary are all signed with the correct TSIG key.

    Check the configuration and reload:

    named-checkconf
    systemctl reload named

    BIND9 will initiate a zone transfer immediately after reload. Watch the journal for confirmation:

    journalctl -u named -f

    A successful transfer produces a log entry similar to this:

    named[1234]: transfer of 'solvethenetwork.com/IN' from 192.168.10.10#53:
      Transfer completed: 1 messages, 22 records, 648 bytes, 0.002 secs

    Step 5 – Verify Zone Transfer Behavior with dig

    Testing both authenticated and unauthenticated paths confirms that your TSIG configuration is enforced correctly.

    First, attempt an unauthenticated AXFR — this must be rejected:

    dig @192.168.10.10 solvethenetwork.com AXFR

    Expected result — the transfer is refused and no zone data is returned:

    ; Transfer failed.
    ;; communications error to 192.168.10.10#53: end of file

    Now test an authenticated AXFR using the TSIG key file with the

    -k
    flag:

    dig @192.168.10.10 solvethenetwork.com AXFR \
        -k /etc/bind/tsig-keys/solvethenetwork-transfer.key

    A successful authenticated transfer returns the full zone contents and ends with transfer statistics:

    ;; Query time: 4 msec
    ;; SERVER: 192.168.10.10#53(192.168.10.10) (TCP)
    ;; WHEN: Tue Mar 25 14:22:11 UTC 2026
    ;; XFR size: 22 records (messages 1, bytes 648)

    Common error codes you may encounter during testing:

    • BADSIG – The HMAC signature did not verify. Secrets do not match between the two key files.
    • BADKEY – The key name in the request is not recognized by the server. Verify spelling and case of the key name in all configuration files.
    • BADTIME – Clock skew between client and server exceeds the fudge window. Resynchronize NTP immediately.
    • NOTAUTH – The presented key is valid but not listed in the zone's
      allow-transfer
      directive.

    Step 6 – Covering Multiple Zones with One Key Pair

    If you operate multiple zones on the same primary-secondary pair, a single TSIG key can authenticate all zone transfers between those two servers. Reference the same key in the

    allow-transfer
    directive of each zone on the primary:

    zone "solvethenetwork.com" {
        type primary;
        file "/etc/bind/zones/db.solvethenetwork.com";
        allow-transfer { key solvethenetwork-transfer-key; };
        also-notify { 192.168.10.11; };
    };
    
    zone "10.168.192.in-addr.arpa" {
        type primary;
        file "/etc/bind/zones/db.192.168.10";
        allow-transfer { key solvethenetwork-transfer-key; };
        also-notify { 192.168.10.11; };
    };

    On the secondary, define each zone using the same key in the primaries directive. The single server{} block and key inclusion already handle all zones automatically — no additional per-zone key configuration is needed on the secondary side.

    Step 7 – Rotating TSIG Keys Without Service Disruption

    TSIG keys should be rotated periodically — at minimum annually, and immediately upon any suspected compromise. The rotation must be carefully sequenced to avoid a window where zone transfers fail because one server is using a key the other no longer recognizes.

    Follow this overlap rotation procedure:

    1. Generate a new key on the primary and save it to a new file:
      tsig-keygen -a hmac-sha256 solvethenetwork-transfer-key-v2 \
          > /etc/bind/tsig-keys/solvethenetwork-transfer-v2.key
      chown root:bind /etc/bind/tsig-keys/solvethenetwork-transfer-v2.key
      chmod 640 /etc/bind/tsig-keys/solvethenetwork-transfer-v2.key
    2. Copy the new key file to the secondary using scp.
    3. On the secondary, add an include for the new key file in
      named.conf
      while keeping the old include in place. Reload named.
    4. On the primary, add an include for the new key file, then update the
      server{}
      block and all
      allow-transfer
      directives to reference the new key name. Reload named.
    5. Verify zone transfers succeed using
      dig -k
      with the new key file.
    6. Remove the old key include from both servers and reload to complete the rotation.

    This two-phase approach ensures both servers simultaneously recognize both keys during the transition window, eliminating any risk of transfer failures.

    Step 8 – Using TSIG to Secure Dynamic DNS Updates

    TSIG authentication is equally important for securing RFC 2136 dynamic DNS updates. If any host performs self-registration via

    nsupdate
    , require a TSIG key for those updates rather than relying on IP-based allow-update rules.

    Generate a dedicated DDNS key, separate from the transfer key:

    tsig-keygen -a hmac-sha256 solvethenetwork-ddns-key \
        > /etc/bind/tsig-keys/solvethenetwork-ddns.key
    chown root:bind /etc/bind/tsig-keys/solvethenetwork-ddns.key
    chmod 640 /etc/bind/tsig-keys/solvethenetwork-ddns.key

    Include the key and add an update-policy block to the zone definition on the primary:

    include "/etc/bind/tsig-keys/solvethenetwork-ddns.key";
    
    zone "solvethenetwork.com" {
        type primary;
        file "/etc/bind/zones/db.solvethenetwork.com";
        allow-transfer { key solvethenetwork-transfer-key; };
        update-policy {
            grant solvethenetwork-ddns-key zonesub A AAAA TXT;
        };
    };

    This configuration permits holders of the DDNS key to add or update A, AAAA, and TXT records within the zone, but prevents modification of SOA or NS records. Test a dynamic update from a client host:

    nsupdate -k /etc/bind/tsig-keys/solvethenetwork-ddns.key << EOF
    server 192.168.10.10
    zone solvethenetwork.com
    update add dev-host.solvethenetwork.com 300 A 192.168.10.50
    send
    EOF

    Enabling TSIG Debug Logging in BIND9

    During initial configuration and troubleshooting, enabling detailed TSIG and zone transfer logging in BIND9 saves significant time. Add this logging block to

    /etc/bind/named.conf.options
    :

    logging {
        channel tsig_debug {
            file "/var/log/named/tsig.log" versions 3 size 5m;
            severity debug 3;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        category security { tsig_debug; };
        category xfer-in { tsig_debug; };
        category xfer-out { tsig_debug; };
    };

    Create the log directory and set ownership before reloading:

    mkdir -p /var/log/named
    chown bind:bind /var/log/named
    systemctl reload named

    Tail the log during a test transfer to see detailed TSIG validation steps:

    tail -f /var/log/named/tsig.log

    Once TSIG is confirmed working, dial back the logging severity to

    info
    to avoid filling disk with debug output in production.

    Security Hardening Checklist

    Before considering this configuration production-ready, verify each of the following:

    • All TSIG key files are owned
      root:bind
      with permissions
      640
    • The
      /etc/bind/tsig-keys/
      directory has permissions
      750
    • No zone anywhere uses
      allow-transfer { any; }
    • Algorithm is HMAC-SHA256 or HMAC-SHA512 — not HMAC-MD5 or HMAC-SHA1
    • NTP or chrony is running and synchronized on all DNS servers
    • TSIG key files are not present in any git repository or backup system accessible to non-administrators
    • Key rotation is documented in your change management calendar (minimum annually)
    • NOTIFY messages are sent only to known secondary IPs via
      also-notify
    • Firewall rules restrict port 53 TCP (used for zone transfers) to known DNS peer IPs only
    • TSIG key names and rotation schedule are documented in your internal DNS runbook

    Frequently Asked Questions

    Q: What is the difference between TSIG and DNSSEC?

    A: TSIG and DNSSEC solve different problems. TSIG authenticates DNS transactions between two specific hosts — such as a primary and secondary server — using a shared HMAC secret. It protects the channel of communication. DNSSEC, by contrast, uses asymmetric cryptography to sign zone data itself, allowing any resolver anywhere on the internet to verify that records have not been tampered with in transit. The two mechanisms are complementary and can be used together.

    Q: Can I use TSIG to authenticate recursive queries from end-user clients?

    A: In principle yes, but in practice it is rarely done for standard client-to-resolver communication because it requires distributing a shared secret to every client. TSIG is best suited for server-to-server authentication: zone transfers, dynamic updates, and notify messages. For client authentication on a local network, IP-based allow-recursion or DNS over TLS are more practical approaches.

    Q: How long should the TSIG secret be?

    A: The tsig-keygen tool generates a 256-bit (32-byte) secret for HMAC-SHA256 by default, which is the correct output size for that algorithm. You should not manually specify a shorter secret. For HMAC-SHA512, the tool generates a 512-bit secret. Always use tsig-keygen rather than constructing keys by hand.

    Q: What happens if the NTP clocks drift out of sync?

    A: If the clock skew between the two DNS servers exceeds the TSIG fudge value — 300 seconds by default — every TSIG-signed message will be rejected with a BADTIME error code. Zone transfers will stop working until clocks are resynchronized. Monitor NTP drift with

    chronyc tracking
    and alert on skew exceeding 60 seconds to give yourself a buffer before TSIG failures occur.

    Q: Do I need a separate TSIG key for each zone on a primary-secondary pair?

    A: No. A single TSIG key authenticates the relationship between two specific servers, not individual zones. One key can cover all zones transferred between sw-infrarunbook-01 and sw-infrarunbook-02. Use separate keys only when you have multiple secondary servers or different trust levels for different zone groups.

    Q: Can TSIG be used with BIND9 views for split-horizon DNS?

    A: Yes. When using BIND9 views, define the TSIG key at the global level with an include directive, then reference it in allow-transfer within the appropriate view's zone definitions. The server{} block mapping a peer IP to a key also operates at the global level. Be careful to place zone transfer restrictions inside the correct view to avoid accidentally exposing internal view data to external secondaries.

    Q: How do I force an immediate zone retransfer on the secondary after updating the primary?

    A: Use rndc retransfer on the secondary server:

    rndc retransfer solvethenetwork.com
    . This forces an immediate AXFR request regardless of the SOA refresh timer. Alternatively, incrementing the SOA serial number on the primary and running
    rndc reload solvethenetwork.com
    on the primary will trigger a NOTIFY to the secondary, which will then initiate a refresh automatically.

    Q: Can I use allow-transfer with both a key and an IP address?

    A: Yes, and combining both is a good defense-in-depth practice. The syntax

    allow-transfer { key solvethenetwork-transfer-key; 192.168.10.11; };
    means BIND9 will accept transfers from either a valid TSIG-signed request or an unsigned request from that specific IP. For maximum security, use only the key and rely on firewall rules to restrict the IP range rather than granting IP-only access in BIND9 itself.

    Q: What BIND9 log category shows TSIG verification failures?

    A: TSIG-related authentication failures are logged under the

    security
    category. Zone transfer activity appears under
    xfer-in
    (on the secondary) and
    xfer-out
    (on the primary). Setting severity to
    debug 3
    on those channels during troubleshooting will show detailed per-message TSIG validation results including the error code returned.

    Q: Is TSIG the same as TKEY?

    A: No. TKEY (RFC 2930) is a dynamic key establishment mechanism that allows two parties to negotiate a shared TSIG key without out-of-band key distribution. It is more complex to configure and is typically used in large-scale GSS-TSIG deployments — for example, when integrating BIND9 with Active Directory for dynamic DNS updates. For most Linux-only BIND9 deployments, manually distributed TSIG keys via tsig-keygen are simpler and equally secure.

    Q: How do I verify which TSIG keys BIND9 currently has loaded?

    A: Run

    rndc status
    to get general server information, or query the BIND9 statistics channel if configured. A more direct approach is
    named-checkconf -p 2>/dev/null | grep key
    , which pretty-prints the loaded configuration and shows all key{} blocks currently parsed. After any key file change, always run
    named-checkconf
    before reloading to catch syntax errors before they cause a service disruption.

    Q: Can TSIG protect against DNS cache poisoning attacks?

    A: TSIG protects server-to-server authentication for zone transfers and dynamic updates, but it does not protect recursive resolver responses from cache poisoning attacks like the Kaminsky vulnerability. DNSSEC is the correct mechanism for cache poisoning defense, as it cryptographically validates zone data regardless of how it reaches the resolver. TSIG and DNSSEC address separate attack surfaces and should both be deployed in a hardened DNS infrastructure.

    Frequently Asked Questions

    What is the difference between TSIG and DNSSEC?

    TSIG authenticates DNS transactions between two specific hosts using a shared HMAC secret, protecting the communication channel. DNSSEC uses asymmetric cryptography to sign zone data itself, letting any resolver verify records were not tampered with. They are complementary and can be deployed together.

    Can I use TSIG to authenticate recursive queries from end-user clients?

    In principle yes, but it requires distributing a shared secret to every client, which is impractical. TSIG is best suited for server-to-server authentication: zone transfers, dynamic updates, and notify messages. For client authentication, DNS over TLS or IP-based allow-recursion are more practical.

    How long should the TSIG secret be?

    The tsig-keygen tool generates a 256-bit secret for HMAC-SHA256 and a 512-bit secret for HMAC-SHA512 by default, which are the correct output sizes for those algorithms. Always use tsig-keygen rather than constructing keys manually.

    What happens if the NTP clocks drift out of sync?

    If clock skew between the two DNS servers exceeds the TSIG fudge value — 300 seconds by default — every TSIG-signed message will be rejected with a BADTIME error and zone transfers will stop. Monitor NTP drift and alert on skew exceeding 60 seconds to catch the problem before TSIG failures occur.

    Do I need a separate TSIG key for each zone on a primary-secondary pair?

    No. A single TSIG key authenticates the relationship between two specific servers, not individual zones. One key covers all zones transferred between a given primary and secondary. Use separate keys only when dealing with multiple secondaries or different trust levels for different zone groups.

    Can TSIG be used with BIND9 views for split-horizon DNS?

    Yes. Define the TSIG key at the global level with an include directive and reference it in allow-transfer within the appropriate view's zone definitions. The server{} block mapping a peer IP to a key also operates at the global level, covering all views.

    How do I force an immediate zone retransfer on the secondary?

    Run rndc retransfer solvethenetwork.com on the secondary to force an immediate AXFR regardless of the SOA refresh timer. Alternatively, increment the SOA serial on the primary and run rndc reload on the primary, which sends a NOTIFY to the secondary triggering an automatic refresh.

    Can I use allow-transfer with both a key and an IP address?

    Yes. The syntax allow-transfer { key solvethenetwork-transfer-key; 192.168.10.11; }; accepts either a valid TSIG-signed request or an unsigned request from that IP. For maximum security, use only the key requirement and enforce IP restrictions at the firewall level rather than granting IP-only access in BIND9.

    What BIND9 log category shows TSIG verification failures?

    TSIG authentication failures appear under the security log category. Zone transfer activity is logged under xfer-in on the secondary and xfer-out on the primary. Setting severity to debug 3 on those channels during troubleshooting shows detailed per-message TSIG validation results including the specific error code.

    Is TSIG the same as TKEY?

    No. TKEY (RFC 2930) is a dynamic key establishment mechanism for negotiating shared TSIG keys without out-of-band distribution, typically used in GSS-TSIG deployments such as BIND9 integrated with Active Directory. For most Linux-only BIND9 deployments, manually distributed TSIG keys via tsig-keygen are simpler and equally secure.

    Related Articles