Skip to main content

SVID Block List

defakto-server v0.42.0 required

The SVIDBlockList section requires defakto-server (or spirl-server) v0.42.0 or above on every Trust Domain Server in the trust domain. Older servers ignore the section without an error and keep issuing SVIDs for blocked SPIFFE IDs.

The SVIDBlockList managed configuration section lists SPIFFE IDs that the Trust Domain Server must not issue SPIFFE Verifiable Identity Documents (SVIDs) for. Use it as a break-glass control when a workload is compromised. Adding an entry stops new X.509-SVIDs, JWT-SVIDs, and WIT-SVIDs for matching SPIFFE IDs within seconds, with no server or agent restart and no change to the cluster's SVID Issuance Policy. If the compromise may extend to the trust domain's signing keys, also follow the Signing Key Rotation Runbook.

The Trust Domain Server checks the block list against the SPIFFE ID it is about to issue, after the path template is rendered. The check applies to every way the Trust Domain Server issues an SVID:

The section applies at the trust domain scope. One entry blocks matching SPIFFE IDs in every cluster and on every Trust Domain Server in the trust domain.

Configuration​

section: SVIDBlockList
schema: v1
spec:
entries:
- spiffeID: "spiffe://example.org/prod/ns/payments/sa/api"
match: exact
reason: "INC-123: leaked credentials"
- spiffeID: "spiffe://example.org/prod/ns/checkout/**"
match: wildcard
reason: "INC-124: namespace compromised"
- spiffeID: "spiffe://example.org/*/ns/billing/sa/worker"
match: wildcard
reason: "INC-125: service account compromised in every cluster"
FieldRequiredDescription
entries[].spiffeIDYesThe SPIFFE ID to block, or a pattern when match is wildcard. Must belong to the trust domain the section is applied to.
entries[].matchYesexact or wildcard. See Match types.
entries[].reasonNoFree text recorded with each denial. The reason is not sent to the workload.

Quote each spiffeID value so entries with and without wildcards read the same way. Quoting does not change how an entry matches.

An empty entries list blocks nothing.

Match types​

matchspiffeID holdsBlocks
exactA literal SPIFFE ID. Wildcards are not allowed.Only that SPIFFE ID.
wildcardA pattern of path segments, where * and a final ** are wildcards.Every SPIFFE ID the pattern matches.

A wildcard pattern is compared segment by segment:

  • * matches exactly one whole segment with any value.
  • ** matches zero or more segments. It is allowed only as the final segment.
  • Any other segment must match exactly.

Without a final **, a SPIFFE ID matches only if it has the same number of segments as the pattern.

PatternSPIFFE IDBlocked
spiffe://example.org/prod/ns/checkout/**spiffe://example.org/prod/ns/checkoutYes
spiffe://example.org/prod/ns/checkout/**spiffe://example.org/prod/ns/checkout/sa/webYes
spiffe://example.org/prod/ns/checkout/**spiffe://example.org/prod/ns/checkout-v2/sa/webNo
spiffe://example.org/*/ns/billing/sa/workerspiffe://example.org/staging/ns/billing/sa/workerYes
spiffe://example.org/*/ns/billing/sa/workerspiffe://example.org/staging/ns/billing/sa/worker/extraNo

Blocking a workload in every cluster​

The default Kubernetes path template starts with the cluster name:

/{{cluster.name}}/ns/{{kubernetes.pod.namespace}}/sa/{{kubernetes.pod.service_account}}

A * in the cluster segment blocks a workload in every cluster, including clusters registered after the entry is added:

spiffeID (match: wildcard)Blocks
spiffe://example.org/*/ns/billing/sa/workerThe worker service account in the billing namespace, in every cluster.
spiffe://example.org/*/ns/checkout/**Every workload in the checkout namespace, in every cluster.

* must be a whole segment. There is no partial-segment wildcard such as pay*, and no regular expression.

A pattern needs only one literal segment, so a short pattern can still match most of a trust domain. If every SPIFFE ID in the trust domain starts with /k8s, then spiffe://example.org/k8s/** blocks all of them. Before applying a wildcard entry, compare it against the SPIFFE IDs your path templates produce.

Blocking a SPIFFE ID​

