Skip to content

ADR 0016: iPXE enforces X.509 name constraints - #1184

Merged
mastacontrola merged 1 commit into
working-1.6from
adr-ipxe-name-constraints
Aug 18, 2026
Merged

ADR 0016: iPXE enforces X.509 name constraints#1184
mastacontrola merged 1 commit into
working-1.6from
adr-ipxe-name-constraints

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

Records the decision behind FOGProject/fog-ipxe#6.

Why

createSSLCA() issues the web certificate through a FOG Web CA intermediate, and _nameConstraints() gives that intermediate a critical nameConstraints extension. Separately, buildipxe.sh bakes .fogCA.pem into iPXE via CERT=/TRUST= whenever the netboot protocol is HTTPS — which _resolveNetbootProto() selects automatically when rebuildIpxeWithMyCA is set.

Those two features are mutually incompatible. iPXE's x509_extensions[] knows five extensions and x509_parse_extension() refuses any critical one it doesn't recognise, so iPXE cannot parse FOG's own Web CA:

https://<server>/fog/service/ipxe/boot.php... Operation not supported (https://ipxe.org/3c16e283)

Both branches were affected — working-1.6 since 2026-08-10, dev-branch since 2026-08-09.

What it records

  • Fixing iPXE rather than dropping the critical flag, and why the flag is the whole point.
  • Enforcing per path rather than per issuer — a constraining CA binds everything below it, and checking only the immediate issuer is correct for a depth-3 chain and silently wrong deeper, in the accept direction.
  • Refusing unenforceable constraint types when the extension is parsed, not when names are compared, so a constraint on an unused name type can't be skipped silently.
  • The deliberate deviation from a strict RFC 5280 reading for the commonName of an end entity with no SAN, which x509_check_name() would otherwise accept as a host name outside the permitted set.
  • The two rejected alternatives (non-critical constraints; a --no-web-name-constraints flag) and what each costs.

Follow-up

The FOG_IPXE_VERSION pin bump lands once fog-ipxe cuts a release, on both branches.

🤖 Generated with Claude Code

Records why the fix for the HTTPS netboot failure went into iPXE rather
than into FOG's certificate issuance.

FOG's Web CA carries a critical nameConstraints extension. iPXE knows
five extensions and refuses any critical one it does not recognise, so
it cannot parse the CA FOG bakes into it, and HTTPS netboot fails with
"Operation not supported (https://ipxe.org/3c16e283)". The two features
-- constrained issuance and CA-embedded iPXE -- are wired to enable each
other and were mutually incompatible.

The decision is to teach iPXE the extension rather than stop marking it
critical. Criticality exists so that a verifier which cannot enforce a
constraint refuses the certificate instead of ignoring it; removing it
would convert every such verifier from fail-closed to fail-open to
accommodate one that happens to be ours.

Also records the two implementation choices that are easy to get wrong
in a way that fails open -- enforcing per path rather than per issuer,
and refusing unenforceable constraints when the extension is parsed
rather than when names are compared -- and the deliberate deviation from
a strict RFC 5280 reading for the commonName of an end entity with no
subjectAltName, which x509_check_name() would otherwise accept as a host
name outside the permitted set.

Implementation is FOGProject/fog-ipxe#6. The FOG_IPXE_VERSION pin bump
follows once that is released.
@mastacontrola
mastacontrola merged commit d937d45 into working-1.6 Aug 18, 2026
3 checks passed
@mastacontrola
mastacontrola deleted the adr-ipxe-name-constraints branch August 18, 2026 14:12
This was referenced Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant