A Kubernetes (k8s) controller which watches for new annotated Services and automatically creates a corresponding port forwarding rule on your router.
Currently Unifi routers are the only supported router model.
This project uses mise for tool version management.
Install mise:
curl https://mise.run | shActivate mise in your shell:
# For bash - add to ~/.bashrc
eval "$(mise activate bash)"
# For zsh - add to ~/.zshrc
eval "$(mise activate zsh)"
# For fish - add to ~/.config/fish/config.fish
mise activate fish | sourceAfter adding the activation line, restart your shell or source your config file.
Install project tools:
mise installThis will install Go 1.25 as specified in .mise.toml.
The expected use case is to create a DNS record which points to your router's public IP and have a port forwarding rule send the traffic into the k8s cluster. This controller automatically manages the creation and deletion of port forwarding rules as Services are created and destroyed.
For example, you might deploy an nginx-ingress Service with the following annotation:
port-forwarding.ryanmcafee.com/enable: "true"
The port-forwarding-controller will notice this new annotated Service and add a port forwarding rule on your router to forward traffic on the given ports to the IP of the new Service.
The controller supports Services of type LoadBalancer
and Services with a non-empty externalIPs property.
Services must be annotated
with port-forwarding.ryanmcafee.com/enable: "true" for the controller to
manage forwarding rules for that Service.
Pros:
- Traffic is balanced across multiple workers
Cons:
- Requires setting up a bare metal Load Balancer, e.g. MetalLB
Setup steps for MetalLB + Unifi router:
- Configure each k8s worker machine with a static IP
- Deploy MetalLB as described here
- Create a ConfigMap containing your router's IP address as described here
- Configure your Unifi router to accept BGP peers as described here
- To persist your BGP config across reboots create a config.gateway.json on your Unifi Controller
- Deploy a LoadBalancer service with the
port-forwardingannotation - Verify the controller has created a new port forwarding rule in the Unifi Controller UI
Explanation:
The MetalLB Concepts page does a good job explaining the underlying BGP (Border Gateway Protocol) concepts and the limitations of it. The short version is that BGP allows multiple machines to "share" a single IP address and the router will load balance requests across all machines which advertise that IP.
Pros:
- Minimal additional setup
Cons:
- All traffic goes to a single worker
Setup steps:
- Configure a k8s worker machine with a static IP
- Create a ClusterIP Service with an
externalIPsset to the IP address of the k8s worker - Verify the controller has created a new port forwarding rule on your router
Explanation:
Setting the externalIPs field will cause the kube-proxy job on each worker node to
start listening on the Service's configure port on the host.
If a worker receives a packet on that port and the destination IP field matches
the configured externalIP of a given Service, kube-proxy will forward traffic to that
Service's pod.
If there are multiple pod replicas, traffic will be load balanced between the pods,
but all traffic will be initially received by a single worker node before being routed
to one of the pod replicas.
This is due to port forwarding rules requiring a one-to-one mapping of port to IP address.
Add the Helm repository:
helm repo add port-forwarding https://ryanmcafee.github.io/port-forwarding-controller
helm repo updateInstall the chart:
helm install port-forwarding port-forwarding/port-forwarding \
--set router.url=YOUR_ROUTER_URL \
--set router.username=YOUR_USERNAME \
--set router.password=YOUR_PASSWORDOr create a values.yaml file with your configuration:
router:
url: https://your-router-url
username: your-username
password: your-passwordThen install:
helm install port-forwarding port-forwarding/port-forwarding -f values.yamlMulti-architecture images are available on GitHub Container Registry:
docker pull ghcr.io/ryanmcafee/port-forwarding-controller:latestSupported platforms: linux/amd64, linux/arm64, linux/arm/v7
- Create a
./secrets/router.envfile with the following contents:ROUTER_URL=$YOUR_ROUTER_URL ROUTER_USERNAME=$YOUR_ROUTER_USERNAME ROUTER_PASSWORD=$YOUR_ROUTER_PASSWORD - Run the following command to install or upgrade the controller:
make deploy
Additional optional Annotations:
port-forwarding.ryanmcafee.com/unifi-site: SOME_SITE, defaults todefaultsite
- Run unit tests:
make test - Try out controller in a local container:
make run - Build docker images:
make docker-build - Test release locally:
make release-snapshot