Applying a section replaces that section as a whole. To add an entry without dropping the entries already in place, start from the current section.

  1. Confirm that every Trust Domain Server in the trust domain runs v0.42.0 or above.

  2. Read the current configuration:

    defakto config get trust-domain --id <trust-domain-id>
  3. Write svid-block-list.yaml with every existing SVIDBlockList entry plus the new one.

  4. Apply it:

    defakto config set trust-domain --id <trust-domain-id> svid-block-list.yaml

    defakto config set leaves the trust domain's other sections in place.

  5. Confirm the block took effect. See Observing denials.

To unblock a SPIFFE ID, remove its entry and apply the section again.

Using Terraform​

If the trust domain's configuration is managed with the spirl_trust_domain_config Terraform resource, add the section there:

resource "spirl_trust_domain_config" "example" {
trust_domain_id = spirl_trust_domain.example.id
sections = {
SVIDBlockList = <<-YAML
section: SVIDBlockList
schema: v1
spec:
entries:
- spiffeID: "spiffe://example.org/prod/ns/payments/sa/api"
match: exact
reason: "INC-123: leaked credentials"
YAML
}
}

A section added with the defakto CLI outside Terraform appears as drift. Copy the entry into the Terraform configuration before the next terraform apply, or the apply can remove the block.

Observing denials​

A block-list denial is recorded separately from an SVID Issuance Policy denial, so a dashboard or event filter can tell a break-glass block from a workload that no policy section covers.

A denial is recorded as an SVID error event with the category blocklist_denied, carrying the workload's attested attributes, the matched entry, and the entry's reason. The event's issuer type identifies the path that was denied: a cluster for agent workloads, serverless, or Developer Identity. Identical repeated denials are suppressed for 10 minutes so a retrying caller does not flood the event feed. That suppression applies to the event feed only.

A denial increments these counters:

CounterLabelsNotes
defakto_server_svid_blocklist_denials_totalissuer_type, cluster_id, svid_typeDenials only, on every issuance path. issuer_type is cluster, serverless, or developer-identity, the same values the activity feed uses. cluster_id is empty for Developer Identity, and for serverless workloads matched by a trust-domain-scoped policy.
defakto_server_mint_svid_totalsvid_type, status_codeAgent workloads only. Denials land on the primary issuance counter as status_code = blocklist_denied, alongside the other outcomes in the server runbook.

The Trust Domain Server returns a FailedPrecondition error. Serverless workloads and Developer Identity receive that error directly. An agent treats the error as final, and the workload's Workload API call fails with PermissionDenied, carrying the Trust Domain Server's message:

the trust domain server rejected the SVID issuance request with reason: SVID
issuance denied: the SPIFFE ID is on the trust domain's SVID block list

The matched entry and its reason appear only in the SVID error event, not in the error the workload receives.

In Console, a denial appears on the trust domain's Audit Logs tab with the event type Workload credential denied. Its summary starts with "X509 issuance blocked by the SVID block list" (or JWT or WIT), followed by the workload, the matched entry, and its reason.

Limitations​

  • SVIDs already issued stay valid. The block stops new SVIDs and rotations. An X.509-SVID, JWT-SVID, or WIT-SVID issued before the entry was added stays valid until it expires, 24 hours by default. Revoke or rotate whatever those SVIDs grant access to at the relying party.
  • Servers older than v0.42.0 do not enforce the block list. The Defakto Control Plane accepts the section regardless of server version, so entries added while a server runs an older version take effect when that server is upgraded. Review the block list before upgrading.
  • A server skips an entry it cannot read. An entry this server version cannot parse, such as one using a match type added in a later version, blocks nothing on that server. The server logs Skipped SVID block list entry: it blocks nothing on this signer at Warn level, with the entry's SPIFFE ID, match type, and error.
  • The block list cannot change while the Defakto Control Plane is unreachable. Trust Domain Servers keep enforcing the last block list they received.
  • Token exchange is not covered. On-behalf-of tokens and OAuth access tokens are governed by the trust domain's token policy.

Validation​

The Defakto Control Plane rejects a section that:

  • Contains more than 100 entries.
  • Contains an entry whose spiffeID belongs to a different trust domain.
  • Sets match to a value other than exact or wildcard.
  • Contains an exact entry that is not a valid SPIFFE ID, or that contains *.
  • Contains a wildcard entry with a * that is not a whole path segment, or a ** anywhere but the final segment.
  • Contains a wildcard entry with no literal path segment, such as spiffe://example.org/* or spiffe://example.org/**, which would block the whole trust domain.