Skip to content

fix(origin): issue leaves as CA:FALSE, and trust them on the box that serves them - #18

Merged
ralyodio merged 1 commit into
mainfrom
fix/origin-leaf-not-a-ca
Aug 9, 2026
Merged

fix(origin): issue leaves as CA:FALSE, and trust them on the box that serves them#18
ralyodio merged 1 commit into
mainfrom
fix/origin-leaf-not-a-ca

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

The bug

openssl req -x509 defaults to basicConstraints=CA:TRUE, and setup-origin.sh never overrode it. Every origin this script has ever created serves a certificate marked as a certificate authority. On the dev box, all three do:

$ openssl x509 -in <served> -noout -ext basicConstraints
X509v3 Basic Constraints: critical
    CA:TRUE

This certificate is meant to be trusted directly — it is its own anchor, which is the point of a pinned self-signed origin. An anchor marked CA:TRUE may issue for any name. The SAN bounds what the certificate speaks for; it does not bound what a key trusted as an authority may go on to sign. Trusting one of these to reach chovy.hacker also hands its holder google.com.

The fix

CA:FALSE + single-name SAN + serverAuth. That is the shape where direct trust is a bounded grant — verified in the tests to still work as its own anchor, which is what a stock client needs.

Repair is free. The key is reused and the pin is over the key, so re-issuing leaves the published pin untouched: no registry update, no flag day, no client holding the old pin breaks.

Also here

  • --all re-issues every name the box already has a key for, so repairing a fleet is one command with nothing to type and mistype.
  • Local trust. The machine serving a Moshpit name is usually one somebody browses it from, and until now curl https://<name> failed to verify on the origin itself. The pinned-TLS proxy cannot fix that case — it works by owning 443 on loopback, and on an origin nginx already has 443; a second bind gets EADDRINUSE. Trusting the leaf needs no port. Guarded: it re-reads the file and refuses anything that is not CA:FALSE.
  • Path safety. The name becomes a path under /etc/ssl, /etc/nginx and /usr/local/share/ca-certificates, as root. It is now checked against a hostname charset first.

Tests

tests/setup-origin.test.ts, 8 new. The extension flags are read out of the script rather than restated, so a test cannot pass while the script mints something else — which is exactly the failure mode being guarded against. Full suite 69/69.

🤖 Generated with Claude Code

… serves them

`openssl req -x509` defaults to basicConstraints=CA:TRUE, and setup-origin.sh
never overrode it. Every origin this script has ever created is therefore
serving a certificate that is marked as a certificate authority.

That matters because this certificate is meant to be trusted *directly* — it is
its own anchor, which is the whole point of a pinned self-signed origin. An
anchor marked CA:TRUE may issue for any name in the world. The SAN bounds what
the certificate speaks for; it does not bound what a key trusted as an authority
may go on to sign. So trusting one of these to reach `chovy.hacker` also hands
its holder google.com.

CA:FALSE with a single-name SAN is the shape that makes direct trust a bounded
grant. Re-running fixes an already-published certificate for free: the key is
reused and the pin is over the key, so the registry needs no update and no
client holding the old pin breaks.

Also here:

- `--all` re-issues every name the box already has a key for, so repairing a
  fleet is one command with nothing to type and mistype.
- the certificate is trusted on this machine once it is issued. The machine
  serving a Moshpit name is usually one somebody browses it from, and until now
  `curl https://<name>` failed to verify there. The pinned-TLS proxy cannot fix
  that case: it works by owning 443 on loopback, and on an origin nginx already
  has 443 — a second bind gets EADDRINUSE. Trusting the leaf needs no port.
- a name is checked against a hostname charset before it becomes a path under
  /etc/ssl, /etc/nginx and /usr/local/share/ca-certificates, as root.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 5b376fc into main Aug 9, 2026
3 checks passed
@ralyodio
ralyodio deleted the fix/origin-leaf-not-a-ca branch August 9, 2026 17:28
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