-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# sfunnel: multi-flow K8s pod session affinity | ||
|
||
`sfunnel` is an [eBPF](https://ebpf.io/)-based tool desgined to [_funnel_](docs/funneling) | ||
multiple traffic flows through a single [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) | ||
_port_, ensuring - under [certain conditions](#requirements) - consistent `ClientIP` | ||
affinity across all _ports_ within the service. | ||
|
||
See the original use-case [here](docs/use-cases/network-telemetry-nfacctd.md). | ||
|
||
## At a glance | ||
|
||
Example where `TCP/8080` and `TCP/443` traffic is funneled through `TCP/80`. | ||
|
||
|
||
Remove _ports_ from the K8s service and e.g. deployment. Add the `sfunnel` | ||
container along with the [rules](docs/rules.md) in `SFUNNEL_RULESET`: | ||
|
||
```diff | ||
--- a/service.yaml | ||
+++ b/service.yaml | ||
@@ -1,18 +1,12 @@ | ||
apiVersion: v1 | ||
kind: Service | ||
metadata: | ||
name: my-loadbalancer-service | ||
spec: | ||
type: LoadBalancer | ||
selector: | ||
app: my-nginx-app | ||
ports: | ||
- protocol: TCP | ||
port: 80 | ||
targetPort: 80 | ||
- - protocol: TCP | ||
- port: 8080 | ||
- targetPort: 8080 | ||
- - protocol: TCP | ||
- port: 443 | ||
- targetPort: 443 | ||
``` | ||
|
||
```diff | ||
--- a/nginx.yaml | ||
+++ b/nginx.yaml | ||
@@ -1,21 +1,31 @@ | ||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
name: my-nginx-deployment | ||
spec: | ||
replicas: 4 | ||
selector: | ||
matchLabels: | ||
app: my-nginx-app | ||
template: | ||
metadata: | ||
labels: | ||
app: my-nginx-app | ||
spec: | ||
containers: | ||
+ - name: sfunnel-init | ||
+ env: | ||
+ - name: SFUNNEL_RULESET | ||
+ value: ip tcp dport 80 sport 540 actions unfunnel tcp | ||
+ image: ghcr.io/datahangar/sfunnel:0.0.3 | ||
+ securityContext: | ||
+ privileged: true | ||
+ capabilities: | ||
+ add: [BPF, NET_ADMIN] | ||
+ volumeMounts: | ||
+ - name: bpffs | ||
+ mountPath: /sys/fs/bpf | ||
- name: nginx | ||
image: nginx:latest | ||
ports: | ||
- containerPort: 80 | ||
- - containerPort: 8080 | ||
- - containerPort: 443 | ||
|
||
``` | ||
|
||
On the other end (e.g. a Linux host, server etc..), deploy it with the | ||
matching [rules](docs/rules.md): | ||
|
||
```shell | ||
SFUNNEL_RULESET="ip daddr <your LB IP1> tcp port 443 actions funnel tcp dport 80 sport 540;\ | ||
ip daddr <your LB IP1> tcp port 8080 actions funnel tcp dport 80 sport 540" | ||
docker run --network="host" --privileged -e SFUNNEL_RULESET="$SFUNNEL_RULESET" sfunnel | ||
``` | ||
|
||
The `sfunnel` container will run and load the eBPF code. | ||
|
||
##### More use-cases | ||
|
||
This is a simple example yet not very useful example. See [use-cases](docs/use-cases/) | ||
for real world examples. | ||
|
||
## Requirements | ||
|
||
* In Kubernetes: | ||
* Permissions to spawn containers with `BPF` and `NET_ADMIN` capabilities. | ||
* [eBPF](https://ebpf.io/)-enabled kernel, with support for `clsact` and `direct-action`. | ||
* Proper MTU configuration (20 bytes for TCP, 8 for UDP). | ||
* On the funneling side: | ||
* Permissions to spawn `sfunnel`. | ||
* Route or proxy traffic to be funneled. More on this [here](docs/funneling.md) | ||
|
||
Make sure stateful firewalls and IDS/IDPS are properly configured to allow this | ||
type of traffic. | ||
|
||
Contact | ||
------- | ||
|
||
Marc Sune < marcdevel (at) gmail (dot) com> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Funneling rules | ||
|
||
Rules have the following format: | ||
|
||
``` | ||
<list of match conditions> actions <list of actions> | ||
``` | ||
|
||
Rules are processed in the strict order, top to bottom, as they are defined. | ||
Total ordering is therefore guaranteed. | ||
|
||
## Match conditions | ||
|
||
Match conditions use [nftables syntax](https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes#Matches)[^1]. | ||
|
||
### `ip` | ||
|
||
Matches IPv4 packets. Required to match `saddr` and `daddr`. | ||
|
||
### `saddr`/`daddr` | ||
|
||
Matches IPv4 source/destination address of the packet against CIDR. It can | ||
optionally be negated `!=`. | ||
|
||
Examples: | ||
|
||
``` | ||
ip saddr 127.0.0.1 | ||
``` | ||
|
||
``` | ||
ip daddr 10.0.0.0/24 | ||
``` | ||
|
||
``` | ||
ip saddr != 127.0.0.1 daddr 10.0.0.0/8 | ||
``` | ||
|
||
### `tcp`/`udp` | ||
|
||
Matches TCP or UDP packets. Required to match `sport` and `dport`. | ||
|
||
### `sport`/`dport` | ||
|
||
Matches _a single_ L4 source or destination port (exact match). It can | ||
optionally be negated with `!=`. | ||
|
||
``` | ||
tcp dport 80 | ||
``` | ||
|
||
``` | ||
udp dport 1000 sport != 1000 | ||
``` | ||
|
||
## Actions | ||
|
||
|
||
|
||
[^1] To the best of my ability, as there isn't a defacto-standard python library | ||
to parse nftable filters. |