From b499eceebe82d7ebe24097ca49d4984490ce6c90 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 3 Nov 2025 12:55:29 +0100 Subject: [PATCH] cloud_discovery: add CAA DNS-based EST server discovery Implement EST server discovery via CAA DNS records for air-gapped deployments. When DHCP Option 224 provides a controller FQDN, query CAA records to determine the appropriate EST server endpoint. The discovery flow: 1. Read controller FQDN from /tmp/cloud.json (set by DHCP handler) 2. Query CAA records for the controller domain 3. Use EST server from CAA 'issue' tag if present 4. Fall back to certificate issuer-based selection if CAA lookup fails This allows network administrators to configure local EST servers via DNS rather than relying on hardcoded public endpoints. Air-gapped deployments can now specify private EST servers through standard DNS infrastructure. Example DNS configuration: controller.local. IN CAA 0 issue "est.local:8001" When an AP receives controller.local via DHCP Option 224, it will query CAA records and use est.local:8001 for certificate enrollment instead of the public est.certificates.open-lan.org endpoint. Signed-off-by: John Crispin --- .../cloud_discovery/files/usr/bin/est_client | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/feeds/tip/cloud_discovery/files/usr/bin/est_client b/feeds/tip/cloud_discovery/files/usr/bin/est_client index b22c06340..ff8543368 100755 --- a/feeds/tip/cloud_discovery/files/usr/bin/est_client +++ b/feeds/tip/cloud_discovery/files/usr/bin/est_client @@ -3,6 +3,7 @@ 'use strict'; import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log'; +import { query } from 'resolv'; import * as fs from 'fs'; import * as libuci from 'uci'; @@ -11,7 +12,49 @@ let store_operational_ca = false; let est_server = 'est.certificates.open-lan.org'; let cert_prefix = 'operational'; +function discover_est_server_via_caa() { + let cloud_config = fs.readfile('/tmp/cloud.json'); + if (!cloud_config) + return null; + + let cloud = json(cloud_config); + if (!cloud || !cloud.dhcp_server) + return null; + + let controller_fqdn = cloud.dhcp_server; + let fqdn_parts = split(controller_fqdn, ':'); + if (length(fqdn_parts) > 0) + controller_fqdn = fqdn_parts[0]; + + ulog(LOG_INFO, `Attempting CAA lookup for controller FQDN: ${controller_fqdn}\n`); + + let result = query([controller_fqdn], { type: ['CAA'] }); + + if (!result || !result[controller_fqdn] || !result[controller_fqdn].CAA) + return null; + + let caa_records = result[controller_fqdn].CAA; + + for (let record in caa_records) { + if (record.tag == 'issue') { + let est_server = trim(record.value, '" '); + ulog(LOG_INFO, `Found EST server via CAA: ${est_server}\n`); + return est_server; + } + } + + return null; +} + function set_est_server() { + let discovered_server = discover_est_server_via_caa(); + if (discovered_server) { + est_server = discovered_server; + return; + } + + ulog(LOG_INFO, 'No EST server found via CAA, using certificate issuer-based selection\n'); + let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`); let issuer = pipe.read("all"); pipe.close();