Skip to main content

System Architecture

kguardian is designed as a distributed system: four core components (Controller, Broker, CLI, UI) plus an audit-mode evaluator (deployed by default) and an optional AI assistant (llm-bridge + mcp-server) work together to observe, analyze, and secure your Kubernetes workloads.

Components

Controller

Language: Rust + eBPF (C)Deployment: DaemonSet (one per node)Purpose: Observes kernel-level events

Broker

Language: Rust + Actix-webDeployment: Deployment with PostgreSQLPurpose: Stores and serves telemetry data

CLI

Language: GoDeployment: kubectl pluginPurpose: Generates security policies

UI

Language: React + TypeScriptDeployment: Web applicationPurpose: Visualizes network topology

Evaluator

Deployment: Deployment (on by default; disable with evaluator.enabled=false)Purpose: Replays observed flows against policies and records would-deny verdicts

AI Assistant (Optional)

Deployment: llm-bridge + mcp-server (opt-in via chart values)Purpose: Natural-language queries and conversational policy generation

Data Flow

1. eBPF Monitoring

The Controller attaches eBPF programs to kernel hooks on each node — fentry probes on tcp_v4_connect, tcp_set_state, udp_sendmsg, and tcp_retransmit_skb, a kprobe/kretprobe pair on inet_csk_accept, and the raw_syscalls/sys_enter tracepoint. When pods make network connections or execute syscalls, eBPF programs capture:
  • Source/destination IPs and ports
  • Protocol (TCP/UDP)
  • Syscall names and arguments
  • Container network namespace inode
  • Timestamp
eBPF runs in the kernel with low overhead, avoiding the need for sidecar proxies or agent injection.

2. Event Enrichment

The Controller’s userspace component:
  1. Receives events from eBPF via BPF ring buffers
  2. Queries containerd to map network namespace inodes to container IDs
  3. Matches containers to Kubernetes pods via the API server
  4. Enriches events with pod name, namespace, labels, and owner references

3. Data Transmission

Enriched events are sent to the Broker via HTTP POST:
  • Network traffic → /pod/traffic (batched via /pod/traffic/batch)
  • Pod metadata → /pod/spec
  • Syscall data → /pod/syscalls
  • Service mappings → /svc/spec
  • Pod deletions → /pod/mark_dead
The Controller batches events to reduce network overhead.

4. Storage & Indexing

The Broker stores all telemetry in PostgreSQL with indexes on:
  • Pod IP address
  • Pod name + namespace
  • Timestamp (for time-range queries)
  • Traffic type (INGRESS/EGRESS)
This enables fast lookups when generating policies.

5. Policy Generation

The CLI queries the Broker API:
  1. Fetches all traffic for a specific pod
  2. Identifies unique source/destination IPs
  3. Resolves IPs to pods/services via the Broker
  4. Groups traffic by protocol and port
  5. Deduplicates rules
  6. Generates K8s/Cilium NetworkPolicy YAML
For seccomp, it aggregates all observed syscalls and creates an allowlist.

6. Visualization

The UI fetches data from the Broker and renders:
  • An interactive graph of pod-to-pod communication (React Flow)
  • Tables of traffic details with filtering
  • Live view (polled, ~5s) — the UI re-fetches from the Broker on a short interval

Technology Stack

Controller (Rust + eBPF)

  • Memory safety without garbage collection (critical for low-level system programming)
  • Performance on par with C/C++ for userspace eBPF handling
  • libbpf-rs bindings provide excellent eBPF ergonomics
  • Tokio async runtime for efficient event processing
  • Written in C, compiled with clang -target bpf
  • Use libbpf-cargo to generate Rust skeleton bindings (.skel.rs)
  • Attach fentry probes to tcp_v4_connect, tcp_set_state, udp_sendmsg, and tcp_retransmit_skb; a kprobe/kretprobe pair to inet_csk_accept; and the raw_syscalls/sys_enter tracepoint
  • Use BPF maps for kernel ↔ userspace communication
  • kube-rs: Rust Kubernetes client for pod watching
  • Watches pods on the current node via field selector
  • Filters by excluded namespaces (kube-system, kguardian, etc.)
  • Handles pod lifecycle events (create, update, delete)

Broker (Rust + Actix-web + PostgreSQL)

  • Actix-web: High-performance HTTP server (thousands of req/sec)
  • RESTful API with JSON payloads
  • Async request handling with Tokio
  • Diesel ORM for type-safe SQL queries
