15. November 2020 6 min read
Traefik v2 and Unifi Controller on Kubernetes
RaspberryPi 3 and 4 Kubernetes cluster can run Unifi Controller on your local network and expose your admin interface over the https on Internet. I also have firewall before my Kubernetes cluster which only exposes https and http ports, so other ports exposed here are only exposed in my local network.
To begin with, we need to open some ports (that are open on linuxserver/docker-unifi-controller container) with Traefik Helm chart, so Traefik helm values.yaml should be appended with:
additionalArguments:
...
# This is needed because unifi has self-signed certificate and we expose valid Letsencrypt certificate over port 443
- "--serverstransport.insecureskipverify"
...
# Configure ports
ports:
...
unifilocal:
port: 8001
# hostPort: 8080
expose: true
exposedPort: 8080
protocol: TCP
tls:
enabled: false
unifilocaludp:
port: 8002
# hostPort: 8080
expose: true
exposedPort: 8080
protocol: UDP
tls:
enabled: false
unifidiscovery:
port: 10001
expose: true
exposedPort: 10001
protocol: UDP
tls:
enabled: false
unifistun:
port: 3478
expose: true
exposedPort: 3478
protocol: UDP
tls:
enabled: false
unifil2:
port: 1900
expose: true
exposedPort: 1900
protocol: UDP
tls:
enabled: false
Now that Traefik exposes the needed ports on our local network we need to setup the unifi-controller. I decided to put it to unifi namespace and it also requires persistentVolume to be set to store the configuration.
unifi-persistent-volume.yaml:
kind: PersistentVolume
apiVersion: v1
metadata:
name: unifi-config-pv-volume
labels:
type: unifi-config
spec:
storageClassName: manual
capacity:
storage: 10Mi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/unifi/config"
unifi-controller.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: unifi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: unifi-config-pv-claim
namespace: unifi
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
selector:
matchLabels:
type: unifi-config
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: unifi-deployment
namespace: unifi
spec:
replicas: 1
selector:
matchLabels:
app: unifi
template:
metadata:
labels:
app: unifi
spec:
volumes:
- name: config
persistentVolumeClaim:
claimName: unifi-config-pv-claim
containers:
- name: unifi
image: linuxserver/unifi-controller:version-6.0.45
ports:
- containerPort: 8080
name: "device-comm"
- containerPort: 8081
name: "noidea1"
- containerPort: 8443
name: "webadmin-port"
- containerPort: 8843
name: "guest-https"
- containerPort: 8880
name: "guest-http"
- containerPort: 1900
name: "l2-network"
protocol: UDP
- containerPort: 3478
name: "stun-service"
protocol: UDP
- containerPort: 10001
name: "ubnt-discovery"
protocol: UDP
volumeMounts:
- mountPath: "/config"
name: config
---
apiVersion: v1
kind: Service
metadata:
name: unifi-service
namespace: unifi
spec:
ports:
- name: port-1
port: 8001
protocol: TCP
targetPort: 8080
- name: port-1a
port: 8002
protocol: UDP
targetPort: 8080
- name: port-2
port: 8081
protocol: TCP
targetPort: 8081
- name: port-3
port: 8443
protocol: TCP
targetPort: 8443
- name: port-4
port: 8843
protocol: TCP
targetPort: 8843
- name: port-5
port: 8880
protocol: TCP
targetPort: 8880
- name: port-6
port: 3478
protocol: UDP
targetPort: 3478
- name: port-7
port: 10001
protocol: UDP
targetPort: 10001
- name: port-8
port: 1900
protocol: UDP
targetPort: 1900
sessionAffinity: None
selector:
app: unifi
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: unifi-cert
namespace: unifi
spec:
commonName: unifi.example.com
secretName: unifi-example-cert
dnsNames:
- unifi.example.com
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: unifihttpsingressroute
namespace: unifi
spec:
entryPoints:
- websecure
routes:
- match: Host(`unifi.example.com`)
kind: Rule
services:
- name: unifi-service
kind: Service
port: 8443
passHostHeader: true
scheme: https
sticky:
cookie:
sameSite: none
tls:
secretName: unifi-example-cert
passthrough: true
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: unifilocalingressroute
namespace: unifi
spec:
entryPoints:
- unifilocal
routes:
- match: HostSNI(`*`)
kind: Rule
services:
- name: unifi-service
kind: Service
port: 8001
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: unifidiscoveryingressroute
namespace: unifi
spec:
entryPoints:
- unifidiscovery
routes:
- services:
- name: unifi-service
kind: Service
port: 10001
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: unifistuningressroute
namespace: unifi
spec:
entryPoints:
- unifistun
routes:
- services:
- name: unifi-service
kind: Service
port: 3478
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: unifil2ingressroute
namespace: unifi
spec:
entryPoints:
- unifil2
routes:
- services:
- name: unifi-service
kind: Service
port: 1900
Now that we have all the files locally, we just need to apply them. First lets update our Traefik:
helm upgrade traefik traefik/traefik --values traefik2-helm-values.yaml --namespace kube-system
And we apply the Kubernetes unifi-controller deplyoment with:
kubectl apply -f unifi-persistent-volume.yaml
kubectl apply -f unifi-controller.yaml
With this you should see the deployment running and also see it routed in Traefik dashboard. Once you login into your Unifi controller you will notice that your Unifi Access Point is not visible (marked as: No device). Here you have few options. You could setup your local DNS to route unifi domain to your Kubernetes master IP, or simply ssh to your Unifi access point and set inform IP towards your Kubernetes master. You can read details here.