feat: add PowerDNS reverse DNS (PTR) integration
Introduces an opt-in reverse DNS management subsystem backed by a PowerDNS
Authoritative HTTP API. Runs via a companion WHMCS addon module
(modules/addons/VirtFusionDns) that holds settings and a Test Connection
page; the server module reads those settings from tbladdonmodules and
short-circuits when the addon is absent or disabled, so provisioning is
unaffected for operators who don't use the feature.
Lifecycle hooks:
- createAccount creates PTRs for every assigned IP (forward DNS must
already resolve to the IP — FCrDNS enforcement)
- renameServer updates only PTRs whose content matched the old hostname,
preserving client-custom records
- terminateAccount deletes all PTRs before the local state is purged
- TestConnection merges PowerDNS health check with the existing VirtFusion
check
- A DailyCronJob hook reconciles missing PTRs additive-only (never
overwrites)
Client UI: new "Reverse DNS" panel on the service overview with one
editable PTR input per assigned IP, per-row status badges, and
forward-DNS rejection on save. Admin services tab gets a parallel
widget with Reconcile (additive) and Reconcile (force reset) buttons.
New subsystem at lib/PowerDns/:
- Client.php PowerDNS API wrapper (X-API-Key, listZones/getZone/
patchRRset/notifyZone), auto-NOTIFY on successful PATCH
- Config.php Loads + decrypts addon settings from tbladdonmodules
- IpUtil.php PTR-name generation (IPv4 + IPv6), zone matching,
RFC 2317 classless parsing
- Resolver.php FCrDNS verification via dns_get_record with CNAME-chain
following and per-(hostname,ip) caching
- PtrManager.php Orchestrator: syncServer, deleteForServer, listPtrs,
setPtr, reconcile, reconcileAll
Security hardening helpers added to Module and applied to the rDNS
endpoints:
- requirePost() HTTP method gate (405 on non-POST mutations)
- requireSameOrigin() Origin/Referer check against WHMCS host (CSRF
defence against cross-site form POST)
- requireServiceStatus() tblhosting.domainstatus filter (Active for
writes, Active+Suspended for reads)
RFC 2317 classless delegations (e.g. 64/64.113.0.203.in-addr.arpa.)
supported with alignment validation: rejects misaligned start addresses
that don't correspond to any real delegation boundary.
PowerDNS zone IDs containing '/' are URL-encoded as '=2F' per the
PowerDNS API convention. PATCH success triggers PUT /zones/{id}/notify
so slaves pick up the SOA-bumped serial immediately.
Includes IPv4 + IPv6 support, per-IP write rate limit (10s), fresh
IP-ownership re-verification on every client write (defends against
stale-ownership after IP reassignment), and audit logging of every
successful edit to the WHMCS module log.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* WHMCS hooks for the VirtFusion module.
|
||||
*
|
||||
* HOW HOOKS WORK IN WHMCS
|
||||
* -----------------------
|
||||
* add_hook('EventName', $priority, $callback) registers $callback to fire on
|
||||
* the named event. WHMCS discovers hook files by walking modules/servers/*
|
||||
* /hooks.php and modules/addons/* /hooks.php on every page load, then invokes
|
||||
* every registered hook for the current event.
|
||||
*
|
||||
* Hooks run IN-REQUEST — there's no queue or background worker. Anything
|
||||
* expensive in a hook (like an external API call) blocks the user's page
|
||||
* load. For that reason we only do:
|
||||
* - Fast in-process work (building DOM snippets, validating session state)
|
||||
* - Scheduled work on DailyCronJob where "in-request" means the cron worker,
|
||||
* not a user session
|
||||
*
|
||||
* HOOKS REGISTERED HERE
|
||||
* ---------------------
|
||||
* DailyCronJob — PowerDNS reconciliation across all services
|
||||
* ShoppingCartValidateCheckout — blocks checkout until OS is selected
|
||||
* ClientAreaFooterOutput — injects the OS/SSH-key gallery on order form
|
||||
*
|
||||
* FAILURE SEMANTICS
|
||||
* -----------------
|
||||
* Every hook wraps its body in try/catch and silently absorbs any exception.
|
||||
* A hook that throws would potentially break the entire WHMCS request for
|
||||
* all users, not just this module — so we log and swallow, preferring
|
||||
* degraded functionality over site-wide breakage.
|
||||
*/
|
||||
|
||||
use WHMCS\Database\Capsule;
|
||||
use WHMCS\Module\Server\VirtFusionDirect\ConfigureService;
|
||||
use WHMCS\Module\Server\VirtFusionDirect\Database;
|
||||
use WHMCS\Module\Server\VirtFusionDirect\Log;
|
||||
use WHMCS\Module\Server\VirtFusionDirect\Module;
|
||||
use WHMCS\Module\Server\VirtFusionDirect\PowerDns\Config as PowerDnsConfig;
|
||||
use WHMCS\Module\Server\VirtFusionDirect\PowerDns\PtrManager;
|
||||
|
||||
if (! defined('WHMCS')) {
|
||||
exit('This file cannot be accessed directly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Daily PowerDNS reconciliation.
|
||||
*
|
||||
* Walks every managed service and creates any missing PTRs (never overwrites existing
|
||||
* values — cron is additive-only). Requires the VirtFusion DNS addon to be activated
|
||||
* and enabled; otherwise short-circuits immediately.
|
||||
*
|
||||
* All error handling lives inside reconcileAll(); this wrapper just logs any escape
|
||||
* without disturbing the rest of the daily cron run.
|
||||
*/
|
||||
add_hook('DailyCronJob', 1, function ($vars) {
|
||||
try {
|
||||
if (PowerDnsConfig::isEnabled()) {
|
||||
(new PtrManager)->reconcileAll();
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
Log::insert('PowerDns:DailyCronJob', [], $e->getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shopping Cart Validation Hook
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user