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.

Newest from this category: