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_DOMAINSon 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):
(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:
- The Scout generates the private key and a CSR on the endpoint — the key never leaves the host, on this lane like every other.
- 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.
- 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.
- 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. |