Tables:
  • pod_details: Pod metadata (name, namespace, IP, labels, spec)
  • svc_details: Service metadata (name, namespace, cluster IP, selectors)
  • pod_traffic: Network connections (src/dst IP, port, protocol, type)
  • pod_syscalls: Syscall observations (pod, syscall names, count, architecture)
  • audit_verdicts: Evaluator would-deny verdicts for observed flows
  • install_info: Installation metadata
Indexes optimize queries by pod IP and name/namespace.
  • audit_verdicts are pruned automatically: broker.audit.retention.days (default 30), with intervalSeconds and batchSize controlling the pruning loop.
  • pod_traffic and pod_syscalls are retained indefinitely.
  • The cluster-wide GET /pod/traffic endpoint is capped to protect the broker: 5000 rows by default, up to 20000 via ?limit=.

CLI (Go)

  • kubectl plugin ecosystem: Natural fit for K8s tooling
  • client-go: Official Kubernetes Go client
  • cobra: Industry-standard CLI framework
  • Cross-platform binaries without dependencies
Network policies:
  1. Query /pod/traffic/{pod} from Broker
  2. For each unique peer IP, query /pod/ip/{ip} or /svc/ip/{ip}
  3. Build NetworkPolicyPeer with podSelector + namespaceSelector
  4. Group by direction (ingress/egress) and port/protocol
  5. Deduplicate rules via JSON marshaling comparison
  6. Generate YAML with sigs.k8s.io/yaml
  1. Query /pod/syscalls/{pod} from Broker
  2. Extract unique syscall names from aggregated data
  3. Group by architecture (x86_64, arm64, etc.)
  4. Generate JSON with defaultAction: SCMP_ACT_ERRNO
  5. Add syscalls array with action: SCMP_ACT_ALLOW

UI (React + TypeScript)

  • React 19: Modern UI framework
  • TypeScript: Type safety for complex state
  • Vite: Fast build tooling
  • TailwindCSS 4: Utility-first styling
  • React Flow: Interactive network graph rendering
  • Pods rendered as collapsible nodes (expand to show containers)
  • Edges show traffic direction with arrows
  • Namespace-based grouping with color coding
  • Interactive filtering by namespace, pod, traffic type
  • Traffic table with sorting and pagination
  • Pod details panel with labels and metadata
  • Live view (polled, ~5s) via the Broker API
  • Dark mode support (matches Cilium Hubble aesthetic)

Deployment Architecture

Standard Deployment (Helm)

High Availability

For production, configure:
  • Broker replicas: 2-3 for load balancing
  • PostgreSQL: Use managed service (RDS, Cloud SQL) or PostgreSQL operator with replication
  • Controller: Automatically HA via DaemonSet (one per node)
  • UI: 2+ replicas behind ingress/load balancer
The Controller must run on every node you want to monitor. Use node selectors or tolerations if you have specialized node pools.

Security Considerations

The Controller requires:
  • Privileged container with CAP_BPF added (to load eBPF programs)
  • hostNetwork: true (to access the node network namespace)
These are necessary for eBPF functionality but should be carefully reviewed in your security policy.
  • The Broker API is unauthenticated by default, with opt-in bearer-token auth via broker.auth (BROKER_AUTH_TOKEN); /health and /metrics stay exempt
  • The chart can also manage a NetworkPolicy restricting Broker access
  • For production, additionally consider:
    • Ingress with authentication (OAuth2 proxy, etc.)
    • mTLS between components
The PostgreSQL database contains:
  • Pod names, namespaces, labels
  • IP addresses and ports of communication
  • Syscall activity
This is metadata, not application data, but should still be protected. Use encryption at rest and in transit.

Performance Characteristics

The chart ships with these default resource requests and limits:
For measured numbers from a real deployment, see the reference deployment in the project README.

Scalability

  • Controllers: Scale linearly with node count (DaemonSet)
  • Broker: Stateless, can add replicas behind a load balancer
  • PostgreSQL: Vertical scaling or read replicas for high query loads
  • CLI: No scaling concerns (client-side)
  • UI: Horizontal scaling behind ingress
For larger clusters, consider:
  • Sharding data by namespace
  • Time-windowed queries
  • Data aggregation/rollup

Deep Dive: eBPF Monitoring

Learn how eBPF programs capture kernel events

Controller Implementation

Explore the Controller source code

Broker API Reference

View all API endpoints and schemas

Troubleshooting

Diagnose common architecture-related issues