Expand description
A certificate authority that can only ever vouch for one suffix.
The https mode needs a certificate the browser accepts for <alias>.<suffix>, and nobody
will issue one: the suffix is not a real TLD and there is no way to prove control of it. So
the daemon makes its own authority, and the whole question is how much damage that authority
could do if its key leaked.
A stock local CA — what mkcert installs — could impersonate any site on the internet. The
key sits in a file on a laptop, and trusting it means trusting that file more than the web
PKI. This one carries nameConstraints with a single permitted subtree, the configured
suffix, so a leaked key can mint certificates for *.ssh-browser and for nothing else.
RFC 5280 §4.2.1.10 requires that extension to be marked critical, which is what stops a
conforming verifier from quietly ignoring it.
Two more limits, for the same reason:
basicConstraintscarriespathLenConstraint: 0, so this authority cannot sign another authority. Without it a leaked key could mint an intermediate; the name constraint would still hold, but the blast radius would grow to whatever that intermediate signed.keyUsageiskeyCertSignandcrlSignonly, so the key cannot serve TLS itself.
Every one of those three is read back out of the encoded certificate by x509-parser, which
is not the code that wrote it. Asserting against rcgen’s own view would only say that the
builder remembered what it was told; a browser reads bytes, so the tests read bytes.
And the constraint is enforced, measured rather than assumed. A name constraint is worth exactly what the verifier reading it chooses to do, and “required by the RFC” and “honoured by the browser on your desk” are different claims. With a CA of this shape in the current user’s root store, two leaves signed by it, and a browser that was not told to ignore certificate errors:
openssl s_client | Chromium | |
|---|---|---|
| a name under the suffix | Verify return code: 0 (ok) | loaded, isSecureContext: true |
evil.example | 47 (permitted subtree violation) | refused, net::ERR_CERT_INVALID |
And what the mode is for is measured too. Through the daemon, against a real SSH host, an
https alias origin reports isSecureContext: true with navigator.serviceWorker,
crypto.subtle and caches all present — the three things an http alias origin does not
have, and the reason this exists.
The same run is what ruled out a wildcard certificate: see Authority::leaf_for.
Firefox and Safari are unmeasured. Firefox keeps its own store and does not read the system
one, which trust_instructions says; whether it honours the constraint is the same question
again, and not one to answer by assuming.
The daemon never installs this. Putting a root into a trust store changes how the whole
machine treats the internet, is not undone by uninstalling a Rust binary, and is not a
decision a background process should make. ssh-browser trust prints the command for the
platform and stops; running it is the reader’s, with the command in front of them.
Structs§
- Authority
- The authority’s certificate and the key that signs with it.
- Leaf
- A serving certificate and its key, both as PEM.
- Limits
- What the encoded certificate says about how far it may reach.
Enums§
- Found
- Whether the authority was already there, which decides how loudly to say what to do next.
- Store
- Which trust store the instructions are for.
Constants§
- AUTHORITY_
NAME - The stem of every authority’s name.
Functions§
- authority_
dir - Where the authority lives between runs.
- certificate_
path - Where the certificate to be trusted goes. Named so
ssh-browser trustcan print it without creating an authority. - common_
name - The exact common name of the authority for
suffix. - instructions_
for - Printed, never executed. The three differ in more than spelling: the Windows one needs no elevation and writes to this account only, the macOS one prompts for a password and writes to the login keychain, and on Linux the location depends on the distribution while Firefox keeps its own store regardless. Guessing wrong while running as somebody’s shell is not a thing to do quietly.
- limits_
of - Read the limits out of a PEM certificate.
- load_
or_ create - Load the authority for
suffix, or make one and write it down. - load_
or_ create_ reporting - The same, saying which of the two happened.
- permits_
only - Is this certificate an authority that can vouch for
suffixand nothing else? - spki_
pin - Base64 of the SHA-256 of a certificate’s
SubjectPublicKeyInfo. - trust_
instructions - What to run to trust this authority, for the platform this is running on.