diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 64c8eed..7f1b219 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,6 +56,9 @@ jobs: - name: test_cli.sh run: bash hosting-panels/common/tests/test_cli.sh + - name: test_hostname_filter.sh + run: bash hosting-panels/common/tests/test_hostname_filter.sh + - name: test_cyberpanel_db.sh run: bash hosting-panels/common/tests/test_cyberpanel_db.sh diff --git a/hosting-panels/common/seconddns_common.py b/hosting-panels/common/seconddns_common.py index 3974751..868c3c9 100644 --- a/hosting-panels/common/seconddns_common.py +++ b/hosting-panels/common/seconddns_common.py @@ -5,6 +5,7 @@ import configparser import json import os +import socket import subprocess import sys import urllib.error @@ -163,11 +164,28 @@ def panel_zones(): zones = [l.strip() for l in (out or "").splitlines() if l.strip()] if out is None: raise PanelError(f"could not read the zone list ({source}); refusing to continue") + # The server's own hostname zone belongs to the host, not the customer; the + # API refuses it, so every cPanel install left a failed op behind. + host = _server_hostname() + if host: + zones = [z for z in zones if z.lower().rstrip(".") != host] # A panel with no zones is a legal state — a fresh server, or one that was # emptied. Only a failed read is an error, and that is the branch above. return zones +def _server_hostname(): + """This machine's fully qualified name, lowercased, or '' if unknown.""" + name = os.environ.get("SECONDDNS_HOSTNAME") # tests + if name is None: + try: + name = socket.getfqdn() + except OSError: + return "" + name = (name or "").strip().lower().rstrip(".") + return name if "." in name else "" + + def zones_from_file(path): zones = [l.strip() for l in open(path) if l.strip() and not l.startswith("#")] if not zones: diff --git a/hosting-panels/common/tests/test_hostname_filter.sh b/hosting-panels/common/tests/test_hostname_filter.sh new file mode 100755 index 0000000..7668da5 --- /dev/null +++ b/hosting-panels/common/tests/test_hostname_filter.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# The server's own hostname is not a customer zone: cPanel lists it, the API +# refuses it, and every install left a failed op behind. +# Run: bash hosting-panels/common/tests/test_hostname_filter.sh +set -u +HERE="$(cd "$(dirname "$0")" && pwd)" +PASS=0; FAIL=0 +ok() { PASS=$((PASS+1)); echo " ok: $1"; } +fail() { FAIL=$((FAIL+1)); echo " FAIL: $1"; } + +zones() { # $1 = hostname, $2 = panel zone list + env SECONDDNS_HOSTNAME="$1" SECONDDNS_PANEL_ZONES_CMD="printf '%s\n' $2" \ + PYTHONPATH="$HERE/.." python3 -c " +from seconddns_common import panel_zones +print(' '.join(panel_zones())) +" 2>&1 +} + +echo "== the hostname zone is dropped" +out=$(zones "server1.seconddns.com" "a.example.com server1.seconddns.com b.example.com") +[ "$out" = "a.example.com b.example.com" ] && ok "hostname removed, order kept" || fail "got '$out'" + +echo "== matching is case- and dot-insensitive" +out=$(zones "server1.seconddns.com" "SERVER1.SecondDNS.com a.example.com") +[ "$out" = "a.example.com" ] && ok "uppercase form removed" || fail "got '$out'" +out=$(zones "server1.seconddns.com" "server1.seconddns.com. a.example.com") +[ "$out" = "a.example.com" ] && ok "trailing dot removed" || fail "got '$out'" + +echo "== only the exact name goes" +out=$(zones "server1.seconddns.com" "seconddns.com sub.server1.seconddns.com a.example.com") +[ "$out" = "seconddns.com sub.server1.seconddns.com a.example.com" ] \ + && ok "parent and child zones kept" || fail "got '$out'" + +echo "== a bare hostname is not a zone name" +out=$(zones "localhost" "localhost a.example.com") +[ "$out" = "localhost a.example.com" ] && ok "no dot, nothing dropped" || fail "got '$out'" + +echo "== an empty panel stays empty, not an error" +out=$(zones "server1.seconddns.com" "") +[ -z "$out" ] && ok "empty list survives the filter" || fail "got '$out'" + +echo +echo "passed=$PASS failed=$FAIL" +[ "$FAIL" -eq 0 ] diff --git a/hosting-panels/cpanel/install.sh b/hosting-panels/cpanel/install.sh index a01c8ac..0d5ca92 100755 --- a/hosting-panels/cpanel/install.sh +++ b/hosting-panels/cpanel/install.sh @@ -503,39 +503,6 @@ if [ -n "$API_NS" ]; then fi fi -# Initial sync -echo "" -if confirm "Sync existing cPanel accounts to secondary DNS now?"; then - echo "[*] Syncing accounts..." - added=0 - failed=0 - - if [ -d /var/cpanel/users ]; then - for user_file in /var/cpanel/users/*; do - [ -f "$user_file" ] || continue - sdomain=$(grep "^DNS=" "$user_file" 2>/dev/null | cut -d= -f2) - [ -z "$sdomain" ] && continue - - response=$(curl -sf --max-time 15 \ - -X POST \ - -H "X-API-Key: $API_KEY" \ - -H "Content-Type: application/json" \ - -H "User-Agent: SecondDNS-cPanel/1.0" \ - -d "{\"name\":\"$sdomain\",\"masterIp\":\"$MASTER_IP\"}" \ - "$API_URL/api/zones" 2>/dev/null) - if [ $? -eq 0 ]; then - echo " [+] $sdomain" - added=$((added+1)) - else - failed=$((failed+1)) - fi - done - else - echo "[!] /var/cpanel/users not found — skipping sync" - fi - - echo "[+] Synced: $added domains, failed: $failed" -fi echo "" # reconcile already knows where each panel keeps its zone list; --add-missing