Matt Greensmith bio photo

Matt Greensmith

Infrastructure and systems-focused software engineer and cloud architect.

Email Twitter LinkedIn Github

In this walkthrough we will:

  • install the latest release of the Ubiquiti UniFi Controller software on an AWS EC2 instance
  • configure nginx as a reverse proxy (to preserve the native port mapping that ships with the controller)
  • secure the controller and nginx proxy with our own SSL certificate

Instance Bootstrap

I assume that you have some familiarity with AWS - demonstrating security group and instance creation is outside the scope of this walkthough. If you’re new to AWS, Amazon has a nice tutorial for creating an EC2 instance.

Create a new EC2 security group that opens inbound access to all necessary UniFi ports. I created a group called unifi-controller that allows inbound traffic to the following ports.

  • TCP 22 (SSH console access)
  • TCP 80 (HTTP for nginx proxy server)
  • TCP 443 (HTTPS for nginx proxy server)
  • TCP 8080 (UniFi device inform port)
  • TCP 8081 (UniFi management/shutdown port)
  • TCP 8443 (UniFi controller UI/API port)
  • TCP 8880 (UniFi guest portal HTTP port)
  • TCP 8843 (UniFi guest portal HTTPS port)
  • UDP 3478 (STUN for UniFi AP management)

You should restrict the inbound traffic sources to networks where you have deployed UniFi equipment that will talk to the controller.

Create a new EC2 instance. I created a t2.micro instance using the official Ubuntu 14.04 AMI, making sure to assign the unifi-controller security group to the instance.

Once your new instance has booted, find the host name (e.g. ec2-x-x-x-x.us-west-2.compute.amazonaws.com) and connect to it via SSH.

me@mycomputer:~$ ssh -i ~/.ssh/my-aws-key.pem ubuntu@YOUR_EC2_HOSTNAME

Package Installation

Add Ubiquiti’s Ubuntu repository to your apt sources, import their GPG key and then install the UniFi Controller package.

echo 'deb http://www.ubnt.com/downloads/unifi/debian unifi4 ubiquiti' | sudo tee /etc/apt/sources.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv C0A52C50
sudo apt-get update
sudo apt-get install unifi

EDIT 2016-1-7: Ubiquiti repo channels unifi-beta and unifi-rapid have been renamed to unifi4 (stable) and unifi3 (oldstable).

Note that UniFi Switch and UniFi Security Gateway products are only supported by the unifi4 version of the controller (4.x series). Also, if you choose the unifi3 channel, you’ll likely have to modify the target of JAVA_HOME in /etc/init.d/unifi, as they hardcoded an obsolete path to the JVM.

You should now be able to connect to the controller UI via your browser at https://YOUR_EC2_HOSTNAME:8443 and complete the first-run setup wizard for the UniFi controller.

DNS Considerations

The rest of this walkthrough only makes sense if you have a ‘pretty’ DNS name to use for your new controller instance. After all, you probably aren’t interested in purchasing an SSL certificate for the domain ec2-x-x-x-x.us-west-2.compute.amazonaws.com. This is a good time to set up a DNS CNAME for your instance (i.e unifi.mydomain.com CNAME ec2-x-x-x-x.us-west-2.compute.amazonaws.com) and purchase an SSL certificate for that domain.

Changing UniFi SSL Certificate

The UniFi controller package ships with a self-signed SSL certificate by default. This might be OK for a local (LAN) deployment if you’re willing to put up with browser warnings, but is definitely a faux pas for a hosted controller.

In good news, it’s pretty simple to replace the SSL certificate used by the UniFi application server.

I purchased a SSL certificate for unifi.mydomain.com, so I started with the following files:

  • unifi.mydomain.com.key (private key in PEM format)
  • unifi.mydomain.com.crt (certificate in PEM format)
  • ca-chain.crt (intermediate certificate chain in PEM format, NOT including the root CA)

We need to create a certificate package in PKCS12 format, which openssl can do for us:

