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 <john@phrozen.org>
This commit is contained in:
John Crispin 2025-11-03 12:55:29 +01:00
parent 2e316ef076
commit b499eceebe

View File

@ -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();