Project

General

Profile

Actions

Bug #16872

open

dyndns.class: Cloudflare DDNS fails (base64 token corruption, no POST create, _checkStatus UNKNOWN ERROR)

Added by Mukesh Kesharwani about 17 hours ago.

Status:
New
Priority:
Normal
Assignee:
-
Category:
Dynamic DNS
Target version:
-
Start date:
Due date:
% Done:

0%

Estimated time:
Plus Target Version:
Release Notes:
Default
Affected Version:
2.8.1
Affected Architecture:

Description

We hit several issues with the native Cloudflare Dynamic DNS client on pfSense 2.8.1-RELEASE (amd64). Cloudflare API works from the same firewall via curl/PHP with the same Zone ID + API token, but the built-in client fails and the GUI shows Cached IP 0.0.0.0.

Environment
pfSense 2.8.1-RELEASE, PHP 8.3.19
WAN public IP detected correctly: 103.160.130.243
Cloudflare zone keekar.au, Zone ID in username field, scoped API token (cfat_…) with DNS edit permissions
Multiple Cloudflare DDNS clients (fw, nas, vpn, oscal subdomains)
Symptoms
No-IP / Custom (DuckDNS) clients: cached IP correct
All Cloudflare clients: Cached IP 0.0.0.0, status red / UNKNOWN ERROR
System log examples:
phpDynDNS (fw): UNKNOWN ERROR -
phpDynDNS (fw): PAYLOAD:
Earlier (wrong username format):
Invalid format for X-Auth-Key header
Root causes identified in /etc/inc/dyndns.class
Bug 1 — base64_decode() corrupts API token if password not stored as base64
Location: constructor ~line 381:

$this->dnsPass = base64_decode($dnsPass);
GUI-saved passwords are base64-encoded in config.xml (e.g. No-IP). If the password is written as plain text (config restore, automation, write_config() from script), base64_decode() silently mangles a cfat
… API token into binary garbage. Cloudflare rejects auth; client logs UNKNOWN ERROR with empty payload.

Repro (CLI on firewall):

$token = "cfat_<valid_token>";
$broken = base64_decode($token); // NOT valid base64, but PHP returns ~39 bytes garbage
// Authorization: Bearer $broken → Cloudflare failure
Suggested fix: base64_decode($dnsPass, true) and if false, use $dnsPass as-is; or always normalize on write_config().

Bug 2 — No POST/create path; update-only (PUT)
Location: Cloudflare case in _update() ~lines 1178–1240.

Flow:

GET /dns_records?name={FQDN}&type=A to find record ID
Only if ID exists: PUT update
If record does not exist: no POST to create; outer curl_exec() still runs
New Cloudflare zones/records cannot be bootstrapped by pfSense alone.

Suggested fix: if GET returns empty result[], POST new A/AAAA record.

Bug 3 — _checkStatus() assumes PUT response shape; fails on GET/list JSON
Location: ~lines 2757–2770:

$output = json_decode($data);
if ($output->result->content === $this->_dnsIP) { /* success */ }
...
else {
$status = ... "UNKNOWN ERROR" - " . $output->errors0->message;
}
When the outer request returns a list response ("result":[]) or empty body, $output->result->content is invalid, $output->errors0 is undefined → UNKNOWN ERROR - (empty message). Cache is never updated; GUI stays at 0.0.0.0 (initial placeholder from _detectChange()).

Suggested fix: handle list vs object responses; treat HTTP 200 PUT with matching result.content as success; clear error messages when errors array is empty.

Bug 4 — Misleading auth modes / docs
If username contains @, code uses legacy X-Auth-Email + X-Auth-Key. Putting a modern API token in the password field yields: Invalid format for X-Auth-Key header.

Recommended Cloudflare setup (Zone ID username + Bearer token) works only when username is hex Zone ID (no @). GUI/docs should state clearly: do not use email with API tokens; use Zone ID + token, or email + Global API Key only.

Error text still says "use API Key for password field" which is outdated for token-based auth.

Workaround (what we did)
Custom cron script calling Cloudflare API v4 directly + writing /conf/dyndns_wancloudflare'…'.cache files. Disabled native client by removing <enable> from Cloudflare entries so pfSense does not overwrite cache with failed updates.

Related existing issues (different root cause?)
#15557 — UNKNOWN ERROR due to CURLOPT_INTERFACE / bridge member without IP
#12877 — multiple Cloudflare records / timeouts
Our case is not curl error 7; curl to api.cloudflare.com succeeds with the same token when not corrupted by base64_decode().

Happy to provide sanitized logs, config.xml snippets (redacted), and test again on request.

No data to display

Actions

Also available in: Atom PDF