Skip to main content

Serverless Attestation Extension

The Extension method plugs custom attestation logic into serverless attestation. The workload submits proof data through the SDK. The Defakto Server forwards the proof to an HTTPS webhook you operate. The webhook validates or enriches the proof and returns custom attributes that become available for SPIFFE Verifiable Identity Document (SVID) issuance.

Use this method when the built-in attestation methods can't express your requirements, for example:

  • Cross-checking workload identity against an internal inventory or CMDB.
  • Enriching SVIDs with deployment metadata (environment, team, cost center) from an external system.
  • Attesting platforms that have no built-in serverless method.
note

This method is used during serverless attestation (when workloads request SVIDs directly from the Defakto Server). For extending agent attestation, see the Agent Attestation Extension. For extending workload attestation, see the Server Workload Attestation Extension and the Agent Workload Attestation Extension. Unlike the agent flow, there is no extension executable. The workload produces the proof in-process through the SDK.

Attestation Flow

The extension is always processed last: The webhook receives the attributes already verified by the other methods in the policy, so it can cross-check the workload's proof against attested platform identity.

Attributes available for SVID issuance

Custom attributes returned by the webhook have the origin custom. The attribute names are defined by your webhook implementation.

AttributeDescription
custom.<key>Any key returned in the webhook's JSON response

Example SPIFFE ID path template using webhook-returned attributes:

/serverless/{{custom.environment}}/{{custom.team}}

How to Deploy

Step 1 — Update Trust Domain configuration

Add type: extension as a required attestor in a ServerlessAttestation policy. Combine the extension with a built-in method so the webhook receives platform-verified attributes to validate the proof against:

section: ServerlessAttestation
schema: v1
spec:
policies:
- name: aws_with_extension
svidPolicy:
pathTemplate: "/serverless/{{custom.environment}}"
requiredAttestors:
- type: aws_token
config:
issuerURLs:
- "https://a1e777e5-1234-5678-9bf8-cdda2afef4bb.tokens.sts.global.api.aws"
- type: extension
config:
webhookURL: "https://attestation.example.com/webhook"
timeout: "10s"
authType: "BEARER"
maxRetries: 3

Apply it using spirlctl:

spirlctl config set trust-domain --id <trust-domain-id> serverless.yaml

Or using Terraform:

resource "spirl_trust_domain_config" "serverless_attestation" {
trust_domain_id = spirl_trust_domain.my_trust_domain.id
sections = {
ServerlessAttestation = <<-YAML
section: ServerlessAttestation
schema: v1
spec:
policies:
- name: aws_with_extension
svidPolicy:
pathTemplate: "/serverless/{{custom.environment}}"
requiredAttestors:
- type: aws_token
config:
issuerURLs:
- "https://a1e777e5-1234-5678-9bf8-cdda2afef4bb.tokens.sts.global.api.aws"
- type: extension
config:
webhookURL: "https://attestation.example.com/webhook"
timeout: "10s"
authType: "BEARER"
YAML
}
}

Once a configuration document is validated and stored, the Defakto control plane syncs it to your Trust Domain Servers automatically. No restart is required.

Server Configuration Reference

FieldTypeRequiredDefaultDescription
webhookURLstringYesHTTPS endpoint to call during attestation
timeoutdurationNo5sMaximum wait time per webhook call (Go duration format: 10s, 1m)
caCertsstringNoSystem rootsPEM-encoded CA certificate bundle for validating the webhook's TLS certificate
insecureSkipVerifyboolNofalseSkip TLS verification. Never use in production.
authTypestringNoNONEAuthentication type: BEARER or NONE
tokenPathstringNoIn-cluster service account tokenPath to bearer token file. Used when authType is BEARER
maxRetriesintNo2Maximum retry attempts for transient errors (5xx, timeouts). Set 0 to disable

Step 2 — Implement the Webhook

Your webhook must accept HTTP POST requests at the configured webhookURL. The Trust Domain Server sends a JSON body and expects a JSON response.

The webhook protocol is the same as the Agent Attestation Extension webhook, including its OpenAPI specification. One webhook implementation can serve both agent and serverless attestation.

Request format

{
"_meta": {
"version": "1.0"
},
"cluster": {
"cluster_id": ""
},
"payload": "<string from the workload's extension attestor>",
"<attestor_key>": {
"<nested>": "<attributes>"
}
}
FieldDescription
_meta.versionProtocol version — currently "1.0"
cluster.cluster_idEmpty in serverless attestation requests, since serverless workloads have no agent cluster context. Treat it as informational rather than a field to branch on
payloadProof data submitted by the workload's extension attestor, forwarded verbatim
<attestor_key>Attributes from the other verified methods in the policy (e.g. aws_token, gcp_iit)

