Exposing the UniFi Network Controller via a Traefik (v2) reverse proxy

This is about my WiFi network set-up with Ubiquiti Access Points (AP’s) in a home situation, but without having (or wanting) physical hardware for running the Unifi Network Controller software on premise (required for set-up and management purposes). The software is therefore running as a container on a Virtual Machine in the Microsoft Azure cloud and AP’s are adopted by it via the ‘Layer 3 Adoption for Remote Unifi Controllers’ method. Because running the Unifi Network Controller web UI with a proper (valid and auto-renewing) LetsEncrypt web certificate and working websocket connections wasn’t trivial after all, that’s what I’m sharing in this article.

I could create many articles about the entire setup, which involves Route 53 DNS hosting with Amazon, HashiCorp’s Terraform for deploying the Virtual Machine (VM) to Azure (including a customized Network Security Group involving IP-filters), Ansible for installing docker on the VM and Traefik Letsencrypt Amazon route53 domain-based certificate generation, but I’ll keep this article focussed on the Traefik (v2) set-up for having it acting as a reverse proxy to the UniFi Network Controller software. You can always leave a comment when one of these topics interests you, it could motivate me to write about it 😃

To be clear, I’m using Traefik v2 as the reverse proxy and have it terminate the SSL connection. The UniFi Network Controller web UI port is 8443 and it has a self-signed web certificate only for providing encryption (though susceptible to a man-in-the-middle-attack). I use Traefik to forward port 443 (https) to port 8443 (https) in the container running the software. Also, the UniFi Network Controller software requires port 8080 (TCP) (among others to be opened up for successful AP adoption, and I don’t intend to have this go through Traefik.

I do advise you to restrict access to the VM’s forwarded ports only from the AP location’s IP address, for security reasons. You don’t want to open it up to the whole Internet.

I use docker-compose for defining what runs in docker on the Azure Virtual Machine. The VM is of type Standard_B1ms (2GB RAM) and will probably cost you money, so please be aware of that. The docker-compose.yml:

As you probably already figured, this is not a ready-to-run example. First of all this is because I’m opting for automatic certificate provisioning for which I’m using Amazon’s route53 domain validation. This is why I need to provide AWS_HOSTED_ZONE_ID, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to the Traefik container after which I can request wilcard-certificates (which is something you don’t get with http validation,). You should probably swap the certificate resolver and the validation type for another, in the traefik.yml provided below.

As you can see, the traefik service is a custom built docker image, which can be built with the files below:

The Dockerfile for building the Traefik (v2) image:

And three configuration files (traefik.yml, tls.yml and http.yml) I choose to put in the Traefik container image (as it’s pretty hard, if not partially impossible to provide them as labels to the traefik service in the docker-compose.yml file):

The reverse proxying part can now work properly, mainly due to these options:

traefik.http.routers.unifi.middlewares=unifiHeaders@file, resolves to

http:
  middlewares:
    unifiHeaders:
      headers:
        customRequestHeaders:
          Authorization: "" # Removes

which removes the Authorization header (we cannot do this via labels attached to the docker containers yet).

- traefik.http.services.unifi.loadbalancer.server.port=8443
- traefik.http.services.unifi.loadbalancer.server.scheme=https

…ensures traffic is forwarded via https, as that’s what UniFi is using on port 8443.

Given that unifi and traefik containers are running and port 443 is correctly forwarded to the Virtual Machine, I can now access my UniFi Network Controller Web UI over a properly terminated SSL connection with a valid auto-renewing LetsEncrypt certificate (eg. https://unificontroller.mydomain.io, not https://unificontroller.mydomain.io:8443).

I want to give some credits to these owners websites for getting the Traefik part to work:

and to this article from Ubiquiti for inspiring me to hosting the controller in “the cloud”.