{"id":17549824,"url":"https://github.com/datahangar/sfunnel","last_synced_at":"2025-08-26T11:33:49.287Z","repository":{"id":254471907,"uuid":"846609993","full_name":"datahangar/sfunnel","owner":"datahangar","description":"K8s service funneling using eBPF","archived":false,"fork":false,"pushed_at":"2025-08-12T21:32:11.000Z","size":350,"stargazers_count":6,"open_issues_count":10,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-12T23:28:53.229Z","etag":null,"topics":["affinity","clientip","clusterip","ebpf","funneling","k8s","kubernetes","loadbalancer","nodeport"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datahangar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.BSD-2-Clause","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-08-23T15:12:50.000Z","updated_at":"2025-01-04T14:34:48.000Z","dependencies_parsed_at":"2025-04-24T02:38:04.229Z","dependency_job_id":"190e9ce6-53b0-4bb6-abbe-efd6831d3222","html_url":"https://github.com/datahangar/sfunnel","commit_stats":null,"previous_names":["datahangar/sfunnel"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/datahangar/sfunnel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datahangar%2Fsfunnel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datahangar%2Fsfunnel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datahangar%2Fsfunnel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datahangar%2Fsfunnel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datahangar","download_url":"https://codeload.github.com/datahangar/sfunnel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datahangar%2Fsfunnel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272214419,"owners_count":24893201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-26T02:00:07.904Z","response_time":60,"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":["affinity","clientip","clusterip","ebpf","funneling","k8s","kubernetes","loadbalancer","nodeport"],"created_at":"2024-10-21T03:04:38.471Z","updated_at":"2025-08-26T11:33:49.268Z","avatar_url":"https://github.com/datahangar.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sfunnel: multi-port/multi-flow session affinity in Kubernetes\n\n`sfunnel` is an [eBPF](https://ebpf.io/) program designed to [funnel](docs/funneling.md)\nmultiple traffic flows through a single Kubernetes service _port_, ensuring\n[under certain conditions](#requirements) consistent `sessionAffinity: ClientIP`\naffinity across all _ports_ within the service.\n\nSee the original use-case [here](docs/use-cases/network-telemetry-nfacctd.md).\n\n:warning: `sfunnel` is still in an early development stage.\n\n:no_entry: severe performance degradation when funneling TCP over TCP/UDP is\nbeing investigated (GSO/TSO issues). Do not use it for real traffic.\n\n## At a glance\n\nExample where `TCP/8080` traffic is funneled through `TCP/80`.\n\nRemove _ports_ from the K8s service and e.g. deployment. Add the `sfunnel`\ncontainer along with the [rules](docs/rules.md) in `SFUNNEL_RULESET`:\n\n```diff\n--- a/service.yaml\n+++ b/service.yaml\n@@ -1,18 +1,12 @@\n apiVersion: v1\n kind: Service\n metadata:\n   name: my-loadbalancer-service\n spec:\n   type: LoadBalancer\n   selector:\n     app: my-nginx-app\n   ports:\n     - protocol: TCP\n       port: 80\n       targetPort: 80\n-    - protocol: TCP\n-      port: 8080\n-      targetPort: 8080\n   sessionAffinity: ClientIP\n```\n\n```diff\n--- a/nginx.yaml\n+++ b/nginx.yaml\n@@ -1,21 +1,31 @@\n apiVersion: apps/v1\n kind: Deployment\n metadata:\n   name: my-nginx-deployment\n spec:\n   replicas: 4\n   selector:\n     matchLabels:\n       app: my-nginx-app\n   template:\n     metadata:\n       labels:\n         app: my-nginx-app\n     spec:\n       containers:\n+        - name: sfunnel-init\n+          env:\n+            - name: SFUNNEL_RULESET\n+              value: ip tcp dport 80 sport 540 actions unfunnel tcp\n+          image: ghcr.io/datahangar/sfunnel:0.0.11@sha256:5f130c2bfc95fb0d264ad54c52b1fef26c58e5635f11b8b862efe611b98b1f9a\n+          securityContext:\n+            privileged: false #Set to true for some public clouds (e.g. GKE standard)\n+            capabilities:\n+              add: [BPF, NET_ADMIN, SYS_ADMIN]\n+          volumeMounts:\n+            - name: bpffs\n+              mountPath: /sys/fs/bpf\n+        - name: sfunnel-init-egress\n+          env:\n+            - name: SFUNNEL_RULESET\n+              value: ip tcp sport 8080 actions funnel tcp dport 540 sport 80\n+            - name: DIRECTION\n+              value: egress\n+          image: ghcr.io/datahangar/sfunnel:0.0.11@sha256:5f130c2bfc95fb0d264ad54c52b1fef26c58e5635f11b8b862efe611b98b1f9a\n+          securityContext:\n+            privileged: false #Set to true for some public clouds (e.g. GKE standard)\n+            capabilities:\n+              add: [BPF, NET_ADMIN, SYS_ADMIN]\n+          volumeMounts:\n+            - name: bpffs\n+              mountPath: /sys/fs/bpf\n         - name: nginx\n           image: nginx:latest\n           ports:\n             - containerPort: 80\n-            - containerPort: 8080\n+     volumes:\n+       - name: bpffs\n+         hostPath:\n+           path: /sys/fs/bpf\n```\n(_funneling HTTPs `TCP/443` through `TCP/80` would work the same way. Manifest\nis just too long for this example_)\n\nOn the other end (e.g. a Linux host, server etc..), deploy it with the\nmatching [rules](docs/rules.md):\n\n```shell\nIFACES=eth0 LB_IP=1.1.1.1 \\\nSFUNNEL_RULESET=\"ip daddr ${LB_IP} tcp dport 8080 actions funnel tcp dport 80 sport 540\" \\\ndocker run --privileged --network=host -it -e IFACES -e DIRECTION=\"egress\" -e SFUNNEL_RULESET ghcr.io/datahangar/sfunnel:0.0.11\n```\n\n```shell\nIFACES=eth0 LB_IP=1.1.1.1 \\\nSFUNNEL_RULESET=\"ip saddr ${LB_IP} tcp sport 80 dport 540 actions unfunnel tcp\" \\\ndocker run --privileged --network=host -it -e IFACES -e DIRECTION=\"ingress\" -e SFUNNEL_RULESET ghcr.io/datahangar/sfunnel:0.0.11\n```\n\nThe `sfunnel` container will run, load the eBPF code and finish its execution.\n\n## Support\n\n### Service types\n\n* `ClusterIP`: supported\n* `LoadBalancer`: supported\n* `NodePort`: _untested, but should work_\n\n\u003e :pencil: **Note**\n\u003e\n\u003e Currently `internalTrafficPolicy: Local` for `ClusterIP` and\n\u003e `externalTrafficPolicy: Local` for `NodePort` and `LoadBalancer` services are required.\n\n### Environments\n\n* **Google Kubernetes Engine(GKE)**: Standard cluster.\n   - Autopilot clusters are _not supported_ due to lack of eBPF support.\n* **MetalLB** with the following CNI plugins:\n  * Cilium\n  * Flannel\n  * Calico\n* **Dockerd**\n\n`sfunnel` should work on any environments supporting `sessionAffinity: ClientIP`.\nIf you encounter any issues or have successfully deployed it in other\nenvironments, please reach out so that we can update this list.\n\n## Requirements\n\n* [eBPF](https://ebpf.io/)-enabled kernel, with support for `clsact` and `direct-action`.\n* Proper [MTU configuration](docs/funneling.md#mtu) (20 bytes for TCP, 8 for UDP).\n* In Kubernetes:\n  * Privileged init container (`CAP_BPF`, `CAP_NET_ADMIN`, `CAP_SYS_ADMIN`)\n    * In some cloud providers (E.g. Google Cloud) `privileged=true` is required.\n* On the funneling side:\n  * Permissions to spawn `sfunnel` (same caps as before).\n  * Route or proxy traffic to be funneled. More on this [here](docs/funneling.md)\n\n## More...\n\n* [Use-cases](docs/use-cases/)\n* [Funneling?](docs/funneling.md)\n* [Rule syntax](docs/rules.md)\n* [sfunnel container](docs/container.md)\n* [Deploying it in K8s](docs/k8s.md)\n* [Next steps](../../issues?q=is%3Aissue+is%3Aopen+label%3Afeature)\n\nContact\n-------\n\nMarc Sune \u003c marcdevel (at) gmail (dot) com\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatahangar%2Fsfunnel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatahangar%2Fsfunnel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatahangar%2Fsfunnel/lists"}