Example request body for a workload attesting with aws_token alongside the extension:

{
"_meta": { "version": "1.0" },
"cluster": { "cluster_id": "" },
"aws_token": {
"account": { "id": "123456789012" },
"source_region": "us-west-2"
},
"payload": "eyJkZXBsb3ltZW50IjoiY2hlY2tvdXQtYXBpIiwiZW52IjoicHJvZHVjdGlvbiJ9"
}

Success response

Return HTTP 200 with a flat JSON object. All keys become custom.* attributes:

{
"environment": "production",
"region": "us-west-2",
"team": "platform"
}

An empty object {} is valid. Attestation succeeds with no additional attributes.

HTTP 200 is the only success status. Any other status, including other 2xx codes such as 201 and 204, rejects the attestation. The response body must also be valid JSON: A 200 with an empty body fails to parse and rejects the attestation, so return {} rather than no body at all.

Error response

Return a JSON object with an error key to reject the attestation:

{
"error": "workload is not registered in CMDB"
}

A non-empty error value rejects the attestation even when the HTTP status is 200. An absent or empty error is treated as success. The error message is logged and returned to the caller. Any keys other than error are ignored when error is present.

HTTP status codes

StatusBehavior
200Attestation proceeds, unless the body contains a non-empty error. The body is parsed for custom attributes
Any other status below 500Attestation rejected immediately — not retried. This includes 4xx and other 2xx codes such as 204
5xxRetried up to maxRetries times with exponential back-off

Retry behavior

Retryable (retried up to maxRetries times with exponential back-off):

  • HTTP 5xx responses — server-side errors
  • Connection timeouts — webhook took too long to respond
  • Temporary network failures — connection refused, connection reset
  • Temporary DNS failures — DNS resolution temporarily unavailable

Non-retryable (fail immediately):

  • Any response status below 500 other than 200 — including 4xx and non-200 2xx codes
  • TLS certificate validation failures — invalid or expired certificate
  • Authentication failures — invalid bearer token
  • Invalid webhook responses — malformed JSON or unexpected format
  • Configuration errors — invalid webhook URL

Because a webhook call can be retried, handlers must be idempotent. A timeout or 5xx returned after the handler has already committed a side effect (an audit record, a counter, a provisioning call) will be retried, so deduplicate on a request identifier or make the side effect safe to repeat.

Step 3 — Implement the workload's extension attestor

The Defakto SDKs have no built-in extension attestor because the proof format is defined entirely by your webhook. Instead, implement the SDK's Attestor interface with the plugin name extension and version 1.0, and pass it to AttestingWorkloadAPIClient alongside the policy's other attestors.

See SDK Examples — Extension for complete Python and TypeScript examples.

Requirements for the extension attestor:

  • The plugin name must be extension to match type: extension in the policy.
  • The plugin version must be 1.0. Other versions are rejected by the server.
  • The payload must be non-empty. The serverless API rejects attestations without evidence.
  • The DEFAKTO_ATTESTORS environment variable does not support extensions. Use AttestingWorkloadAPIClient with an explicit attestor list.

Step 4 — Verify

Server logs — look for webhook activity:

DEBUG Calling external webhook {"url": "https://attestation.example.com/webhook", "attempt": 0}
INFO Custom attributes added {"count": 2}

Common errors:

ErrorLikely cause
no trust domain policy authorizes the provided attestorsThe workload did not submit evidence for every method the policy requires. Verify the SDK submits the extension attestor together with the policy's other attestors
unsupported proof versionThe extension attestor's plugin version is not 1.0
invalid attestation: evidence is requiredThe extension attestor produced an empty payload
failed to process requirement of type extensionThe webhook rejected the proof, returned malformed JSON, or was unreachable. Check the server logs for the webhook's error message

Security Considerations

  • Combine with a built-in method. An extension-only policy makes your webhook the sole gatekeeper for SVID issuance. Requiring a platform method (e.g. aws_token) alongside the extension lets the webhook cross-check the workload's proof against platform-verified attributes.
  • Validate the payload in your webhook. The payload is self-asserted by the workload. Treat it as untrusted input and verify it against the attested attributes or an external source of truth.
  • Use HTTPS for the webhook URL in all non-development environments, and use caCerts when the webhook's certificate is signed by a private CA.
  • Set a short timeout. A slow webhook delays every serverless SVID request that matches the policy.
  • Limit error detail in webhook responses. Error messages are logged and may be returned to the workload, so avoid exposing sensitive internal details.