attribution: per-session human identity (STS SourceIdentity + k8s impersonation)#30
Draft
stxkxs wants to merge 1 commit into
Draft
attribution: per-session human identity (STS SourceIdentity + k8s impersonation)#30stxkxs wants to merge 1 commit into
stxkxs wants to merge 1 commit into
Conversation
…ersonation)
By default a fab session acts as the pod's tenant IRSA role, bound to no
named human — so every Bedrock call and every `aws`/`kubectl` the agent
runs traces to a role, not a person. That is exactly the gap an evidence
engine surfaces. This is the opt-in that closes it.
─── src/attribution.ts ───
Set FAB_OPERATOR (a named human) + FAB_SESSION_ROLE_ARN and the in-pod
session, before the agent loop:
- assumes the session role carrying the operator as STS SourceIdentity
(via the `aws` CLI already in the image — no new dependency, same
child-process posture as the claude-cli runtime), exports the temp
creds, and drops the pod's IRSA web-identity vars so exactly one
credential mechanism remains. Every AWS call — the Bedrock InvokeModel
inference call and any `aws` the Bash tool runs — is then recorded in
CloudTrail under SourceIdentity=<operator>.
- writes a kubeconfig that authenticates with the SA token but
impersonates the operator (KUBECONFIG), so apiserver audit records
impersonatedUser=<operator>.
The operator is validated STS-clean up front so the SAME string binds both
streams. Credential assumption + kubeconfig are computed before any env
mutation (no half-attributed env). Fail-closed: if attribution was
requested but setup fails, the session aborts rather than run unattributed.
─── Wiring ───
role-session.ts applies it once per session pod (after a cheap role check
that avoids a wasted STS call on a typo'd role); sdk-k8s.ts forwards
FAB_OPERATOR / FAB_SESSION_ROLE_ARN / FAB_SESSION_DURATION onto the pod.
Inert for every other runtime and when FAB_OPERATOR is unset.
─── Why SourceIdentity, not Bedrock requestMetadata ───
The Agent SDK doesn't expose the InvokeModel request, so fab can't stamp
requestMetadata from this path. SourceIdentity rides the credentials the
SDK already resolves and is crossbearing's strongest binding — it
attributes the agent's aws/kubectl tool-call records, which crossbearing
corroborates.
docs/attribution.md covers the required platform IAM (session-role trust
policy allowing sts:AssumeRole + sts:SetSourceIdentity; the role needs
bedrock:InvokeModel) and the k8s impersonate RBAC, plus limitations
(process-wide operator, credential-TTL hard cliff, success-only records).
20 tests; full suite green; lint + format clean.
Co-authored-by: stxkxsbot <275011021+stxkxsbot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — the per-session human-attribution hook. Closes the "every agent action collapses to one anonymous IRSA role" gap so an evidence engine can bind actions to a named human. Marked draft because it needs companion platform IAM/RBAC (below) stood up before it does anything.
What
FAB_OPERATOR=<human>+FAB_SESSION_ROLE_ARN→ the in-pod session, before the agent loop:SourceIdentity(via theawsCLI already in the image; zero new deps), exports the temp creds, and drops the pod's IRSA web-identity vars. Every AWS call — BedrockInvokeModeland anyawsthe Bash tool runs — is recorded in CloudTrail underSourceIdentity=<operator>.impersonatedUser=<operator>.The operator is validated STS-clean up front so the same string binds both streams; creds + kubeconfig are computed before any env mutation (no half-attributed env); fail-closed if setup fails. Inert for every other runtime and when
FAB_OPERATORis unset.Crossbearing consumes it
CloudTrail
SourceIdentity→AttrSTSSourceIdentity; K8simpersonatedUser→AttrK8sImpersonation. Both genuinely extracted and bound by the engine (verified against its ingesters during review). This is the "after" state of the divergence demo — corroborated agent actions attribute to a named human instead of a faceless role.Needs (before merge) — platform IAM/RBAC
Documented in
docs/attribution.md:sts:AssumeRole+sts:SetSourceIdentity, withbedrock:InvokeModelin its permission policy.impersonateRBAC for the session SA, scoped to the operator user(s).Notes
AgentSandbox.spec), credential-TTL hard cliff (fail-safe), success-only records.🤖 Generated with Claude Code