Identity
Identity is what turns 192.168.10.42 called api.openai.com into dsmith (group: engineering) called api.openai.com. It's what lets a rule read allow group:engineering → api.anthropic.com instead of a list of IPs, and what makes the audit trail name people. For the model, see Identity model.
How attribution works
The collector resolves the source IP of each request to a user and their directory groups, using a mapping you feed it. With no identity source wired, requests attribute to the IP (and agent), which still works — you just don't get user/group rules or names.
Sources
You can populate the IP → user → groups map from any of these:
- Active Directory — an adapter that watches logon events / DHCP-AD correlation so the map stays current as users move between machines.
- DHCP — map leases to users where your DHCP data carries that.
- CSV import — a static
ip,user,groupsupload for fixed assignments (servers, kiosks, lab machines). The simplest way to start.
Manage mappings on the Identity page; the CSV importer and the manual override live there. For the API, see Identities and DHCP leases.
Getting started
- Start with a CSV for the machines you already know (servers, shared boxes, a pilot group). Immediate value, no integration.
- Wire a live source (AD/DHCP) once you want dynamic, fleet-wide attribution that follows users.
- Write group rules once attribution is flowing — now
group:anduser:conditions in policy resolve.
Why it matters for policy and audit
- Policy gets expressive and stable: rules keyed on groups survive DHCP churn and re-imaging, where IP-based rules rot.
- Audit gets accountable: the Traffic and Audit logs name the person, which is what an incident responder and an auditor actually need.
Workload identity — co-located agents (eBPF host sensor)
IP → user attribution breaks down when many agents share one source IP — a VM or pod running hundreds of AI agents all egress from the same address. For that, Kilasec adds a per-flow layer: the host sensor, a small agent that runs on each agent host (Kubernetes DaemonSet or a systemd unit on a VM) and uses eBPF — via Tetragon — to record which process/container opened each outbound connection.
The sensor pushes (source IP, source port) → {workload, container, pod, labels} to the cloud with a short TTL. The collector records each request's source port as well as its IP, so the who-is lookup joins the flow to the exact process behind it — kernel-observed, no app changes, and not spoofable the way a User-Agent or self-reported agent name is.
What you get once the sensor is running:
- Traffic Log and Live Traffic show the owning workload (process/pod) under the source IP for flows evaluated against workload rules.
- Policy can match it:
workload:(the process binary's full path, or the pod / container name) andworkload_labels:(key=valueKubernetes pod labels, ANY-of). In the rule editor these live on the Source tab.
The binary is matched by full path on purpose: /usr/local/bin/agentd and a rogue /tmp/agentd must not satisfy the same rule. In Kubernetes, prefer matching the pod or container name — those are the stable handles.
- name: payments_bot_only_anthropic
match:
workload: [payments-7f9] # pod name; or /usr/local/bin/payments-bot
destination:
host: ["*.anthropic.com"]
action: allow
- name: prod_workloads_no_shadow_ai
match:
workload_labels: [env=prod]
destination_is_unknown_ai: true
action: denyFail-closed, and what that means for "unattributed" traffic. A flow with no attribution never matches a workload rule — the rule is skipped, and the flow falls through to the rules below it. A workload rule therefore cannot be used to catch unattributed traffic. To constrain that, put a catch-all rule (no workload predicate) after your workload allows, e.g. destination_is_ai_provider: true → require_approval — attributed-and-allowed flows match above it, everything else lands on it.
Network path requirement (NAT breaks the join). The sensor records the connection tuple as the kernel sees it on the agent host — for a pod, that's the pod IP and original ephemeral port. The collector must observe the same tuple, or the join silently never matches (workload stays empty, workload rules stay inert). In Kubernetes, run the collector in-cluster (pod-to-pod traffic keeps pod IPs on standard CNIs); if agents reach a collector outside the cluster, their egress is usually SNAT-masqueraded to the node IP and attribution will not resolve. Same caveat for any NAT device between agent hosts and the collector.
Deploy recipes (Tetragon helm values, the tcp_connect tracing policy, the DaemonSet, and a standalone systemd unit) ship in the agents bundle under kilasec-agents/hostsensor/deploy/ — see its README for the four-command Kubernetes walkthrough. Where eBPF can't run (managed serverless like Lambda/Fargate), use in-agent identity below.
In-agent identity: the kilasec_agent package and identity tokens
Network-side attribution answers which host / container / binary sent a request. It cannot answer which of the fifty logical agents inside one orchestrator process sent it, and it cannot run at all in managed serverless. For those cases the identity comes from inside the agent process: the kilasec_agent Python package (in the agents bundle under kilasec-agents/pyagent/) tags every outbound HTTP request the process makes with the agent's name — and, optionally, a Kilasec-issued identity token. The collector consumes and strips both headers before anything is forwarded upstream; they never leave your network.
Three ways to deploy it, in increasing order of zero-touch:
- One line in the agent:
import kilasec_agent; kilasec_agent.init("billing-reconciler") - One import, config from env:
import kilasec_agentwithKILASEC_AGENT=billing-reconcilerset. - No code changes at all: set
PYTHONPATH=/opt/kilasec-agents/pyagentplus the same env vars in the systemd unit / Dockerfile / K8s manifest / Lambda layer — Python'ssitecustomizehook loads the package before the agent's own code runs. This is how you tag agents whose source you don't own.
Identity tokens (verified identity). A name alone is self-declared — any process can claim it. Tokens close that gap: mint one per agent on Identity → Agent identity tokens, hand it to the agent as KILASEC_AGENT_TOKEN (it's shown once — store it like a secret). The agent sends it on every request; the collector strips it and verifies it against the cloud. A valid token outranks every inferred signal — User-Agent, DHCP, even the self-declared name header — and the audit event records agent_source: token plus the token's fingerprint. Revoking a token on the Identity page takes effect at collectors within ~60 seconds.
An invalid or revoked token never blocks traffic: the flow just falls back to inferred attribution and the event is flagged agent_token_invalid — alert on that flag to catch stale or stolen tokens.
Tokens are attribution credentials, not access credentials — they grant nothing outside Kilasec. If some of the agent's destinations bypass the collector, scope injection with KILASEC_INJECT_HOSTS / KILASEC_INJECT_EXCLUDE so the token only rides on collector-inspected routes. Full env-var reference and framework recipes are in the package README (kilasec-agents/pyagent/README.md).