openssl pkcs12 -export \
  -in unifi.mydomain.com.crt \
  -inkey unifi.mydomain.com.key \
  -CAfile ca-chain.crt \
  -caname root \
  -out unifi.mydomain.com.p12 \
  -name unifi

When prompted to create a password for the new certificate package, use aircontrolenterprise as the password.

Now we have a new file unifi.mydomain.com.p12, which we can import into the UniFi keystore via keytool:

sudo keytool -importkeystore \
  -deststorepass aircontrolenterprise \
  -destkeypass aircontrolenterprise \
  -destkeystore /usr/lib/unifi/data/keystore \
  -srckeystore unifi.mydomain.com.p12 \
  -srcstoretype PKCS12 \
  -srcstorepass aircontrolenterprise \
  -alias unifi

Finally, restart the UniFi server to pick up the new certificate:

sudo service unifi restart

Refresh your browser and enjoy your newly-secured UniFi interface.

Nginx Reverse Proxy

The UniFi Controller ships with the UI on port 8443 by default. I would prefer to see the UI on the default HTTPS port 443 so that my URL can be simplified to https://unifi.mydomain.com. However, there are some anecdotal reports on the Ubiquiti forums advising against changing the default UI port - something to do with javascript hardcoding the port value or creating absolute URLs, etc. So I decided to create a reverse proxy with nginx instead.

Install nginx and remove the default site:

sudo apt-get install nginx
sudo unlink /etc/nginx/sites-enabled/default

Nginx needs your SSL certificate file to contain all intermediate certs as well as your site cert, so combine them together into one file and put it in the nginx config directory. Your private key file should also be available in that directory.

cat unifi.mydomain.com.crt ca-chain.crt > /etc/nginx/unifi.mydomain.com.full.crt

Here’s an example nginx configuration that creates listeners on port 80 and port 443. I created this as /etc/nginx/sites-available/unifi:

server_tokens off;

server {
  listen 80 default_server;
  server_name unifi.mydomain.com;
  return 302 https://unifi.mydomain.com$request_uri;
}

server {
  listen 443;
  server_name unifi.mydomain.com;

  ssl_certificate /etc/nginx/unifi.mydomain.com.full.crt;
  ssl_certificate_key /etc/nginx/unifi.mydomain.com.key;

  ssl on;
  ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC4-SHA';
  ssl_prefer_server_ciphers on;
  ssl_dhparam /etc/nginx/dhparams.pem;

  resolver 8.8.8.8;
    ssl_stapling on;
    ssl_trusted_certificate /etc/nginx/unifi.mydomain.com.full.crt;

  add_header X-Frame-Options SAMEORIGIN;
  add_header X-XSS-Protection "1; mode=block";
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

  location / {
    proxy_pass https://unifi.mydomain.com:8443/;
  }
}

Create a strong, unique Diffie-Hellman group for the server:

sudo openssl dhparam -out /etc/nginx/dhparams.pem 2048

Enable the new nginx site:

sudo ln -s /etc/nginx/sites-available/unifi /etc/nginx/sites-enabled/
sudo service nginx reload

We’re done! The UniFi interface should now be available at https://unifi.mydomain.com without having to add :8443 to the URL. Nginx will reverse-proxy all traffic for you. Here’s a breakdown of the various behaviors that you’ll see based on the source URL:

  • http://unifi.mydomain.com Nginx will redirect to HTTPS at https://unifi.mydomain.com
  • https://unifi.mydomain.com Nginx will proxy traffic to the UniFi controller, user will not see the port number in their browser. (Preferred use case.)
  • http://unifi.mydomain.com:8443 UniFi controller will redirect to HTTPS at https://unifi.mydomain.com:8443
  • https://unifi.mydomain.com:8443 UniFi controller will handle traffic directly, user will see the port number in their browser.

Incidentally, the HTTPS configuration I provided in the example nginx config file is strong enough to earn an A+ grade on the Qualys SSL Labs Server Test, so any traffic that is proxied through nginx will enjoy this security.

We’ve covered installation of the UniFi controller, configuration of nginx as a reverse proxy, and SSL configuration for nginx and the controller itself. That’s it for today; hopefully this was useful to you!