Skip to content

Issue from the internal CA

What you'll have at the end: an internal endpoint serving a certificate signed by the Hub's own built-in CA, renewing like any managed endpoint — same delivery, same verification — with revocation one click away and OCSP/CRL served by the Hub itself. No ACME, no rate limits, no external dependency (see Renewal lanes).

Use this lane for services only your own machines talk to: internal tools, service-to-service TLS, anything where a public CA buys you nothing. The trade is explicit: clients must trust your Hub's root (Step 2), and the Hub only issues for hostnames it classifies as internal — it refuses to sign a public name, so a mis-pointed profile can never mint a lookalike certificate for a public domain.

A hostname classifies as internal when it is localhost, ends in .internal, .local, .svc, or .svc.cluster.local, is a private (RFC 1918) or loopback IP address, or ends with a suffix you declare in the Hub's CYPHERS_INTERNAL_DOMAINS environment variable (comma-separated — e.g. CYPHERS_INTERNAL_DOMAINS=example.com for intranet services living under a registered zone). Everything else is public and is refused.

Prerequisites:

  • The endpoint is visible on Manage with served proof — else do Track a new endpoint first. A first-time internal service typically starts on a self-signed certificate; that is enough for the Scout to observe it serving.
  • Its hostname classifies as internal (list above) — or you have set CYPHERS_INTERNAL_DOMAINS on the Hub (see Hub → Install for where Hub environment variables live).

Time: about 15 minutes, most of it client trust distribution.

Mock values used throughout — substitute your real ones:

Endpoint intranet.example.internal, served by Scout web-01
Hub https://hub.example.internal (PKI endpoints on :8443)
Scout install root /opt/cyphers (data dir /opt/cyphers/data)

Step 1 — Create the profile

Settings → Certificate issuance → Renewal Profiles → New Profile. Under Certificate Issuer, set Issuer Type to Hub Internal CA.

You should now see the issuer panel collapse: Provider, Environment, Validation Method, Preferred Chain, and the ARI checkbox all disappear. That is not a display shortcut — the server forces the same facts (an internal-CA profile is always production, never staging, and ARI is meaningless without an ACME CA), so there is nothing left to choose. Fill in what remains:

Field Value Why
Profile Name internal-ca Named for what it does.
Contact Email ops@example.com The certificate contact for the internal CA (no ACME account is involved).
Key Type ECDSA P-384 (recommended)
Delivery Mode Scout (installs and reloads locally)

Click Create Profile. You should now see it in the Renewal Profiles table. (Field-by-field detail: Hub → Renewal profiles.)

Step 2 — Give clients the root

Anything that will connect to the endpoint must trust the Hub CA. The Hub publishes its own CA material on a public, read-only PKI surface — unauthenticated by design — on the Scout REST port (see Hub → Hub PKI endpoints):

$ curl -sf http://hub.example.internal:8443/api/ca/root -o cyphers-root.pem

(GET /api/ca/chain returns the full chain — intermediate plus root — if a client needs both.) Install cyphers-root.pem wherever your clients read trust: the OS trust store (Debian/Ubuntu: /usr/local/share/ca-certificates/ + update-ca-certificates; RHEL: /etc/pki/ca-trust/source/anchors/ + update-ca-trust), a JVM truststore, a container base image — whatever your estate uses.

You should now see the file begin with -----BEGIN CERTIFICATE-----; openssl x509 -in cyphers-root.pem -noout -subject shows your Hub CA's subject.

Step 3 — Adopt the endpoint with the profile

On Manage, the intranet.example.internal row (Untracked) offers Adopt. Pick internal-ca in the Renewal profile select and click Apply.

You should now see the row managed. Nothing has been issued yet. (Details: Adopt an endpoint.)

Step 4 — Renew Now

Click Renew Now on the row. The flow is the same shape as every other lane — only the signer differs:

  1. The Scout generates the private key and a CSR on the endpoint — the key never leaves the host, on this lane like every other.
  2. The CSR travels to the Hub, which validates it (signature, SAN match against the requested domains) and runs the policy check — including the internal-hostname classification above.
  3. The Hub's built-in CA signs directly. No ACME order, no challenge, no waiting on an external CA. A production server certificate is issued for 47 days by default.
  4. The certificate is delivered back and installed into the managed directory (/opt/cyphers/data/certs/intranet.example.internal/), hooks run, the install is validated, and the served endpoint is re-probed — identical to any lane (see Scout → Commands).

You should now see an Activity entry — Certificate issued via Hub CA for intranet.example.internal with the new serial — and, shortly after, the drawer → Renewals tab showing the renewal Closed. A renewal closes only when a probe observes the endpoint serving the new certificate (see Renewal lanes → Verification closes every lane).

Step 5 — Verify from a client

From a machine that got the root in Step 2:

$ echo | openssl s_client -connect intranet.example.internal:443 \
    -servername intranet.example.internal -CAfile cyphers-root.pem 2>/dev/null \
  | grep "Verify return code"
    Verify return code: 0 (ok)

You should now see Verify return code: 0 (ok), and on Manage the row shows the new expiry. From here the endpoint renews like any managed endpoint — autopilot included.


Revocation

This is your CA, so revocation actually works end to end — no external CA in the loop:

  • From the endpoint's drawer on Manage: each non-revoked certificate in the Known certificates list carries a Revoke the certificate expiring … action, with a reason field and an option to also destroy the private key on the endpoint.
  • From the Internal CA page (/internal-ca): the Hub-issued certificate inventory lists every certificate with its serial, expiry, and status; each non-revoked row offers a Revocation reason field and a Revoke button.

Revocation is published by the Hub itself: clients and middleboxes can check GET /api/ca/crl.pem (the CRL) and POST /api/ca/ocsp (RFC 6960 OCSP) on the same :8443 PKI surface — and every issued certificate embeds these pointers, derived from the Hub's CYPHERS_CA_URL, so standard clients find them without configuration.

Revocation is permanent

A revoked certificate cannot be un-revoked — the endpoint needs a fresh issuance (Renew Now) to serve again, and ticking the destroy-key option additionally erases the private key on the endpoint.

The Internal CA page also carries an Issue certificate form (Common name + Additional names (comma-separated)) for issuing outside the Manage flow. It is still Scout-driven — the key is generated on the endpoint and never leaves it — so a 409 there means no live Scout serves that name, not a form mistake.

Troubleshooting

What you see Meaning Remedy
Renew or + Add endpoint refuses with "not classified as an internal name" (builtin_ca_not_internal) The hostname — or one of the additional names — classifies as public, so the internal CA would refuse to sign it. The refusal is raised at pre-flight, before the Scout generates a key or anything is ordered; nothing is left behind to clean up. If it genuinely is an internal service under a registered zone, add the suffix to CYPHERS_INTERNAL_DOMAINS on the Hub and restart it. If it is a public endpoint, use a public lane instead — Let's Encrypt HTTP-01 or a third-party CA.
Clients show certificate warnings after Step 4 The client doesn't trust the Hub root. Step 2 on that client (and remember: containers and JVMs often carry their own trust store).
409 on Renew or on the Internal CA Issue certificate form No live Scout is bound to that hostname. Check the Scout's row under Scouts and systemctl status cyphers-scout on the host.
Renewal parks in action needed after install Certificate issued + installed but the reload didn't take. No re-issue happens. Fix the reload/hook on the host; the next routine scan closes the renewal on its own — see Scout → Commands.