Options and lifetime resolution
Between “the client asked” and “the CA signed” sit two decisions: what the certificate may carry, and how long it lives. Both only ever narrow. This page is the mechanism; the operator-facing settings are on certificate lifetime policy.
Requests ask, the server narrows, config gates
Section titled “Requests ask, the server narrows, config gates”Three layers narrow what a certificate may carry. Only the server config defines what exists at all; nothing reachable over HTTP can exceed it.
flowchart TD
accTitle: How a request’s options are narrowed before signing
accDescr {
The client asks for extensions, a force-command and a source-address
restriction; principals are never part of the request. Each is tested against what the server
configuration permits. Anything not permitted is trimmed and recorded as
removed rather than silently dropped; anything permitted is kept. The
approval page then shows the candidate set together with the trimmed
options, the user narrows it further or approves as-is, the lifetime
policy is applied, and the certificate is signed.
}
A["Client request:<br/>extensions, force-command,<br/>source-address"] --> B{"Permitted by<br/>server config?"}
B -- "no" --> C["Trim, record as removed"]
B -- "yes" --> D["Keep"]
C --> E["Approval page:<br/>candidate set +<br/>trimmed options shown"]
D --> E
E --> F["User narrows or approves"]
F --> G["Lifetime policy applied<br/>(diagram 3)"]
G --> H["Sign"]
- The client’s request states what it wants: extensions, a
force-command,source-addressrestrictions, and whether a hardware key may skip touch. It names no principals; the approver picks those (see below). - The server checks each against the type’s configuration.
- Anything the config does not permit is trimmed, not rejected, and recorded as removed. A request asking for too much still succeeds; it just gets less.
- The approval page shows the candidate set together with the trimmed options, struck through, so the difference between “asked for” and “will be issued” is visible before anyone clicks.
- The approver may narrow it further, or approve as shown.
- Lifetime policy runs on the result.
- The certificate is signed.
The extension ceiling is per type:
cert_options.user.extensions
defaults to permit-pty, permit-user-rc, while
cert_options.pam.extensions
and
cert_options.console.extensions
default to empty, because permit-pty and friends mean nothing to a
certificate that authenticates one local operation and is then discarded.
Two things never come from the request:
- Principals. They come from the approver’s identity and the accounts it holds, never from a field the caller sent.
- The source address policy is judged on. The server uses the address it
observed, not the
source-addressrestriction the client asked for, which is unverified client input.
Group membership never appears in a certificate. Groups feed the lifetime decision only.
Certificate lifetime policy
Section titled “Certificate lifetime policy”The server is the policy decision point; the target host’s sshd enforces the
result.
flowchart TD
accTitle: How certificate lifetime policy reaches a decision
accDescr {
A certificate request carries signals: the source network, the
requester’s group membership, the certificate type and the options asked
for. Those are matched against the policy rules. With no rule matched
the request is denied, because the default is deny. Where rules do match
they are intersected — the shortest lifetime wins and the narrowest
principal set wins — producing a validity window and its constraints.
The certificate is signed and the signals behind the decision are logged
with it.
}
R["Certificate request"] --> S["Signals:<br/>source network, group membership,<br/>certificate type, requested options"]
S --> M{"Any policy rule<br/>matched? (default deny)"}
M -- "no" --> X["Deny"]
M -- "yes" --> P["Intersect matched rules:<br/>shortest lifetime wins,<br/>narrowest principal set wins"]
P --> Q["Validity window + constraints"]
Q --> Y["Sign, log the signals<br/>behind the decision"]
- A request arrives.
- The signals available to the decision are the source network the server
observed, what the approver’s identity satisfies, and which certificate type
is being issued, which selects the
cert_optionsblock the whole decision is bounded by. - Rules are matched.
- The matched rules are combined, and combining can only shorten and narrow.
- The result is a validity window plus the constraints that go with it.
- The certificate is signed, and the signals behind the decision are recorded.
How a duration is actually chosen
Section titled “How a duration is actually chosen”- Tier. The first tier in
lifetime_policy.tierswhosewhencondition the approver’s identity satisfies. Order matters and is not validated: numeric thresholds nest, since everyone satisfyingat_least: 40also satisfiesat_least: 30, so write them in descending order. No match meansdefault_duration. - Source rule. The longest-prefix match against the request’s source address, as in a routing table. Order does not matter, and ties resolve to the stricter rule, so a duplicate can never loosen anything.
- The result is the minimum of those, clamped to the type’s
valid_durationceiling.
Extensions follow their own algebra. A tier may grant extensions, which reads
like widening and is not: the grant is bounded by the type’s extensions
ceiling, and a grant naming anything outside that ceiling fails startup rather
than being silently trimmed. Identity grants, network narrows – a source rule
can only subtract. On the service path a source rule may also pin the issued
certificate to the address it was requested from, which is a restriction, not a
capability.
The winning tier, the condition it matched, the source rule, the ceilings, and the effective values are recorded as a structured document on the approval’s decision row, so “why did this certificate get fifteen minutes” is answerable from the record rather than by re-deriving the computation.
Ceilings above the policy
Section titled “Ceilings above the policy”Two absolutes sit above everything above, checked before signing as
defense in depth:
max_cert_lifetime
and
max_service_cert_lifetime.
Below them, each type’s valid_duration is the ceiling any policy narrows
from.
Note how different the types are by default:
cert_options.user.valid_duration
and
cert_options.pam.valid_duration
are short, because a PAM or console certificate is validated once and thrown
away, while
cert_options.service.valid_duration
is long, because the job holding it has no human to ask.
What the key ID says
Section titled “What the key ID says”The key ID is the string sshd logs on every login, so it is where the audit
trail reaches the target host. Each type shapes it with a Go template:
{{.Username}}:{{.ClientIP}}:{{.UniqueID}}, including operator-defined extra
OIDC claim fields captured at login ({{.Extra.dept}}).
- A bad template fails startup, not the first issuance.
- A field with no value renders as
MISSINGrather than vanishing. - An unset
cert_options.service.key_id_templatefalls back to theusertemplate. The PAM and console templates deliberately do not, so asudo, a console login, and an SSH login by the same person stay distinguishable in an audit log. - The fields render from the approver’s login. For a service enrollment that names the human who approved it, because the key ID and principals are fixed at approval, long before the code is redeemed unattended.
Where this is configured
Section titled “Where this is configured”| What | Key |
|---|---|
| Who may get this type at all | cert_options.<type>.require (user, service, pam, console) |
| Lifetime ceiling per type | cert_options.<type>.valid_duration |
| Extension ceiling per type | cert_options.<type>.extensions |
| Tiered durations by identity | lifetime_policy.tiers |
| Fallback when no tier matches | lifetime_policy.default_duration |
| Narrowing by source network | lifetime_policy.source_policy |
| What a missing claim resolves to | lifetime_policy.on_absent_claim |
| Absolute ceilings | max_cert_lifetime, max_service_cert_lifetime |
| Which proxy to believe about the client address | http.trusted_proxies |
| The claims the conditions read | authentication.fields |
Related
Section titled “Related”- Certificate lifetime policy – the full semantics, with worked configurations.
- Key ID templates – every field, and what a bad template does.
- Server configuration examples – whole files you can start from.
- Interactive user certificates – where in the flow this all happens.
- Audit log – what the decision record keeps.