Skip to main content

SVID Issuance Policy

The SVIDIssuancePolicy managed configuration section centralizes per-cluster SPIFFE Verifiable Identity Document (SVID) issuance settings and adds an optional list of workload-specific overrides. Once a cluster has a policy, that policy is the complete description of SVID issuance for the cluster. It is the preferred way to configure:

See those pages for field-level documentation and examples.

Why use it?

  • Single configuration source: Manage TTLs, subjects, and path templates per cluster from a single managed-config document instead of separate CLI flags.
  • Per-workload variations: Issue shorter-lived certificates for specific namespaces, distinct path templates for sensitive workloads, or different subjects for one service account — without writing a separate cluster.
  • Live updates: Configuration changes propagate to servers without restart.

Implicit Deny

Behavior change in spirl-server 0.39.0

From 0.39.0, a cluster's SVIDIssuancePolicy is the complete description of SVID issuance for that cluster, and issuance fails closed. A workload that matches no override and no base policy receives no X.509-SVID, no JWT-SVID, and no WIT-SVID. The cluster's legacy path template is never consulted for it, and neither are the cluster's legacy X.509 and JWT customization templates.

Earlier versions fall back to the cluster's legacy configuration. A cluster whose policy is incomplete keeps issuing today and starts denying on upgrade, with no configuration change of its own.

Before upgrading, audit every cluster that has a policy:

  1. Read the cluster's legacy configuration with spirlctl cluster info CLUSTER_NAME --trust-domain TD_NAME and its policy with spirlctl config get cluster --id <cluster-id>.
  2. Confirm the base policy and every override declares a pathTemplate. New saves are rejected without one, but a policy stored before that requirement still loads, and it denies every workload it resolves for. See Validation.
  3. Confirm the base policy is set, unless the section is deliberately an allowlist.
  4. Carry the cluster's legacy X.509 Subject, SANs, and JWT claims into the policy's x509 and jwt fields, since they stop being consulted.

A workload receives an SVID only if a policy section resolves a pathTemplate for it.

This gives two policy shapes:

ShapeEffect
Base policy plus policyOverridesThe base policy is the catch-all. Every workload the overrides do not match still receives an SVID.
policyOverrides onlyAn allowlist. Only workloads matching an override receive SVIDs.

pathTemplate is the field that decides issuance, and it is the one field with no fallback. An unset TTL or Subject is not a denial. An unset pathTemplate is.

The Trust Domain Server flags remain in the fallback chain, and they are the only fallback left on a policy cluster:

Unset in the resolved sectionFalls back toThen to
x509.ttl--x509-svid-ttl24h
jwt.ttl--jwt-svid-ttl24h
x509.subject--x509-svid-subjectThe default SVID subject
wit.ttlNo server flag exists24h
x509.dnsNames, x509.ipAddresses, jwt.additionalClaims, wit.additionalClaimsNothing. The field is simply unset

Clusters with no SVIDIssuancePolicy are unaffected. They continue to issue from the cluster's legacy configuration.

Observing denials

A denial is recorded as an SVID error event with the category policy_denied, carrying the workload's attested attributes so you can identify which workload was denied. Identical repeated denials are suppressed for 10 minutes so a retrying agent does not flood the event feed. That suppression applies to the event feed only. Metrics and logs have their own rate limits.

A denial increments two counters:

CounterLabelsNotes
spirl_server_svid_policy_denials_totalcluster_id, svid_typeDenials only.
spirl_server_mint_svid_totalsvid_type, status_codeDenials land on the primary issuance counter as status_code = policy_denied, alongside the other outcomes in the server runbook. A dashboard that filters this counter to status_code != "ok" already counts denials.

Server logs carry a rate-limited finished call warning with code = FailedPrecondition and no workload identity, so use the SVID error event to identify the denied workload.

An issuance failure is recorded differently, so the two are never confused. A malformed template or an attribute that fails to render lands as status_code = template_error with the SVID error category issuance_error, and the workload sees InvalidArgument.

The agent receives a FailedPrecondition error, which distinguishes a policy denial from an infrastructure failure. The workload's Workload API call fails with the same code, carrying this message from the Trust Domain Server at the end of the agent's error chain:

SVID issuance denied by the cluster's SVID issuance policy: no policy section
resolved a path template for this workload

A denial does not revoke SVIDs already issued.

Per-Workload Overrides

policyOverrides is an ordered list of conditional policy variations. When a workload's attested attributes match an override's when clause, that override's policy applies in place of the base policy. The first match wins; later overrides and the base policy are skipped for that workload.

section: SVIDIssuancePolicy
schema: v1
spec:
policy:
pathTemplate: "/{{cluster.name}}/default/ns/{{kubernetes.pod.namespace}}/sa/{{kubernetes.pod.service_account}}"
x509:
ttl: "24h"
policyOverrides:
- when:
kubernetes.pod.namespace: production
policy:
pathTemplate: "/{{cluster.name}}/prod/ns/{{kubernetes.pod.namespace}}/sa/{{kubernetes.pod.service_account}}"
x509:
ttl: "4h"
- when:
kubernetes.pod.service_account: critical-service
policy:
pathTemplate: "/{{cluster.name}}/critical/ns/{{kubernetes.pod.namespace}}/sa/{{kubernetes.pod.service_account}}"
x509:
ttl: "1h"

