I manage a few Unifi deployments on the side, and part of this is hosting a Unifi controller. For the past year, I have been hosting this controller on a DigitalOcean Kubernetes cluster – which has worked really well. DOKS is a good service (great if you’re a cheapskate like me and don’t want to pay for your controlplane). I have no complaints about their offerings whatsoever.
Given that I recently build a nice shiny new homelab, I wanted to put it to use. I also could reduce my cloud bill by hosting things locally instead. However, one hurdle remained to be overcome – granting public access to the Unifi install from behind my NAT.
I knew about frp from previous research on this topic, but I recently came across https://github.com/b4fun/frpcontroller. User bcho had done the legwork to build a k8s controller to run frp and create tunnels from within Kubernetes. Their code was a bit old though, and needed some updating. I forked their project and re-implemented it onto kubebuilder v3, along with upgrading to Go v1.17. You can check it out at https://github.com/ebauman/frpcontroller.
Here’s the steps I followed to put together these pieces and host Unifi behind NAT.
FRP Server
- Spin up a t2.micro instance on EC2
- Acquire an elastic IP and associate it with the instance
- On this micro instance, download the latest frp release and
tar xzvf
- Move
frps
into/usr/local/bin
- Create
/etc/frp/frps.ini
with the following content:[common] bind_port = 7000 bind_addr = [PRIVATE IPV4 ADDR] token = [YOUR TOKEN]
- Create a new unit file for the
frps
service. This file should be/etc/systemd/system/frps.service
and should have the following content:[Unit] Description=fast reverse proxy [Service] ExecStart=/usr/local/bin/frps -c /etc/frp/frps.ini [Install] WantedBy=multi-user.target
- Execute
systemctl daemon-reload
, followed bysystemctl enable frps --now
FRP Client on Private K8s Cluster
Setting up the private k8s cluster is outside of the scope of this guide.
With the cluster setup, install frpcontroller by calling kubectl apply -f https://raw.githubusercontent.com/ebauman/frpcontroller/main/release/v0.0.2/install.yaml
(check for a more recent version in github.com/ebauman/frpcontroller/tree/main/release and use that instead)
Installing frpcontroller also installs two CRDs into your cluster – Endpoint
and Service
. An Endpoint
is the client-side reference for an FRP server. In this case, we’ll make one to point to our newly-created t2.micro instance.
First, create the namespace into which you will eventually install the Unifi controller. kubectl create namespace unifi
Next, create a new file called endpoint.yaml
and place into it the following contents:
apiVersion: frp.1eb100.net/v1
kind: Endpoint
metadata:
name: unifi
namespace: unifi
spec:
addr: '1.2.3.4' # your elastic ip. include the quotes
port: 7000
token: yourtoken
Create this endpoint by calling kubectl apply -f endpoint.yaml
.
Next, you’ll need to install the Unifi controller. There is plenty of documentation on how to accomplish this – I use https://artifacthub.io/packages/helm/k8s-at-home/unifi to do this.
Once Unifi is installed, there are various ports you will need to connect using FRP. Most commonly, these are:
Port | Usage |
tcp/8080 | device and app communication |
udp/10001 | device discovery |
tcp/8443 | controller web ui |
tcp/6789 | unifi mobile speed test |
udp/3478 | STUN |
udp/5514 | remote syslog capture |
Create service.yaml
with the following content:
apiVersion: frp.1eb100.net/v1
kind: Service
metadata:
name: unifi
namespace: unifi
spec:
endpoint: unifi
ports:
- name: tcp-8080
localPort: 8080
remotePort: 8080
protocol: TCP
- name: udp-10001
localPort: 10001
remotePort: 10001
protocol: UDP
- name: tcp-8443
localPort: 8443
remotePort: 8443
protocol: TCP
- name: tcp-6789
localPort: 6789
remotePort: 6789
protocol: TCP
- name: udp-3478
localPort: 3478
remotePort: 3478
protocol: UDP
- name: udp-5514
localPort: 5514
remotePort: 5514
protocol: UDP
selector:
[dependent upon your setup, see notes below]
The spec.selector
field here works just like it does on a regular k8s service
. The values here are dependent upon how you installed Unifi. For instance, my selector(s) are:
selector:
app.kubernetes.io/name: unifi
Yours may differ. Typically the Unifi chart deploys services for you so you will see selectors implemented on those services – you can copy those.
Create the service in k8s by calling kubectl apply -f service.yaml
.
If all goes successfully, you should see a pod created in the unifi
namespace that runs the frpc
software. Looking at the logs of this pod should show the connection being established to your frps
server.
That’s it! Now you can browse to https://your-elastic-ip:8443/ and get to the Unifi page.