If you run your own authoritative DNS with BIND, it’s easy to set it up once and forget it. Here’s the checklist I work through: audit first without changing anything, then harden one step at a time.
Ground rules
- Back up the config and zone files before every step, and write the rollback down before you start.
- After every change run
named-checkconfandnamed-checkzone, thendigyour SOA, MX and TXT against each of your servers and a public resolver. If you sign with DNSSEC, rundelvtoo and make sure it still validates. - One change at a time.
Step 1: a read-only audit
Run these from a machine that is not in any of your allow lists, over IPv4 and IPv6:
dig @ns1.example.com example.net A +recurse # should be REFUSED (no open resolver)
dig @ns1.example.com example.com AXFR # should fail (no public zone transfers)
dig @ns1.example.com version.bind CH TXT # should not reveal your version
Also read your allow-recursion and allow-transfer lists and look for addresses that don’t belong there anymore.
Step 2: the safe wins (named.conf.options)
options {
recursion no; // authoritative only
version none;
hostname none;
server-id none;
minimal-responses yes;
rate-limit { // Response Rate Limiting
responses-per-second 15;
window 5;
slip 2;
exempt-clients { 127.0.0.1; ::1; <your-other-nameserver>; };
};
};
RRL keeps your server from being used to amplify attacks against somebody else.
Step 3: sign zone transfers with TSIG
IP-only transfer ACLs work, but TSIG is better. Generate a key on the primary and copy it to the secondary over SSH, never through git:
tsig-keygen -a hmac-sha256 xfr-key > /etc/bind/xfr.key # then chmod 640, owned by the bind group
# primary
include "/etc/bind/xfr.key";
server <secondary-ip> { keys { xfr-key; }; };
allow-transfer { key xfr-key; };
# secondary
include "/etc/bind/xfr.key";
server <primary-ip> { keys { xfr-key; }; };
primaries { <primary-ip> key xfr-key; }; // inside the zone
allow-notify { key xfr-key; };
Do the secondary first, then the primary. TSIG is time-sensitive, so check NTP on both. Test with rndc retransfer and a serial bump.
Step 4: the small stuff that adds up
- rndc: make sure there’s a
controlsblock bound to 127.0.0.1 with a key, and move old hmac-md5 keys to hmac-sha256 withrndc-confgen -a -A hmac-sha256. Keep port 953 closed to the outside. - CAA records so only your certificate authority can issue for you:
0 issue "letsencrypt.org",0 issuewild ";"and aniodefmailto. - SOA: the RNAME should be a real mailbox like
hostmaster.example.com., retry should be a sensible fraction of refresh, and bump the serial on every edit. For dynamic zones,rndc freezebefore editing andrndc thawafter. - Stale DS records: compare
dig DSat the parent with your zone’sDNSKEY. A DS with no matching key is leftover from an old rollover. Confirm no rollover is in progress (rndc dnssec -status), then remove only the stale one at your registrar. Never remove the active DS, or validating resolvers will stop resolving your domain. - Firewall: 53 TCP and UDP open to everyone is correct for an authoritative server. Everything else, closed.
Step 5: keep it as code
Put the configs in an Ansible role with templates, validate with named-checkconf before anything is written, and always run ansible-playbook bind.yml --check --diff before the real run. Generate keys on the servers themselves and keep secrets out of the repo.
Have a blessed day! And Happy Hacking!