In this example:

  • A pod in the production namespace receives a SPIFFE ID under /prod/... with a 4h TTL.
  • A pod whose service account is critical-service (in any namespace except production) receives a SPIFFE ID under /critical/... with a 1h TTL.
  • Every other pod receives the base policy: /default/... with a 24h TTL.

When-Clause Match Semantics

Each entry in a when map is one condition. All conditions in a single when clause must match (logical AND).

EntryMatch Type
key: valueExact match — the workload must carry that attribute with that exact string value.
key: (no value)Presence match — the workload must carry that attribute with any value.

Allowed When Keys

when keys are restricted to attributes whose values represent a real authorization boundary — that is, attributes a pod author or process launcher cannot freely pick to claim a more privileged policy.

SourceAttributeSet by
Agent / cluster identityagent.idSPIRL agent provisioning
Kubernetes admissionkubernetes.pod.namespaceKubernetes RBAC
kubernetes.pod.service_accountKubernetes RBAC
Workload Attestationjwt.claim.issOIDC JWT Issuer

Attributes that look like identity but are pod-spec- or process-launcher-chosen — including kubernetes.pod.name, kubernetes.pod.node.name, kubernetes.container.name, kubernetes.container.image.name, linux.user.*, and linux.binary.path — are not on the allowlist. Anyone with create pod permission can pick those values, so matching on them adds no security restriction beyond what the underlying RBAC already provides.

Workload-supplied or content-addressed attributes (custom.*, pod annotations, pod labels, JWT claims, kubernetes.container.image.id, linux.binary.sha256) are also excluded: when-clause matching is scoped to platform-set identity, not workload content.

Override Semantics

When an override's when clause matches at issuance time, the override's policy fully replaces the base policy for that workload. There is no merging of fields. Each override must therefore be self-contained. It must declare its own pathTemplate, and any of x509 / jwt / wit it wants to apply.

Base declaresOverride declaresEffective for matching workload
pathTemplate, x509, jwtpathTemplate, x509, jwtAll from override
pathTemplate, x509, jwt, witpathTemplate, x509: {ttl: 4h}Override's pathTemplate and x509. JWT and WIT not configured for matching workload.
pathTemplate, x509, jwtx509: {ttl: 4h} and no pathTemplateRejected at admission. Every override must carry its own pathTemplate.

An override that omits wit turns off WIT-SVID issuance for the workloads it matches. That refusal is not a policy denial: The agent receives Unimplemented, and no policy_denied event is recorded.

SVID Key Type

spirl-system v0.42.0 required

The svidKeyType field requires spirl-system v0.42.0 or above.

svidKeyType configures the key algorithm agents use when generating SVID key pairs. Unlike policy and policyOverrides, it is an agent-level setting that applies uniformly to all workloads on the cluster and cannot be varied per workload.

section: SVIDIssuancePolicy
schema: v1
spec:
svidKeyType:
x509: ec-p256

X.509 key type

This setting controls the key pair generated by the agent for each X.509 SVID. It does not affect the root CA signing key algorithm used by the Trust Domain Server.

Supported values:

ValueAlgorithm
rsa-2048RSA 2048-bit (default when not set)
rsa-3072RSA 3072-bit
rsa-4096RSA 4096-bit
ec-p256ECDSA P-256
ec-p384ECDSA P-384

Validation

The control plane rejects configurations that:

  • Set neither policy nor policyOverrides.
  • Include an override with an empty when clause.
  • Include an override whose policy declares no fields.
  • Use a when key not on the allow-list above, or a when key that isn't a valid attribute name.
  • Contain two overrides with identical when clauses.
  • Omit pathTemplate, or set it to whitespace, on the base policy or on any override (overrides do not inherit from the base policy).
  • Set x509.dnsNames or x509.ipAddresses to an empty list. Omit the field instead to leave it unset.
  • Reference a deprecated attribute name, such as provider.aws.*, in x509.subject, x509.dnsNames, or x509.ipAddresses, even where a path template still accepts that name.
  • Use a jwt.additionalClaims template that is malformed or references reserved JWT claim names (iss, sub, aud, exp, nbf, iat, jti).

pathTemplate is required in the generated JSON schema, so the console editor flags a missing one before you save.

Existing policies are not re-validated

The pathTemplate requirement is enforced on writes only. A policy saved before the requirement existed still loads, and on spirl-server 0.39.0 it denies every workload it resolves for. Re-saving the configuration is what surfaces the error. Audit stored policies rather than relying on admission to have caught them.

Path-template syntax and platform-specific allowed tags are validated the same way they are for SPIFFE ID templates, against both the base policy and each override.