Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 139 additions & 11 deletions charts/kagenti-operator/crds/agent.kagenti.dev_agentcards.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,22 @@ spec:
jsonPath: .status.protocol
name: Protocol
type: string
- description: Workload Kind
jsonPath: .status.targetRef.kind
name: Kind
type: string
- description: Target Workload
jsonPath: .status.targetRef.name
name: Target
type: string
- description: Agent Name
jsonPath: .status.card.name
name: Agent
type: string
- description: Signature Verified
jsonPath: .status.validSignature
name: Verified
type: boolean
- description: Identity Bound
jsonPath: .status.bindingStatus.bound
name: Bound
Expand Down Expand Up @@ -72,31 +84,48 @@ spec:
allowedSpiffeIDs:
description: |-
AllowedSpiffeIDs is the allowlist of SPIFFE IDs that can bind to this agent.
Each ID must be a valid SPIFFE ID in the format spiffe://<trust-domain>/<path>
Each ID must be a valid SPIFFE ID in the format spiffe://<trust-domain>/<path>.
The SPIFFE ID from the JWS protected header must match one of these entries.
items:
description: SpiffeID represents a SPIFFE identity in the format
spiffe://<trust-domain>/<path>
pattern: ^spiffe://[a-zA-Z0-9][a-zA-Z0-9\-\.]*[a-zA-Z0-9](/[a-zA-Z0-9\-\._~%!$&'()*+,;=:@]+)*$
type: string
minItems: 1
type: array
expectedSpiffeID:
description: |-
Deprecated: ExpectedSpiffeID is no longer used. The SPIFFE ID now comes exclusively
from the JWS protected header (sign with --spiffe-id). This ensures all identity
claims are cryptographically bound to the signature.
This field is retained for backward compatibility and will be removed in a future release.
pattern: ^spiffe://[a-zA-Z0-9][a-zA-Z0-9\-\.]*[a-zA-Z0-9](/[a-zA-Z0-9\-\._~%!$&'()*+,;=:@]+)*$
type: string
strict:
default: false
description: |-
Strict enables strict enforcement mode. When true and binding fails,
the Agent controller will scale the deployment to 0.
Strict enables strict enforcement mode for identity binding.
When true, binding failures result in network isolation: the signature-verified
label is removed from pods, and NetworkPolicy restricts all ingress/egress.
When false (audit mode), binding results are recorded in status but network
access is not affected.
NOTE: Scale-to-zero enforcement is only available via the legacy Agent CRD controller.
type: boolean
trustDomain:
description: |-
TrustDomain overrides the controller's default trust domain.
Must be a valid DNS-like string without slashes.
Deprecated: TrustDomain is no longer used. The trust domain is determined
from the SPIFFE ID in the JWS protected header.
This field is retained for backward compatibility and will be removed in a future release.
pattern: ^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$
type: string
required:
- allowedSpiffeIDs
type: object
selector:
description: Selector identifies the Agent to index
description: |-
Selector identifies the Agent to index using label matching.
Deprecated: Use TargetRef instead. Selector is kept for backward compatibility.
If both TargetRef and Selector are specified, TargetRef takes precedence.
properties:
matchLabels:
additionalProperties:
Expand All @@ -112,8 +141,31 @@ spec:
description: SyncPeriod is how often to re-fetch the agent card (e.g.,
"30s", "5m")
type: string
required:
- selector
targetRef:
description: |-
TargetRef identifies the workload backing this agent using duck typing.
The referenced workload must have the required Kagenti labels (kagenti.io/type=agent).
This is the preferred way to reference agent workloads.
properties:
apiVersion:
description: APIVersion is the API version of the target resource
(e.g., "apps/v1")
minLength: 1
type: string
kind:
description: Kind is the kind of the target resource (e.g., "Deployment",
"StatefulSet")
minLength: 1
type: string
name:
description: Name is the name of the target resource
minLength: 1
type: string
required:
- apiVersion
- kind
- name
type: object
type: object
status:
description: AgentCardStatus defines the observed state of AgentCard.
Expand Down Expand Up @@ -175,6 +227,40 @@ spec:
name:
description: Name is the human-readable name of the agent
type: string
signatures:
description: |-
Signatures contains JWS signatures per A2A spec section 8.4.2.
Each element uses JWS JSON Serialization with protected header containing
the algorithm (alg), key ID (kid), and optional SPIFFE ID (spiffe_id).
items:
description: |-
AgentCardSignature represents a JWS signature on an AgentCard.
Follows the A2A specification section 8.4.2 — JWS JSON Serialization.
properties:
header:
description: Header contains optional unprotected JWS header
parameters.
properties:
timestamp:
description: Timestamp is when the signature was created
(ISO 8601 string)
type: string
type: object
protected:
description: |-
Protected is the base64url-encoded JWS protected header.
Decoded, it contains {"alg":"RS256","kid":"key-1","spiffe_id":"spiffe://..."}.
type: string
signature:
description: |-
Signature is the base64url-encoded JWS signature value.
The signing input is: BASE64URL(protected) || '.' || BASE64URL(canonical_payload)
type: string
required:
- protected
- signature
type: object
type: array
skills:
description: Skills is a list of skills/capabilities this agent
offers
Expand Down Expand Up @@ -303,8 +389,8 @@ spec:
type: object
type: array
expectedSpiffeID:
description: ExpectedSpiffeID is the derived SPIFFE ID based on Kubernetes
metadata
description: ExpectedSpiffeID is the SPIFFE ID used for binding evaluation
(from JWS protected header)
type: string
lastSyncTime:
description: LastSyncTime is when the agent card was last successfully
Expand All @@ -314,9 +400,51 @@ spec:
protocol:
description: Protocol is the detected agent protocol (e.g., "a2a")
type: string
signatureIdentityMatch:
description: |-
SignatureIdentityMatch indicates if both signature AND identity binding pass.
true only when ValidSignature is true AND BindingStatus.Bound is true.
type: boolean
signatureKeyId:
description: SignatureKeyID is the key ID used for verification (from
JWS protected header kid)
type: string
signatureSpiffeId:
description: |-
SignatureSpiffeID is the SPIFFE ID extracted from the JWS protected header.
This enables cross-referencing the signer's identity with the identity binding evaluation.
type: string
signatureVerificationDetails:
description: SignatureVerificationDetails contains details about the
last signature verification
type: string
targetRef:
description: |-
TargetRef contains the resolved reference to the backing workload.
This is populated after the controller successfully locates the workload.
properties:
apiVersion:
description: APIVersion is the API version of the target resource
(e.g., "apps/v1")
minLength: 1
type: string
kind:
description: Kind is the kind of the target resource (e.g., "Deployment",
"StatefulSet")
minLength: 1
type: string
name:
description: Name is the name of the target resource
minLength: 1
type: string
required:
- apiVersion
- kind
- name
type: object
validSignature:
description: ValidSignature indicates if the agent card signature
was validated (future use)
was validated
type: boolean
type: object
type: object
Expand Down
22 changes: 22 additions & 0 deletions charts/kagenti-operator/templates/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,28 @@ spec:
{{- range .Values.controllerManager.container.args }}
- {{ . }}
{{- end }}
{{- if .Values.signatureVerification.enabled }}
- "--require-a2a-signature=true"
- "--signature-provider={{ .Values.signatureVerification.provider }}"
{{- if .Values.signatureVerification.auditMode }}
- "--signature-audit-mode=true"
{{- end }}
{{- if .Values.signatureVerification.secret.name }}
- "--signature-secret-name={{ .Values.signatureVerification.secret.name }}"
{{- end }}
{{- if .Values.signatureVerification.secret.namespace }}
- "--signature-secret-namespace={{ .Values.signatureVerification.secret.namespace }}"
{{- end }}
{{- if .Values.signatureVerification.secret.key }}
- "--signature-secret-key={{ .Values.signatureVerification.secret.key }}"
{{- end }}
{{- if .Values.signatureVerification.jwks.url }}
- "--signature-jwks-url={{ .Values.signatureVerification.jwks.url }}"
{{- end }}
{{- if .Values.signatureVerification.enforceNetworkPolicies }}
- "--enforce-network-policies=true"
{{- end }}
{{- end }}
command:
- {{ .Values.controllerManager.container.cmd }}
image: {{ .Values.controllerManager.container.image.repository }}:{{ .Values.controllerManager.container.image.tag }}
Expand Down
19 changes: 19 additions & 0 deletions charts/kagenti-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,22 @@ certmanager:
# [NETWORK POLICIES]: To enable NetworkPolicies set true
networkPolicy:
enable: false

# [SIGNATURE VERIFICATION]: A2A agent card signature verification
signatureVerification:
# Enable signature verification for agent cards
enabled: false
# Audit mode: log failures but don't block (use for gradual rollout)
auditMode: false
# Provider type: "secret", "jwks", or "none"
provider: "none"
# Secret provider configuration
secret:
name: ""
namespace: ""
key: ""
# JWKS provider configuration
jwks:
url: ""
# Enforce network policies based on signature verification
enforceNetworkPolicies: false
Loading
Loading