Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login
    Introducing Netgate Nexus: Multi-Instance Management at Your Fingertips.

    Cloudflare DDNS: multiple bugs in dyndns.class (2.8.1) — UNKNOWN ERROR, 0.0.0.0 cache, no record create

    Scheduled Pinned Locked Moved pfSense Packages
    3 Posts 3 Posters 1.0k Views 4 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • K Offline
      keekar
      last edited by

      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->errors[0]->message;
      }
      When the outer request returns a list response ("result":[]) or empty body, $output->result->content is invalid, $output->errors[0] 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.

      T GertjanG 2 Replies Last reply Reply Quote 0
      • T Offline
        trangkimnha @keekar
        last edited by

        We ran into a few issues with the native Cloudflare DDNS client on pfSense 2.8.1. The same Zone ID and API token work fine when calling the Cloudflare API directly with curl/PHP, but the built-in client consistently reports Cached IP 0.0.0.0 and UNKNOWN ERROR. After tracing the code, we found what appear to be several problems: API tokens can be corrupted by unconditional base64_decode(), Cloudflare updates only existing records (no create path), status checking assumes a specific response format, and the authentication guidance is confusing for API tokens versus Global API Keys.

        1 Reply Last reply Reply Quote 0
        • GertjanG Offline
          Gertjan @keekar
          last edited by

          @keekar said in Cloudflare DDNS: multiple bugs in dyndns.class (2.8.1) — UNKNOWN ERROR, 0.0.0.0 cache, no record create:

          Bug 1 — base64_decode() corrupts API token if password not stored as base64

          There where you set the password, Services > Dynamic DNS > Dynamic DNS Clients > Edit
          the password is stored in the config using "base64_encode()".
          I tens to say this password, as most, if not any, are stored encoded with base64_encode().

          Why do you think this password is stored 'in clear' in the config ? Is it ?

          https://github.com/pfsense/pfsense/blob/9363ac5b8651a1c7a333180425ce7719070f95f9/src/usr/local/www/services_dyndns_edit.php#L215

          No "help me" PM's please. Use the forum, the community will thank you.

          1 Reply Last reply Reply Quote 0
          • First post
            Last post
          Copyright 2026 Rubicon Communications LLC (Netgate). All rights reserved.
          Privacy Policy · Cookie Policy