Skip to content

feat(tsig): BIND panels — enable, disable, restore and the installer choice - #98

Merged
0kaba0 merged 4 commits into
developfrom
feat/tsig-bind
Oct 12, 2026
Merged

0kaba0 merged 4 commits into
developfrom
feat/tsig-bind

Conversation

@0kaba0

@0kaba0 0kaba0 commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

Step 2 of 3 for #96: cPanel with BIND and DirectAdmin. Plesk and PowerDNS panels follow separately (see below).

How a customer turns it on. The installer either takes --tsig=[ALG:]KEY:SECRET or asks on a terminal:

How should SecondDNS fetch your zones?
  1) by our IP address in allow-transfer   [default]
  2) with a TSIG key (create one in Settings › DNS first)

The secret is read without echo. --yes without --tsig, or no terminal, keeps the IP path; that path is unchanged. On an existing install: seconddns-tsig enable --tsig=….

seconddns-tsig enable, in this order so no transfer is ever refused:

  1. Key file next to the BIND config (0640, BIND's group), include, and key "…" added to allow-transfer in options next to our IPs. Backup, named-checkconf (rollback on rejection), rndc reload.
  2. [tsig] stored, seconddns-queued restarted (it reads the config once), so new zones are created with the key.
  3. The key set on this server's zones in SecondDNS (masterIp = this server).
  4. Only when every zone has it, our IPs leave allow-transfer. Other entries and their comments stay.

If a zone could not be switched, our IPs stay and the command says how to finish. Zone-level allow-transfer entries naming our IP (cPanel WHM "Additional zone configuration") override options: they are counted and the WHM line with the key is printed.

disable mirrors it: IP back, [tsig] off and worker restarted, zones unsigned, then the key goes. If a zone keeps the key, [tsig] is put back and the key stays on BIND, so a rerun finishes. restore puts the key file, include and key entry back when the panel rewrote its configuration; it is silent when nothing is missing. setup is the installer step above.

Edits happen only while named is the running DNS server: a named.conf left on a PowerDNS server is not touched.

Existing code, every touched line:

  • cpanel, directadmin install.sh: one --tsig=* case, one help line, and the seconddns-tsig setup call before the initial sync (failure only prints a hint).
  • All four install.sh: one line installing seconddns_bind.py next to the PR-1 line.
  • All four uninstall.sh: seconddns-tsig disable --zones before the first removal step, while the config and the tool still exist; seconddns_bind.py added to the PR-1 rm line.
  • The configure_named_axfr IP step is untouched.

Tests:

  • test_tsig_bind.sh (44 checks, mock API, stand-in checkconf, reload and worker restart):
    • edits on the real cPanel named.conf fixture: none, multi-line list with a trailing comment, entries inside comments left alone, idempotent;
    • full flows: enable without and with zones, a failed zone PATCH, rejected checkconf, restore and its no-op, disable refused and completed, a failed disable rerun, worker restarts, the zone-level warning, setup with --tsig, with --yes and without BIND.
  • test_tsig_bind_named.sh (9 checks, a real named): from our IP, AXFR without the key is refused, with the key it transfers, with a wrong secret it is refused; the customer's other secondary still transfers by IP; restore; after disable our IP transfers again. Run on Ubuntu 24.04 with BIND 9.18.39: 9/9. CI installs bind9 and fails if the test reports itself skipped.
  • test_tsig.sh 35, test_queue.sh 104, test_named_conf_edit.sh 34, test_initial_sync.sh 34: unchanged and green.

Not verified in a live panel (owner decision, no panel servers): the installer prompt inside a real cPanel/DirectAdmin install, and whether either panel rewrites named.conf (that case is what restore is for).

Found while doing this, not changed here: Plesk includes allow-transfer { common-allow-transfer; }; in every zone, so options does not apply to Plesk zones. TSIG on Plesk needs the key in that ACL, kept by restore. Plesk stays on the IP path until that PR. The same finding may affect the existing IP step on Plesk; to be checked separately.

…choice

seconddns-tsig enable puts the key on BIND (key file 0640, include,
key in allow-transfer of the options block), restarts the queue worker,
sets the key on this server's zones and only then drops our IPs from
allow-transfer; other entries and comments stay. disable mirrors it;
restore puts the key back after the panel rewrote its configuration.
Every edit is backed up, checked by named-checkconf and rolled back on
rejection. Edits happen only while named is the running DNS server.

cPanel and DirectAdmin installers take --tsig=[ALG:]KEY:SECRET or ask on
a terminal (IP by default); uninstallers turn TSIG off first. All
panels install seconddns_bind.py next to seconddns-tsig.
The cPanel fixture sets directory "/var/named" and includes
named.rfc1912.zones, so named-checkconf rejected even the untouched file.
CI had no BIND, so the check never ran until the TSIG test brought bind9
to the runner. With a root holding those paths it accepts the installers'
edits and still rejects a broken one (talso-notify).
named-checkconf -t needs root and the CI runner is not; rewriting the
/var/named prefix in the checked copy gives the same paths without it.
@0kaba0

0kaba0 commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

CHANGES REQUESTED at 449dd46, three points.

Verified: the enable order (key accepted next to our IPs → [tsig] stored and the worker restarted → this server's zones switched in SecondDNS → only then our IPs leave allow-transfer) never refuses a transfer in between; edit_allow_transfer works on a comment/string mask, keeps other entries and comments, drops the none placeholder, handles one-per-line and inline lists; backup + named-checkconf with rollback on rejection; restore is idempotent; setup keeps the IP path without a terminal or --tsig; edits only while named is running; installers and uninstallers touched exactly as described; the real-named test proves key-only for our IP, IP for the customer's other secondary, restore and disable; CI fails if that test skips itself; the two fixes to test_named_conf_edit.sh make a check that never ran actually run. No attribution lines.

  1. disable puts back ips[0] (the first of dnsIps, IPv4) while enable dropped every one of our IPs that was there. The installer's IP step adds one SECONDARY_IP chosen by the customer's IP preference, so on a v6-preference host enable removes our IPv6 entry and disable restores our IPv4 one: our transfers from the IPv6 source are refused after disable. Record the entries enable actually removed (e.g. removed_ips in [tsig]) and have disable put those back; a row with our IPv6 entry in the fixture must come back unchanged.

  2. edit_named: when named-checkconf passes but the reload fails, the function only prints a warning and returns True, and enable goes on to drop our IPs. named-checkconf does not read the key file as named does, so an unreadable key file (point 3) passes the check, the reload fails, our IPs are removed from the file, and the next named restart cannot load its configuration: the customer's DNS is down. A failed reload must restore the backup and exit before any further step; row: a reload stub that fails leaves the options file as it was and enable exits non-zero with our IPs still present.

  3. write_key_file gives the key file the group of the options file with mode 0640. On hosts where that file is root:root (/etc/named.conf 0644 root:root is common on DirectAdmin/CentOS), named runs as named/bind and cannot read a 0640 root:root key file. Take the group from the running named process (its gid from /proc/<pid>/status or ps -o group) and refuse enable when it cannot be determined; with point 2 fixed this becomes a clean failure instead of an outage, but it should work on those hosts in the first place.

Plesk and the PowerDNS panels: as described, separate PRs.

…key group from named

- enable records the allow-transfer entries it removed (removed_entries)
  and disable puts exactly those back, so an IPv6 entry is not replaced by
  the first IPv4 address.
- A reload that fails after named-checkconf passed restores the backup,
  reloads again and stops: named-checkconf does not read the key file the
  way named does, and the next named restart would not load the config.
- The key file group is the group of the running named process, not of
  the options file; enable refuses when it cannot be determined.
@0kaba0

0kaba0 commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

APPROVED at b7d5c56.

All three points closed with rows: (1) enable records the allow-transfer entries it actually removed in [tsig] removed_entries and disable puts back exactly those (row 13: the IPv6 entry comes back, not the first IPv4; an install that never dropped anything falls back to our first IP); (2) a reload that fails after a passing named-checkconf restores the backup, reloads once more and exits with the reason, before any zone is switched or [tsig] stored (row 14: config byte-identical, no PATCH, nothing stored); (3) the key file's group comes from the running named process's Gid, and enable refuses without writing the file when it cannot be read (row 15). test_tsig_bind.sh 56, the real-named test and the rest green in CI; no attribution lines.

Merge into develop. Plesk (key in the common-allow-transfer ACL plus restore) and the PowerDNS panels follow as separate PRs.

@0kaba0
0kaba0 merged commit 722e78d into develop Oct 12, 2026
1 check passed
@0kaba0
0kaba0 deleted the feat/tsig-bind branch October 12, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant