{"id":49112905,"url":"https://github.com/false-systems/syva","last_synced_at":"2026-04-21T05:37:36.963Z","repository":{"id":349056723,"uuid":"1200893346","full_name":"false-systems/syva","owner":"false-systems","description":"Kernel-level eBPF enforcement for existing Kubernetes clusters. 7 LSM hooks watch every open, exec, kill, ptrace, and cgroup move — no runtime replacement needed.","archived":false,"fork":false,"pushed_at":"2026-04-12T23:27:28.000Z","size":141,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-12T23:27:42.295Z","etag":null,"topics":["bpf","container-security","containerd","containers","ebpf","isolation","kubernetes","linux-security","lsm","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/false-systems.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-04T00:30:43.000Z","updated_at":"2026-04-12T23:27:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/false-systems/syva","commit_stats":null,"previous_names":["false-systems/syva"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/false-systems/syva","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fsyva","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fsyva/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fsyva/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fsyva/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/false-systems","download_url":"https://codeload.github.com/false-systems/syva/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fsyva/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32079435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-21T02:38:07.213Z","status":"ssl_error","status_checked_at":"2026-04-21T02:38:06.559Z","response_time":128,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bpf","container-security","containerd","containers","ebpf","isolation","kubernetes","linux-security","lsm","rust"],"created_at":"2026-04-21T05:37:36.179Z","updated_at":"2026-04-21T05:37:36.955Z","avatar_url":"https://github.com/false-systems.png","language":"Rust","funding_links":[],"categories":["Sandboxing \u0026 Isolation"],"sub_categories":[],"readme":"# Syva\n\n**Your containers share a kernel. Syva makes the kernel enforce boundaries between them.**\n\nToday, container isolation is structural — namespaces and cgroups set up walls. But nothing checks whether Container A is accessing Container B's files, sending it signals, or attaching a debugger to it. The kernel doesn't know these containers shouldn't interact.\n\nSyva fixes this. It loads small programs into the kernel (via eBPF) that intercept security-sensitive operations — opening files, executing binaries, mapping executable memory, sending signals — and checks whether the caller is allowed to touch the target. If not, the operation is denied before it happens.\n\nNo sidecar. No proxy. No runtime replacement. Deploy the core engine per node, connect an adapter for your environment, done.\n\n\u003e *Syvä* (Finnish) — deep. Where enforcement happens.\n\n---\n\n## AI Agent Isolation\n\nAI agents run code. They make tool calls, spawn subprocesses, read files, and interact with APIs — all inside containers that share a kernel with everything else on the node. Namespaces don't stop an agent container from reading another container's files through bind mounts, sending signals to adjacent processes, or attaching a debugger to a database running next door.\n\nSyva enforces these boundaries in the kernel. Put your agent workloads in one zone, your production services in another, and the kernel blocks every cross-zone `open()`, `exec()`, `mmap()`, `ptrace()`, and `kill()` — before it happens, not after.\n\n```\n Node\n ═══════════════════════════════════════════════════\n\n   Zone: \"agent-sandbox\"         Zone: \"production\"\n  ┌─────────────────┐           ┌─────────────────┐\n  │                 │           │                 │\n  │   ai-agent      │           │   api-server    │\n  │   code-runner   │           │   postgres      │\n  │                 │           │                 │\n  └─────────────────┘           └─────────────────┘\n\n          │                              │\n          │   open(\"/db/data\") ──────X   │\n          │   exec(\"/usr/bin/pg_dump\") X │\n          │   ptrace(api_pid) ───────X   │\n          │   kill(postgres_pid, 9) ──X  │\n          │                              │\n          X = denied by Syva in the kernel\n```\n\nAn agent sandbox policy locks it down:\n\n```toml\n# agent-sandbox.toml\n\n[capabilities]\nallowed = [\"CAP_NET_BIND_SERVICE\"]\n\n[resources]\nmemory_limit = \"4Gi\"\npids_max = 256\n\n[network]\nmode = \"bridged\"\nallowed_zones = []    # no cross-zone communication\n\n[filesystem]\nwritable_paths = [\"/tmp\", \"/workspace\"]\nhost_paths = [\"/srv/agent/workspace\"]\n\n[syscalls]\ndeny = [\"mount\", \"umount2\", \"pivot_root\", \"ptrace\"]\n```\n\nNo `allowed_zones` means the agent zone is fully isolated — every cross-zone operation is denied at the kernel level. The agent can run arbitrary code inside its zone, but it cannot reach anything outside it.\n\n---\n\n## What Syva Does\n\nImagine two groups of containers on the same node:\n\n```\n Node\n ═══════════════════════════════════════════════════\n\n   Zone: \"frontend\"              Zone: \"database\"\n  ┌─────────────────┐           ┌─────────────────┐\n  │                 │           │                 │\n  │   nginx         │           │   postgres      │\n  │   react-app     │           │   redis         │\n  │                 │           │                 │\n  └─────────────────┘           └─────────────────┘\n\n          │                              │\n          │   open(\"/db/data\") ──────X   │\n          │   mmap(lib, PROT_EXEC) ──X   │\n          │   kill(pg_pid, 9) ───────X   │\n          │   ptrace(redis_pid) ─────X   │\n          │                              │\n          X = denied by Syva in the kernel\n```\n\nWithout Syva, these containers can interact through the shared kernel — read each other's files via `/proc`, send signals, attach debuggers, load each other's shared libraries. With Syva, every such operation hits a kernel checkpoint that verifies zone membership first.\n\n## How It Works\n\nSyva has two layers: a **core engine** that manages eBPF enforcement, and **adapters** that tell it what to enforce.\n\n```\n ┌──────────────┐  ┌──────────────┐  ┌──────────────┐\n │ syva-file    │  │ syva-k8s     │  │ syva-api     │\n │ TOML/ConfigMap│  │ CRD + Pods   │  │ REST API     │\n └──────┬───────┘  └──────┬───────┘  └──────┬───────┘\n        └─────────────────┼─────────────────┘\n                          │\n               gRPC / Unix socket\n                          │\n        ┌─────────────────▼─────────────────┐\n        │           syva-core               │\n        │   BPF maps + 7 LSM hooks          │\n        │   Health :9091 + Prometheus        │\n        └───────────────────────────────────┘\n```\n\n**The core** loads eBPF programs into the kernel and exposes a gRPC API over a Unix socket. It handles zone registration, container membership, cross-zone communication policy, and inode-level file enforcement. It never knows where commands came from.\n\n**Adapters** connect to the core and translate their domain into enforcement commands:\n- **syva-file** — reads TOML policy files, watches containerd for container events, hot-reloads on ConfigMap changes\n- **syva-k8s** — watches `SyvaZonePolicy` CRDs and Pod annotations via kube-rs\n- **syva-api** — exposes a REST API for programmatic zone management\n\n**Step 1: Label your containers.**\n\n```yaml\nmetadata:\n  annotations:\n    syva.dev/zone: \"frontend\"\n```\n\nOr define a CRD (with the k8s adapter):\n\n```yaml\napiVersion: syva.dev/v1alpha1\nkind: SyvaZonePolicy\nmetadata:\n  name: frontend\nspec:\n  network:\n    allowedZones: [\"database\"]\n  filesystem:\n    hostPaths: [\"/srv/frontend/static\"]\n```\n\n**Step 2: The kernel enforces.**\n\nEvery sensitive operation goes through Syva's kernel hooks:\n\n```\n Process in \"frontend\" zone calls open()\n      │\n      ▼\n Kernel hits the file_open hook\n      │\n      ▼\n Syva's eBPF program runs:\n   ┌─────────────────────────────────────┐\n   │ 1. What zone is the caller in?      │  → \"frontend\"\n   │ 2. What zone owns this file?        │  → \"database\"\n   │ 3. Are these zones allowed to talk?  │  → No\n   │ 4. DENY. Return -EACCES.            │\n   └─────────────────────────────────────┘\n      │\n      ▼\n Process gets \"Permission denied\"\n Event logged to ring buffer\n```\n\nThis check happens **inside the kernel**, on every call. No round-trip to userspace. No daemon in the path.\n\n## Mental Model\n\nSyva is a map-population system wrapped around a small set of kernel hooks.\n\nUserspace exists to keep a few BPF maps correct:\n\n- `ZONE_MEMBERSHIP` — which cgroup belongs to which zone\n- `ZONE_POLICY` — what a zone is allowed to do\n- `INODE_ZONE_MAP` — which inode belongs to which zone\n- `ZONE_ALLOWED_COMMS` — which zone pairs may cross boundaries\n\nThe kernel side exists to answer one question quickly:\n\n\u003e Caller zone, target zone, allowed or denied?\n\nThat gives Syva three layers:\n\n- **Adapters** discover external truth: TOML files, CRDs, Pods, REST requests, container lifecycle.\n- **syva-core** owns enforcement truth: zone lifecycle state and BPF map population.\n- **eBPF hooks** enforce at runtime: resolve caller, resolve target, consult maps, allow or deny.\n\nThe hot-path decision is always the same:\n\n1. Who is the caller?\n2. What is the target?\n3. What zone owns that target?\n4. Is this cross-zone access allowed?\n5. If not, deny and emit an event.\n\nIf you keep one idea in your head while reading the code, keep this one:\nSyva is not mainly a policy engine. It is a kernel-enforcement pipeline where\nadapters translate the world into zone state, `syva-core` turns that into BPF\nmap state, and the eBPF side turns that map state into allow/deny decisions on\nsensitive kernel operations.\n\n## Seven Kernel Hooks\n\nSyva intercepts seven operations. Together, they cover the main ways containers can interact through a shared kernel:\n\n```\n ┌──────────────────────────────────────────────────────────────────┐\n │                                                                  │\n │   open()              Can this process read/write this file?     │\n │                                                                  │\n │   exec()              Can this process run this binary?          │\n │                                                                  │\n │   mmap(PROT_EXEC)     Can this process map executable memory?    │\n │                                                                  │\n │   ptrace()            Can this process debug/inspect that one?   │\n │                                                                  │\n │   kill()              Can this process send a signal to that?    │\n │                                                                  │\n │   cgroup_attach()     Can this process escape its zone?          │\n │                                                                  │\n │   unix_connect()      Cross-zone Unix socket connections blocked  │\n │                                                                  │\n └──────────────────────────────────────────────────────────────────┘\n\n Each hook:  caller zone ──?── target zone\n             same zone → allow\n             different zone, no policy rule → DENY\n             different zone, explicit allow → allow\n```\n\nEvery deny is recorded with the caller PID, both zone IDs, and hook-specific context (file inode, target cgroup, ptrace mode, etc.).\n\n## Zone Policies\n\nEach `.toml` file in the policy directory defines one zone. The filename is the zone name — `agent-sandbox.toml` creates zone `agent-sandbox`.\n\n```toml\n# database.toml\n\n[capabilities]\nallowed = [\"CAP_NET_BIND_SERVICE\", \"CAP_CHOWN\"]\n\n[resources]\ncpu_shares = 1024\nmemory_limit = 536870912   # 512Mi in bytes\npids_max = 512\n\n[network]\nmode = \"bridged\"\nallowed_zones = [\"frontend\"]    # frontend can talk to database\n\n[filesystem]\nwritable_paths = [\"/data\", \"/tmp\", \"/var/log\"]\nhost_paths = [\"/srv/database/data\"]   # enforced via INODE_ZONE_MAP\n\n[syscalls]\ndeny = [\"mount\", \"umount2\", \"pivot_root\"]\n```\n\n**Default: deny everything.** Cross-zone communication only happens when both zones list each other in `allowed_zones`.\n\n**What Syva enforces vs declares:**\n\n| Policy field | Kernel enforcement |\n|---|---|\n| `network.allowed_zones` | **Enforced** — cross-zone file/exec/mmap/ptrace/signal blocked |\n| `filesystem.host_paths` | **Enforced** — inodes registered for file/exec/mmap hooks |\n| `POLICY_FLAG_ALLOW_PTRACE` | **Enforced** — intra-zone ptrace gated on `CAP_SYS_PTRACE` in capabilities |\n| `capabilities`, `resources`, `devices`, `syscalls`, `network.allowed_egress/ingress` | **Declarative only** — use seccomp, cgroups, NetworkPolicy |\n\nSyva logs a warning at startup for each declarative-only field that is configured.\n\n---\n\n## Installation\n\n### Prerequisites\n\n**Kernel requirements:**\n- Linux 6.1+ with `CONFIG_BPF_LSM=y`, `CONFIG_BPF_SYSCALL=y`, `CONFIG_DEBUG_INFO_BTF=y`\n- Boot parameter: `lsm=lockdown,capability,bpf`\n- BTF at `/sys/kernel/btf/vmlinux`\n- `pahole` is NOT required — kernel struct offsets are resolved directly from BTF at startup\n\n**Runtime requirements:**\n- containerd with Unix socket at `/run/containerd/containerd.sock` (configurable)\n- Root access or `CAP_BPF` + `CAP_SYS_ADMIN` + `CAP_PERFMON`\n\n### Building from Source\n\n```bash\n# Build all binaries (Linux only — aya uses Linux-specific libc)\ncargo build --release\n\n# Build the eBPF programs (requires nightly Rust)\ncargo run -p xtask -- build-ebpf --release\n\n# Binaries:\n#   target/release/syva-core       — enforcement engine\n#   target/release/syva-file       — file/ConfigMap adapter\n#   target/release/syva-k8s        — Kubernetes CRD adapter\n#   target/release/syva-api        — REST API adapter\n```\n\n### Deploy on Kubernetes — ConfigMap Policies\n\n```bash\nkubectl create namespace syva-system\n\nkubectl create configmap syva-policies \\\n  --from-file=frontend.toml=policies/frontend.toml \\\n  --from-file=database.toml=policies/database.toml \\\n  -n syva-system\n\nkubectl apply -f deploy/v0.2/daemonset-file.yaml\n```\n\nThis deploys two containers per node: `syva-core` (enforcement) + `syva-file` (policy adapter), sharing a Unix socket via `emptyDir`.\n\n### Deploy on Kubernetes — CRD Policies\n\n```bash\nkubectl apply -f deploy/v0.2/daemonset-k8s.yaml\n\n# Define a zone via CRD\nkubectl apply -f - \u003c\u003cEOF\napiVersion: syva.dev/v1alpha1\nkind: SyvaZonePolicy\nmetadata:\n  name: frontend\nspec:\n  network:\n    allowedZones: [\"database\"]\n  filesystem:\n    hostPaths: [\"/srv/frontend/static\"]\nEOF\n```\n\nLabel your pods with `syva.dev/zone: \"frontend\"`. Containers without the annotation are not enforced.\n\n### Deploy Standalone\n\n```bash\n# Terminal 1: Start the core engine\nsyva-core --socket-path /run/syva/syva-core.sock\n\n# Terminal 2: Start the file adapter\nsyva-file --policy-dir /etc/syva/policies --socket-path /run/syva/syva-core.sock\n```\n\n### Verify\n\n```bash\n# Check enforcement status\nkubectl exec -n syva-system -c syva-core daemonset/syva -- syva-core status\n\n# Validate policies without loading BPF\nsyva-file verify --policy-dir ./policies\n```\n\n---\n\n## Observability\n\nEvery deny is emitted as a structured event:\n\n```\nDENY hook=file_open pid=1847 caller_zone=2 target_zone=3 context=8421376\nDENY hook=ptrace    pid=992  caller_zone=1 target_zone=2 context=4\n```\n\nStream events in real time:\n\n```bash\nsyva-core events --follow\nsyva-core events --follow --format json    # ndjson output\n```\n\nEvents come from a BPF ring buffer (4MB, ~75K events). Allows are tracked in per-CPU counters only — no per-event overhead for the common path. Lost events (ring buffer overflow) are counted per-hook and shown in `syva-core status`.\n\nPrometheus metrics are available at `:9091/metrics` — per-hook allow/deny/error/lost counters, plus agent health gauges. Enforcement errors (kernel struct read failures) are monitored every 30 seconds.\n\n## How Syva Handles Kernel Differences\n\neBPF programs read kernel struct fields (`task_struct-\u003ecgroups`, `file-\u003ef_inode`). The byte offset of these fields changes between kernel versions. Syva handles this:\n\n```\n Startup\n    │\n    ├─ Parse /sys/kernel/btf/vmlinux for kernel struct offsets\n    ├─ Inject offsets into eBPF programs as globals\n    ├─ Load programs (verified by kernel, not yet attached)\n    │\n    ├─ Attach all 7 hooks (enforcement begins)\n    ├─ Adapters connect and populate BPF maps via gRPC\n    │\n    ├─ Three self-tests validate offset chains:\n    │   ├─ Cgroup: BPF helper vs offset chain\n    │   ├─ Inode: BPF-derived inode vs stat()\n    │   └─ Unix socket: peer cgroup resolution\n    │\n    ├─ All pass? → enforcement active, drop CAP_SYS_ADMIN\n    └─ Any fail? → refuse to start (no silent failure)\n```\n\nIf BTF is unavailable, defaults for Linux 6.1+ are used (self-tests validate correctness).\n\n## Architecture\n\n```\n ┌────────────────┐  ┌────────────────┐  ┌────────────────┐\n │   syva-file    │  │   syva-k8s     │  │   syva-api     │\n │ TOML/ConfigMap │  │  CRD + Pods    │  │   REST API     │\n └───────┬────────┘  └───────┬────────┘  └───────┬────────┘\n         └──────────────────┬┘───────────────────┘\n                            │\n                 gRPC / Unix socket\n                 /run/syva/syva-core.sock\n                            │\n         ┌──────────────────▼──────────────────┐\n         │            syva-core                │\n         │   ZoneRegistry + BPF map mgmt       │\n         │   Health :9091 + Prometheus          │\n         │   gRPC server (10 RPCs)             │\n         └──────────────────┬──────────────────┘\n                            │\n ┌──────────────────────────▼──────────────────────────────┐\n │                      Linux kernel                       │\n │                                                         │\n │  ZONE_MEMBERSHIP    cgroup_id → zone    (who is where)  │\n │  ZONE_POLICY        zone → caps, flags  (Array, O(1))   │\n │  INODE_ZONE_MAP     inode → zone        (NO_PREALLOC)   │\n │  ZONE_ALLOWED_COMMS (zone, zone) → ok   (who can talk)  │\n │  ENFORCEMENT_EVENTS ring buffer, 4MB    (what happened)  │\n │                                                         │\n │  7 LSM hooks: open/exec/mmap/kill/ptrace/cgroup/unix    │\n └─────────────────────────────────────────────────────────┘\n```\n\n| Crate | Binary | What it does |\n|-------|--------|------------|\n| `syva-core` | `syva-core` | Enforcement engine. Loads eBPF, serves gRPC, health/metrics. |\n| `syva-adapter-file` | `syva-file` | Reads TOML policies, watches containerd, hot-reloads. |\n| `syva-adapter-k8s` | `syva-k8s` | Watches SyvaZonePolicy CRDs and Pod annotations. |\n| `syva-adapter-api` | `syva-api` | REST API for programmatic zone management. |\n| `syva-proto` | — | gRPC contract between core and adapters. |\n| `syva-ebpf` | — | 7 kernel programs. Separate build, `bpfel-unknown-none`. |\n| `syva-ebpf-common` | — | `#[repr(C)]` types shared between kernel and userspace. |\n\n## Building\n\n```bash\ncargo build --release              # all binaries (Linux only)\ncargo run -p xtask -- build-ebpf   # kernel programs (requires nightly Rust)\ncargo test                         # all tests\n```\n\n**Requires:** Linux 6.1+, `CONFIG_BPF_LSM=y`, `CONFIG_DEBUG_INFO_BTF=y`, boot with `lsm=lockdown,capability,bpf`.\n\n## Limitations\n\n**Software enforcement, not hardware.** Syva uses BPF maps in kernel memory. It's defense-in-depth, not a hypervisor boundary. A kernel exploit could bypass it.\n\n**Additive only.** eBPF LSM hooks can deny but never override existing MAC policy (SELinux, AppArmor). Syva stacks on top.\n\n**Policy hot-reload.** Policies are polled every 5 seconds. ConfigMap updates are detected via symlink rotation. Zones removed while containers are running transition to draining state — enforcement continues until the last container leaves.\n\n**One instance per node.** Syva pins BPF maps at `/sys/fs/bpf/syva/`. A second instance will refuse to start. The DaemonSet init container cleans stale pins automatically.\n\n**Fail-open on errors.** Kernel struct read failures allow the operation (with error counter increment). This is defense-in-depth — a read failure shouldn't block the system. Errors are monitored and surfaced in `syva status`.\n\n**Declarative-only fields.** Capabilities, resources, devices, syscalls, and network egress/ingress in policy TOML are parsed but not enforced by Syva's eBPF hooks. Use seccomp, cgroups, and NetworkPolicy for those. Syva warns at startup about configured-but-not-enforced fields.\n\n**/proc access not controlled.** Virtual filesystem inodes are not in `INODE_ZONE_MAP`. Cross-zone `/proc/\u003cpid\u003e/mem` access is possible. Deferred to a future release.\n\n**Enforcement gap during rolling update.** During a DaemonSet rolling update, the init container removes old BPF pins before the new agent starts. Between pin removal and new hook attachment (10-30 seconds), all LSM hooks are inactive. Plan upgrades during maintenance windows or accept the gap as a tradeoff for clean restart.\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffalse-systems%2Fsyva","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffalse-systems%2Fsyva","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffalse-systems%2Fsyva/lists"}