Layer-2 TAP tunnel into a Kubernetes cluster via pod NAD bridge
Project description
k8s-l2-tunneler
Layer-2 TAP tunnel into a Kubernetes cluster. Your developer machine joins the
cluster's pod network as a native L2 peer — ping, curl, and DNS all work
against pod IPs directly, with no proxy or port-forwarding required.
Architecture
Developer machine
TAP (stolen IP + MAC)
└── socat/WireGuard client
└── NodePort UDP
└── Tunnel pod
socat/WireGuard server → tap0 ↔ br0 ↔ net1
│
nad-br0 (host bridge, per tunnel)
│
| Donor pod
net1 ↔ br-donor ↔ eth0
│
cluster pod network
Two pods are created per tunnel:
| Pod | Role |
|---|---|
| Tunnel pod | Runs the socat/WireGuard UDP server. Bridges tap0 (L2 tunnel endpoint) ↔ net1 (NAD) |
| Donor pod | Provides a CNI-registered IP + MAC to the developer TAP. Bridges net1 (NAD) ↔ eth0 (cluster) after identity is stolen |
A NetworkAttachmentDefinition (Multus) creates a host-side Linux bridge
(nad-tun-<id>) that connects both pods' net1 interfaces — this is the
cross-pod L2 segment that was impossible to create from inside either pod.
Both pods are pinned to the same node (mandatory for the host bridge to connect them).
When connecting to an existing cni the donor pod is not created,
instead the identity is stolen directly from the
NetworkAttachmentDefinition interface on the tunnel pod.
Requirements
| Requirement | Notes |
|---|---|
| Multus CNI | Must be installed cluster-side |
socat/WireGuard |
Must be installed on the developer machine |
| Root / sudo | TAP creation + raw ARP sockets need CAP_NET_ADMIN |
| Python ≥ 3.8 | |
uv |
Package manager |
Installation
uv tool install k8s-l2-tunneler
# or, from source:
git clone ...
cd k8s-l2-tunneler
uv sync
Usage
# Create a tunnel (namespace defaults to owner name if omitted)
sudo uv run k8s-l2-tunneler connect-to-cluster --owner alice
# With an explicit namespace and custom local TAP name
sudo uv run k8s-l2-tunneler connect-to-cluster --owner alice --namespace my-ns --local-iface-name cluster-net
# When the cluster is deployed behind a NAT and the pod is able to directly ping your machine's IP
# TODO: support both cluster behind NAT and developer behind NAT using shared STUN server.
sudo uv run k8s-l2-tunneler connect-to-cluster --owner alice --cluster-behind-nat
# Tear it down
sudo uv run k8s-l2-tunneler clean --owner alice
CNI compatibility
| CNI | Works | Notes |
|---|---|---|
| Flannel VXLAN | ✅ | Best case — cni0 bridge does MAC learning |
| Flannel host-gw | ✅ | Same as VXLAN |
| Canal | ✅ | Flannel data plane; check NetworkPolicy |
| Calico BGP | ✅* | *TAP MAC not stolen; promisc mode used instead |
| Calico VXLAN | ✅* | Same as Calico BGP |
| Cilium | ✅ | Works under default policy (no strict source-IP enforcement) |
| WeaveNet | ✅ | Weave L2 mesh propagates MAC correctly |
| Antrea | ✅ | OVS flows match on existing (port, IP) registration |
MTU
All interfaces in the packet path are configured to MTU 1370 to prevent fragmentation across the network path and any CNI overlay encapsulation.
Development
uv sync
uv run ruff format src/
uv run ruff check src/ --fix
uv run ruff check src/
uv run mypy src/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file k8s_l2_tunneler-0.1.0.tar.gz.
File metadata
- Download URL: k8s_l2_tunneler-0.1.0.tar.gz
- Upload date:
- Size: 90.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0fb9cc0fb6361df502c63dc702daa419fb6269dd54e52821ae12468545cd054f
|
|
| MD5 |
795a3f21a0a23809c49ac4f67f4b814c
|
|
| BLAKE2b-256 |
b5094f661486ce6a85e27afeb68f9f8fccbc3069b83f3d624e02630fa55080bd
|
File details
Details for the file k8s_l2_tunneler-0.1.0-py3-none-any.whl.
File metadata
- Download URL: k8s_l2_tunneler-0.1.0-py3-none-any.whl
- Upload date:
- Size: 37.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6a12d61731721cbca532e8c1868156080b7b4df5258a2b9f9f1c37305e92e49
|
|
| MD5 |
3017f1cb52ff5f8f14df465dd1c9c7e5
|
|
| BLAKE2b-256 |
cdb15ce6b0af4e002a0765d2ba5a042da090c062ddd561287e68633b460fb38a
|