{"id":49521782,"url":"https://github.com/spinningfactory/kloak","last_synced_at":"2026-05-02T00:01:25.156Z","repository":{"id":353456342,"uuid":"1152263249","full_name":"spinningfactory/kloak","owner":"spinningfactory","description":"Cloud native zero trust security for AI agents run environments","archived":false,"fork":false,"pushed_at":"2026-04-24T02:15:10.000Z","size":29215,"stargazers_count":2,"open_issues_count":21,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-24T02:23:22.758Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spinningfactory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"MAINTAINERS.md","copyright":null,"agents":null,"dco":null,"cla":"CLA.md"}},"created_at":"2026-02-07T16:08:49.000Z","updated_at":"2026-04-24T02:07:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/spinningfactory/kloak","commit_stats":null,"previous_names":["spinningfactory/kloak"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/spinningfactory/kloak","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spinningfactory%2Fkloak","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spinningfactory%2Fkloak/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spinningfactory%2Fkloak/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spinningfactory%2Fkloak/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spinningfactory","download_url":"https://codeload.github.com/spinningfactory/kloak/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spinningfactory%2Fkloak/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32517232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-05-02T00:00:50.104Z","updated_at":"2026-05-02T00:01:25.146Z","avatar_url":"https://github.com/spinningfactory.png","language":"C","funding_links":[],"categories":["kubernetes","Secrets Management \u0026 Isolation","Secret Management"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://getkloak.io/\"\u003e\u003cimg src=\"transparent-logo.svg\" alt=\"Kloak Logo\" width=\"120\"/\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003ch1 align=\"center\"\u003e\u003ca href=\"https://getkloak.io/\"\u003eKloak\u003c/a\u003e\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eSecure Your Secrets, Agentless\u003c/b\u003e\u003cbr\u003e\n  Kubernetes eBPF HTTPS interceptor. Transparent secret injection without application changes or sidecars.\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![CI](https://github.com/spinningfactory/kloak/actions/workflows/ci.yml/badge.svg)](https://github.com/spinningfactory/kloak/actions/workflows/ci.yml)\n[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/dhiaayachi/2d4be46e60ee478d5703d8b009dbd6e9/raw/kloak-coverage.json)](https://gist.github.com/dhiaayachi/2d4be46e60ee478d5703d8b009dbd6e9)\n[![Go Version](https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go)](https://go.dev/)\n[![Kubernetes](https://img.shields.io/badge/Kubernetes-1.28+-326CE5?logo=kubernetes)](https://kubernetes.io/)\n[![License](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](LICENSE)\n\n\u003c/div\u003e\n\n---\n\nKloak transparently intercepts outbound TLS traffic in Kubernetes using eBPF uprobes, replacing hashed placeholders with real secrets at the kernel level before encryption. Applications never handle actual credentials, and no sidecars or code changes are required.\n\n## Features\n\n- **No code changes** -- No SDK, no library, no application modifications. Mount a secret, make HTTPS requests, and Kloak handles the rest.\n- **Secret isolation** -- Applications only see hashed shadow values (`kloak:\u003cUUID\u003e`). Real secrets exist solely in eBPF maps and are injected in-kernel at TLS write time.\n- **Zero overhead** -- eBPF uprobes operate in kernel space with negligible latency impact. No userspace proxy or sidecar in the data path.\n- **Kubernetes native** -- Works with standard Kubernetes Secrets. Enable with a single label.\n- **Host and IP filtering** -- Secrets annotated with `getkloak.io/hosts` are only sent to specific destination hostnames or IP addresses, preventing exfiltration to unauthorized servers.\n- **Port-based filtering** -- Secrets annotated with `getkloak.io/port` are restricted to connections on a specific destination port.\n- **Broad runtime support** -- Hooks into OpenSSL, BoringSSL, and Go's native `crypto/tls`. Works with Python, Node.js, Go, Rust, Ruby, PHP, curl, and any OpenSSL-linked runtime.\n\n## Quick Start\n\n### Prerequisites\n\n- Kubernetes cluster (1.28+) with Linux kernel 5.17+\n- [Helm](https://helm.sh/docs/intro/install/) 3.12+\n- `kubectl` configured with cluster access\n\n### Install with Helm\n\n```bash\nhelm repo add kloak https://chart.getkloak.io\nhelm repo update\n\nhelm install kloak kloak/kloak -n kloak-system --create-namespace\n\nkubectl get pods -n kloak-system\n```\n\n### Labels and Annotations\n\n| Key | Type | Scope | Description |\n|-----|------|-------|-------------|\n| `getkloak.io/enabled=true` | Label | Secret, Namespace, Pod | Enables Kloak for the target resource. On namespaces and pods, controls webhook scope. On secrets, triggers shadow secret creation. |\n| `getkloak.io/hosts=host` | Annotation | Secret | Restricts which destination host or IP address a secret can be sent to (single value only) |\n| `getkloak.io/port=443` | Annotation | Secret | Restricts which destination port a secret can be sent to |\n| `getkloak.io/managed=true` | Label | Secret | Marks shadow secrets created by Kloak (do not set manually) |\n\n### Try the Demo\n\n```bash\n# Full demo: creates a Lima VM with K3s, deploys Kloak and sample apps\n./examples/setup-demo.sh\n\nexport KUBECONFIG=/tmp/kloak-k3s.yaml\n\n# View logs (should show real secrets in httpbin.org responses)\nkubectl logs -f -l app=demo-python -n kloak-demo -c demo-app\n\n# Cleanup\n./examples/destroy-demo.sh\n```\n\n## Architecture\n\nKloak consists of a control plane (controller + webhook) and an eBPF data plane that runs entirely in kernel space.\n\n```mermaid\ngraph TD\n    subgraph Control_Plane [Control Plane]\n        C[Kloak Controller - DaemonSet]\n        W[Mutating Webhook - Deployment]\n    end\n\n    subgraph Data_Plane [Data Plane - eBPF in Kernel]\n        UP[TLS Uprobes]\n        P2[Phase 2 Rewrite]\n        XOR[XOR Path]\n        KP[DNS Kprobe/Kretprobe]\n        TP[Connect/Close Tracepoints]\n        PROC[Process Tracepoints]\n        TCP[tcp_sendmsg Kprobe]\n        TC[TC Egress Patch]\n        GHASH[TC GHASH Update]\n    end\n\n    subgraph App [Application]\n        P[Pod with Shadow Secret]\n    end\n\n    C --\u003e|Creates shadow secrets and syncs BPF maps| UP\n    C --\u003e|Attaches probes to container processes| UP\n    W --\u003e|Rewrites volume mounts to shadow secrets| P\n    P --\u003e|TLS write with kloak:UUID| UP\n    UP --\u003e|Tail call: plaintext rewrite| P2\n    UP --\u003e|Tail call: ciphertext rewrite| XOR\n    XOR --\u003e|Stores xor_pending| TCP\n    TCP --\u003e|Stores tc_pending| TC\n    TC --\u003e|Tail call| GHASH\n    P2 --\u003e|Real secret injected| Internet\n    TC --\u003e|Patched ciphertext| Internet\n    KP --\u003e|Populates dns_ip_map| UP\n    TP --\u003e|Populates conn_ip_map / last_verified_fd| UP\n    PROC --\u003e|Tracks process lifecycle| C\n```\n\n### Components\n\n| Component | Description |\n|-----------|-------------|\n| **Controller** (DaemonSet) | Watches Secrets labeled `getkloak.io/enabled=true`, creates shadow secrets with length-matched `kloak:\u003cUUID\u003e` placeholders, syncs real values into eBPF maps, and attaches TLS uprobes to container processes via cgroup discovery. |\n| **Webhook** (Deployment) | Mutating admission webhook that intercepts Pod creation. Rewrites Secret volume mounts to point to shadow secrets. Evaluates enablement through pod labels or namespace labels. Rejects pods if the shadow secret has not been created yet (fail-closed). Two webhook entries ensure only kloak-enabled namespaces and pods are affected; non-kloak workloads are never impacted, even when the webhook is down. |\n| **TLS Uprobes** | Attach to `SSL_write` / `SSL_write_ex` (OpenSSL/BoringSSL) and `crypto/tls.(*Conn).Write` (Go native). Intercept outbound TLS writes, scan for `kloak:` prefixes. Two rewrite paths: Phase 2 for plaintext rewrite (before encryption), and XOR path for ciphertext patching (after encryption). |\n| **XOR Path + TC Egress** | For Go native TLS: computes XOR diff in the uprobe, bridges through `tcp_sendmsg` kprobe to TC egress, which patches the encrypted packet in-flight and recomputes the GHASH authentication tag via a tail call to `tc_ghash_update`. |\n| **DNS Kprobe** | Kprobe/kretprobe on `udp_recvmsg` captures DNS responses system-wide. Parses A/AAAA records for watched hostnames and populates `dns_ip_map` (IP to hostname) with TTL tracking. |\n| **Connect/Close Tracepoints** | Hooks `sys_enter/exit_connect` to track TCP connections (fd to destination IP in `conn_ip_map`). When the destination matches a DNS-verified hostname, caches the fd in `last_verified_fd`. Hooks `sys_enter_close` to clean up stale entries. |\n| **Process Tracepoints** | Hooks `sched_process_exec` and `sched_process_exit` to track container process lifecycle for uprobe attachment and cleanup. |\n\n### Supported Runtimes\n\n| Runtime | TLS Library | Hook Point |\n|---------|------------|------------|\n| Python, Rust, Ruby, PHP, curl | OpenSSL (libssl.so) | `SSL_write` / `SSL_write_ex` uprobe |\n| Node.js | BoringSSL (statically linked) | `SSL_write` uprobe |\n| Go | crypto/tls (native) | `crypto/tls.(*Conn).Write` uprobe |\n\n## DNS-Verified Trust Chain\n\nSecrets with `getkloak.io/hosts` annotations are only rewritten when the destination is verified through the full DNS resolution chain. This prevents secret exfiltration to unauthorized servers, even if an application is compromised.\n\n```mermaid\nsequenceDiagram\n    participant App as Application\n    participant DNS as DNS Server\n    participant KP as eBPF kprobe\n    participant TP as eBPF tracepoint\n    participant UP as eBPF uprobe\n    participant P2 as Phase 2 Rewrite\n    participant Srv as api.stripe.com\n\n    Note over App,Srv: 1. DNS Resolution\n    App-\u003e\u003eDNS: resolve api.stripe.com\n    DNS--\u003e\u003eApp: A 52.55.108.115\n    KP-\u003e\u003eKP: dns_ip_map[52.55.108.115] = api.stripe.com\n\n    Note over App,Srv: 2. TCP Connect\n    App-\u003e\u003eSrv: connect(fd=7, 52.55.108.115:443)\n    TP-\u003e\u003eTP: conn_ip_map[tgid,fd=7] = 52.55.108.115\n    TP-\u003e\u003eTP: IP in dns_ip_map -\u003e mark fd as verified\n\n    Note over App,Srv: 3. TLS Write (Allowed)\n    App-\u003e\u003eUP: SSL_write with kloak:a1b2c3d4\n    UP-\u003e\u003eUP: resolve_host -\u003e api.stripe.com\n    UP-\u003e\u003eP2: Tail call\n    P2-\u003e\u003eP2: allowed_host matches -\u003e rewrite secret\n    P2-\u003e\u003eSrv: Real secret sent to api.stripe.com\n\n    Note over App,Srv: 4. TLS Write (Blocked)\n    App-\u003e\u003eUP: SSL_write to evil.com with kloak:a1b2c3d4\n    UP-\u003e\u003eUP: resolve_host -\u003e evil.com\n    UP-\u003e\u003eP2: Tail call\n    P2-\u003e\u003eP2: allowed_host mismatch -\u003e BLOCKED\n    P2--xApp: Placeholder sent as-is\n```\n\n### How Host Verification Works\n\n1. **DNS capture** -- A kprobe on `udp_recvmsg` intercepts DNS responses on the node. For hostnames listed in `getkloak.io/hosts`, resolved IPs are stored in `dns_ip_map` with TTL tracking.\n\n2. **Connection tracking** -- Tracepoints on `sys_enter/exit_connect` record each TCP connection's fd-to-destination-IP mapping in `conn_ip_map`. If the destination IP exists in `dns_ip_map`, the fd is marked as verified.\n\n3. **Host resolution** -- At `SSL_write` time, `resolve_host()` chains through verified fd, `conn_ip_map`, and `dns_ip_map` to determine the hostname of the current TLS connection.\n\n4. **Secret filtering** -- Phase 2 compares the resolved hostname against the secret's `allowed_host`. Match: rewrite. Mismatch: placeholder sent as-is, keeping the secret safe.\n\n5. **TTL enforcement** -- DNS entries include a TTL. Expired entries are skipped on lookup, requiring re-verification through fresh DNS responses.\n\n## How It Works\n\n### 1. Label and Annotate Your Secrets\n\nAdd `getkloak.io/enabled=true` as a label to enable Kloak. Use annotations for host and port filtering. Kloak generates a shadow secret with `kloak:\u003cUUID\u003e` placeholders that are length-matched to the original values.\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: api-credentials\n  labels:\n    getkloak.io/enabled: \"true\"\n  annotations:\n    getkloak.io/hosts: \"api.stripe.com\"\n    getkloak.io/port: \"443\"\ndata:\n  api-key: c2stbGl2ZS14eXoxMjM=  # sk-live-xyz123\n```\n\n### 2. Deploy Your Application\n\nThe webhook automatically rewrites volume mounts to use the shadow secret. Your application sees only `kloak:\u003cUUID\u003e` placeholders and never handles real credentials.\n\n```\n# What the application reads from the mounted secret:\nkloak:a1b2c3d4-e5f6-7890\n```\n\n### 3. Automatic In-Kernel Rewrite\n\nWhen the application makes an outbound HTTPS request, the eBPF uprobe intercepts the TLS write, verifies the destination through the DNS trust chain, and replaces the placeholder with the real secret before encryption.\n\n```\n# What the application writes:\nAuthorization: Bearer kloak:a1b2c3d4-e5f6-7890\n\n# What leaves the node (after eBPF rewrite):\nAuthorization: Bearer sk-live-xyz123\n```\n\n## Development\n\n```bash\nmake build          # Build the kloak binary\nmake test           # Run all tests\nmake docker-build   # Build Docker image\n```\n\neBPF development requires Linux. On macOS, Kloak uses Lima VMs:\n\n```bash\nmake lima-start      # Start Lima VM\nmake generate-ebpf   # Generate eBPF Go bindings\nmake test-linux      # Run tests in Linux VM\nmake lima-shell      # Shell into VM\n```\n\n## License\n\nAGPL-3.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspinningfactory%2Fkloak","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspinningfactory%2Fkloak","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspinningfactory%2Fkloak/lists"}