diff --git a/INSTALL.md b/INSTALL.md index 8d62bc7..6b85632 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -9,7 +9,7 @@ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' -o caddy-stabl gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg caddy-stable.gpg.key curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list apt update && apt upgrade -apt install -y bzip2 caddy composer curl gettext git gnupg2 net-tools php8.2 php8.2-bcmath php8.2-cli php8.2-common php8.2-curl php8.2-fpm php8.2-gd php8.2-gnupg php8.2-intl php8.2-mbstring php8.2-opcache php8.2-readline php8.2-swoole php8.2-xml unzip wget whois +apt install -y bzip2 caddy composer curl gettext git gnupg2 net-tools php8.2 php8.2-bcmath php8.2-cli php8.2-common php8.2-curl php8.2-fpm php8.2-gd php8.2-gmp php8.2-gnupg php8.2-intl php8.2-mbstring php8.2-opcache php8.2-readline php8.2-swoole php8.2-xml unzip wget whois ``` ## 2. Database installation (please choose one): diff --git a/automation/README.md b/automation/README.md index fa76d7d..51238b9 100644 --- a/automation/README.md +++ b/automation/README.md @@ -1 +1 @@ -composer require phpseclib/phpseclib +composer require badcow/dns phpseclib/phpseclib \ No newline at end of file diff --git a/automation/config.php b/automation/config.php index 13959cb..7b45474 100644 --- a/automation/config.php +++ b/automation/config.php @@ -27,4 +27,7 @@ return [ 'reporting_upload' => false, 'reporting_username' => 'your_username', 'reporting_password' => 'your_password', + + // Zone Writer Configuration + 'dns_server' => 'bind', ]; \ No newline at end of file diff --git a/automation/write-zone.php b/automation/write-zone.php new file mode 100644 index 0000000..df1e1e3 --- /dev/null +++ b/automation/write-zone.php @@ -0,0 +1,183 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + die("Connection failed: " . $e->getMessage()); +} + +$timestamp = time(); +$ns1 = 'ns1.namingo.org'; +$ns2 = 'ns2.namingo.org'; + +$sth = $dbh->prepare('SELECT id, tld FROM domain_tld'); +$sth->execute(); + +while (list($id, $tld) = $sth->fetch(PDO::FETCH_NUM)) { + $tldRE = preg_quote($tld, '/'); + $cleanedTld = ltrim(strtolower($tld), '.'); + $zone = new Zone($cleanedTld . '.'); + $zone->setDefaultTtl(3600); + + $soa = new ResourceRecord; + $soa->setName('@'); + $soa->setClass(Classes::INTERNET); + $soa->setRdata(Factory::Soa( + $ns1 . '.', + 'postmaster.' . $cleanedTld . '.', + $timestamp, + 3600, + 14400, + 604800, + 3600 + )); + $zone->addResourceRecord($soa); + + $nsRecord1 = new ResourceRecord; + $nsRecord1->setName('@'); + $nsRecord1->setClass(Classes::INTERNET); + $nsRecord1->setRdata(Factory::Ns($ns1 . '.')); + $zone->addResourceRecord($nsRecord1); + + $nsRecord2 = new ResourceRecord; + $nsRecord2->setName('@'); + $nsRecord2->setClass(Classes::INTERNET); + $nsRecord2->setRdata(Factory::Ns($ns2 . '.')); + $zone->addResourceRecord($nsRecord2); + + $sth2 = $dbh->prepare('SELECT DISTINCT domain.id, domain.name, domain.rgpstatus, host.name + FROM domain + INNER JOIN domain_host_map ON domain.id = domain_host_map.domain_id + INNER JOIN host ON domain_host_map.host_id = host.id + LEFT JOIN host_addr ON host_addr.host_id = host.id + WHERE domain.tldid = :id + AND ( + host.domain_id IS NULL + OR host_addr.addr IS NOT NULL + ) + AND ( + domain.exdate > CURRENT_TIMESTAMP + OR rgpstatus = \'pendingRestore\' + ) + ORDER BY domain.name'); + $sth2->execute([':id' => $id]); + + while (list($did, $dname, $rgp, $hname) = $sth2->fetch(PDO::FETCH_NUM)) { + $sthStatus = $dbh->prepare("SELECT id FROM domain_status WHERE domain_id = :did AND status LIKE '%Hold' LIMIT 1"); + $sthStatus->bindParam(':did', $did, PDO::PARAM_INT); + $sthStatus->execute(); + $status_id = $sthStatus->fetchColumn(); + + if ($status_id) continue; + + $dname = trim($dname, "$tldRE."); + $dname = ($dname == "$tld.") ? '@' : $dname; + + $nsRecord = new ResourceRecord; + $nsRecord->setName($dname); + $nsRecord->setClass(Classes::INTERNET); + $nsRecord->setRdata(Factory::Ns($hname . '.')); + $zone->addResourceRecord($nsRecord); + } + + $sth2 = $dbh->prepare("SELECT host.name, host.domain_id, host_addr.ip, host_addr.addr + FROM domain + INNER JOIN host ON domain.id = host.domain_id + INNER JOIN host_addr ON host.id = host_addr.host_id + WHERE domain.tldid = :id + AND (domain.exdate > CURRENT_TIMESTAMP OR rgpstatus = 'pendingRestore') + ORDER BY host.name"); + $sth2->execute([':id' => $id]); + + while (list($hname, $did, $type, $addr) = $sth2->fetch(PDO::FETCH_NUM)) { + $sthStatus = $dbh->prepare("SELECT id FROM domain_status WHERE domain_id = :did AND status LIKE '%Hold' LIMIT 1"); + $sthStatus->bindParam(':did', $did, PDO::PARAM_INT); + $sthStatus->execute(); + $status_id = $sthStatus->fetchColumn(); + + if ($status_id) continue; + + $hname = trim($hname, "$tldRE."); + $hname = ($hname == "$tld.") ? '@' : $hname; + + $record = new ResourceRecord; + $record->setName($hname); + $record->setClass(Classes::INTERNET); + + if ($type == 'v4') { + $record->setRdata(Factory::A($addr)); + } else { + $record->setRdata(Factory::AAAA($addr)); + } + + $zone->addResourceRecord($record); + } + + $builder = new AlignedBuilder(); + $completed_zone = $builder->build($zone); + + if ($c['dns_server'] == 'bind') { + $basePath = '/etc/bind/zones'; + } elseif ($c['dns_server'] == 'nsd') { + $basePath = '/etc/nsd'; + } elseif ($c['dns_server'] == 'knot') { + $basePath = '/etc/knot'; + } else { + // Default path + $basePath = '/etc/bind/zones'; + } + + file_put_contents("{$basePath}/{$cleanedTld}.zone", $completed_zone); +} + +if ($c['dns_server'] == 'bind') { + exec("rndc reload .{$cleanedTld}", $output, $return_var); + if ($return_var != 0) { + print "Failed to reload BIND. $return_var \n"; + } + + exec("rndc notify .{$cleanedTld}", $output, $return_var); + if ($return_var != 0) { + print "Failed to notify secondary servers. $return_var \n"; + } +} elseif ($c['dns_server'] == 'nsd') { + exec("nsd-control reload", $output, $return_var); + if ($return_var != 0) { + print "Failed to reload NSD. $return_var \n"; + } +} elseif ($c['dns_server'] == 'knot') { + exec("knotc reload", $output, $return_var); + if ($return_var != 0) { + print "Failed to reload Knot DNS. $return_var \n"; + } + + exec("knotc zone-notify .{$cleanedTld}", $output, $return_var); + if ($return_var != 0) { + print "Failed to notify secondary servers. $return_var \n"; + } +} else { + // Default + exec("rndc reload .{$cleanedTld}", $output, $return_var); + if ($return_var != 0) { + print "Failed to reload BIND. $return_var \n"; + } + + exec("rndc notify .{$cleanedTld}", $output, $return_var); + if ($return_var != 0) { + print "Failed to notify secondary servers. $return_var \n"; + } +} \ No newline at end of file diff --git a/automation/zonegen.php b/automation/zonegen.php deleted file mode 100644 index bd25500..0000000 --- a/automation/zonegen.php +++ /dev/null @@ -1,79 +0,0 @@ -setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -} catch (PDOException $e) { - die("Connection failed: " . $e->getMessage()); -} - -$timestamp = time(); -$ns1 = 'ns1.namingo.org'; -$ns2 = 'ns2.namingo.org'; - -$sth = $dbh->prepare("SELECT `id`,`tld` FROM `domain_tld`"); -$sth->execute(); - -while (list($id, $tld) = $sth->fetch(PDO::FETCH_NUM)) { - $tldRE = preg_quote($tld, '/'); - $outFile = fopen("/var/named/named{$tld}.zone", 'w') or print "Unable to open file '/var/named/named{$tld}.zone'.\n"; - fwrite($outFile, "\$TTL\t1H\n@\tIN\tSOA\t{$ns1}.\tpostmaster{$tld}. (\n\t$timestamp\n\t3H\n\t1H\n\t1W\n\t1D\n\t)\n\n"); - fwrite($outFile, "@\t1H\tIN\tNS\t{$ns1}.\n"); - fwrite($outFile, "@\t1H\tIN\tNS\t{$ns2}.\n"); - - // Select all the hosts - $sth2 = $dbh->prepare("SELECT DISTINCT `domain`.`id`, `domain`.`name`, `domain`.`exdate`, `rgpstatus`, `domain`.`name` - FROM `domain` - WHERE `domain`.`tldid` = :id - AND ( - `domain`.`exdate` > NOW() - OR `rgpstatus` = 'pendingRestore' - ) - ORDER BY `domain`.`name`"); - $sth2->execute([':id' => $id]); - - while (list($did, $dname, $hname) = $sth2->fetch(PDO::FETCH_NUM)) { - $status_id = $dbh->query("SELECT `id` FROM `domain_status` WHERE `domain_id` = '$did' AND `status` LIKE '%Hold' LIMIT 1")->fetchColumn(); - if ($status_id) continue; - $dname = trim($dname, "$tldRE."); - $dname = ($dname == "$tld.") ? '@' : $dname; - $hname = trim($hname, "$tldRE."); - fwrite($outFile, "$dname\tIN\tNS\t$hname\n"); - } - - // Select the A and AAAA records - $sth2 = $dbh->prepare("SELECT `host`.`name`,`host`.`domain_id`,`host_addr`.`ip`,`host_addr`.`addr` - FROM `domain`,`host`,`host_addr` - WHERE `domain`.`tldid` = :id - AND `domain`.`id` = `host`.`domain_id` - AND `host`.`id` = `host_addr`.`host_id` - AND (`domain`.`exdate` > NOW() OR `rgpstatus` = 'pendingRestore') - ORDER BY `host`.`name`"); - $sth2->execute([':id' => $id]); - - while (list($hname, $did, $type, $addr) = $sth2->fetch(PDO::FETCH_NUM)) { - $status_id = $dbh->query("SELECT `id` FROM `domain_status` WHERE `domain_id` = '$did' AND `status` LIKE '%Hold' LIMIT 1")->fetchColumn(); - if ($status_id) continue; - $hname = trim($hname, "$tldRE."); - $hname = ($hname == "$tld.") ? '@' : $hname; - if ($type == 'v4') { - fwrite($outFile, "$hname\tIN\tA\t$addr\n"); - } else { - fwrite($outFile, "$hname\tIN\tAAAA\t$addr\n"); - } - } - - fwrite($outFile, "\n; EOF\n"); - fclose($outFile); -} - -exec("systemctl reload named.service", $output, $return_var); -if ($return_var != 0) { - print "Failed to reload named. $return_var \n"; -} \ No newline at end of file