So you've got a UniFi controller setup, you connect to the management page, and the browser shows "this connection is not safe". How annoying. This post covers how to avoid the warning by using letsencrypt certbot certificates along with nginx to proxy port 443 to UniFI.

I'm using the Ubuntu 16.04.3 droplet on DigitalOcean. In the examples below replace YOUR_FQDN with your FQDN; for this to work, it must have a valid hostname verifiable with a public DNS server.

Here's my example A and CNAME entries on DigitalOcean:

Type  Hostname              Value
CNAME www.testunifi.foo.com testunifi.foo.com. (note the trailing '.')
A     testunifi.foo.com     107.xx.yy.zz

Enable the firewall and allow inbound ports

Ubuntu doesn't start the firewall by default so enable that, and forward these ports. If you're using a cloud provider with off-host network security, like AWS, remember to update the network configuration for these ports.

ufw enable
ufw allow ssh
# Allow 'informs'
ufw allow 8080/tcp
# Allow STUN
ufw allow 3478/udp
# Allow HTTP (forwarded to HTTPS) if you want the redirect
ufw allow 80/tcp
# Allow HTTPS
ufw allow 443/tcp

Install Unifi

Install the Unifi controller software if you don't already have it.

# Install Unifi
apt-get update
apt-get upgrade
echo deb http://www.ubnt.com/downloads/unifi/debian stable ubiquiti > /etc/apt/sources.list.d/100-ubnt.list
# Public key of Unifi Developers
apt-key adv --keyserver keyserver.ubuntu.com --recv 06E85760C0A52C50
apt-get update
apt-get install unifi

Install and configure NGINX

NGINX does the proxying from HTTPS to Unifi (running on 8443), and does all of the SSL handshaking.

# Install nginx and certbot (Ubuntu Xenial 16.04.3)
# https://certbot.eff.org/#ubuntuxenial-nginx
apt-get install software-properties-common
add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install nginx python-certbot-nginx 

# Create a DH param file. This takes a while.
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Create /etc/nginx/conf.d/YOUR_FQDN.conf with approximately this content:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80 ipv6only=on;
    listen 443 ssl;
    listen [::]:443 ipv6only=on ssl;

    server_name YOUR_HOST.com www.YOUR_HOST.com;
    client_max_body_size 2G;

    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

    location / {
        proxy_pass https://localhost:8443;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_buffering off;
    }

    # These are managed by certbot.
    # ssl_certificate /etc/letsencrypt/live/YOUR_FQDN/fullchain.pem;
    # ssl_certificate_key /etc/letsencrypt/live/YOUR_FQDN/privkey.pem;
}

The NGINX configuration can be tricky, and be sure to verify the settings with nginx -t -c /etc/nginx/nginc.conf. Execute systemctl reload nginx to reload the configuration.

Create the SSL certificates with certbot

certbot is a tool that reads the NGINX configuration and can easily renew certs and update NGINX configuration. Feel free to use letsencrypt directly if you're needing a more custom approach.

Create the certificate using this, and you don't need to forward 80 to 443 since the above configuration does that already. If certbot fails try restarting nginx or take the manual approach:

certbot --nginx

Reload NGINX and verify HTTPS sessions are valid

Reload nginx one more time, systemctl reload nginx, and try connecting to YOUR_FQDN on 80, which redirects to HTTPS.