Part 3 : Setting up HTTPS in a Unifi Cluster

Good morning, everyone! Welcome to the 3rd and last part of our tutorial on Unifi cluster! In this section, we will focus on HTTPS: how to secure the Web interfaces of our Unifi cluster?

To implement HTTPS, we will use Let’s Encrypt, the well-known tool for issuing certificates free of charge. And on its Certbot utility, proposed for Unix servers.

This tutorial is strongly inspired by the one proposed by “sblaisot” here: https://blog.blaisot.org/letsencrypt-wildcard-part1.html#comment-4091551395. I then applied it to our cluster.

This tutorial also uses the tools proposed by Steve Jenkins, on his Github: https://github.com/stevejenkins/unifi-linux-utils. Feel free to take a look, there are plenty of tools dedicated to Unifi!

0. VERIFICATIONS AND PREREQUISITES

Before we begin the configuration, it will be necessary to perform checks, especially at the DNS level. The implementation of a certificate requires a functional and stable DNS configuration: Let’s Encrypt uses the DNS records of your domain to verify that you are the owner.

We will start by refining the framework of this tutorial:

  • Two dedicated servers, the Unifi1 server of IP X.X.X.Xand the Unifi2 server of IP Y.Y.Y.Y.
  • A floating IP Z.Z.Z.Z, which can be assigned to one of the two servers as required.

IPs are Public IPs, accessible from the Internet.
We also have three DNS records, on the domain of your choice. Here for the example, yourdomain.com :

  • unifi1.yourdomain.com pointing to X.X.X.X,
  • unifi2.yourdomain.com pointing to Y.Y.Y.Y,
  • unifi.yourdomain.com pointing to Z.Z.Z.Z

We will therefore check that our domain names are pointing to the right places: the “nslookup” tool, on Windows or Unix, allows you to do this check:

root@unifi1:~# nslookup unifi1.yourdomain.com
Server:         62.210.16.6
Address:        62.210.16.6#53

Non-authoritative answer:
Name:   unifi1.yourdomain.com
Address: X.X.X.X

root@unifi1:~# nslookup unifi.yourdomain.com
Server:         62.210.16.6
Address:        62.210.16.6#53

Non-authoritative answer:
Name:   unifi.yourdomain.com
Address: Z.Z.Z.Z

root@unifi1:~# nslookup unifi2.yourdomain.com
Server:         62.210.16.6
Address:        62.210.16.6#53

Non-authoritative answer:
Name:   unifi2.yourdomain.com
Address: Y.Y.Y.Y

Here all names are correctly configured. We will then check that your /etc/hosts files are well configured. First of all on the primary server:

root@unifi1:~# nano /etc/hosts
127.0.0.1       localhost
127.0.1.1       unifi1.yourdomain.com unifi1

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Then on the secondary server:

root@unifi2:~# nano /etc/hosts
127.0.0.1       localhost
127.0.1.1       unifi2.yourdomain.com unifi2

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

We see here that both servers are well declared, in short name and in long name. Everything is ready to launch the tutorial!

1. CERTIFICATE CREATION

In a standard case, we could create a certificate for each server, with the certbot utility, and that would be the end of the tutorial. However, here we are in the case of a cluster: if we set up a certificate for the first unifi1.yourdomain.com server, and another for the second unifi2.yourdomain.com server, there is a risk of having a problem concerning unifi.yourdomain.com. This DNS entry, which serves as an entry ticket to the cluster, will be in error because the Web interface will point to unifi1.yourdomain.com or unifi2.yourdomain.com and the name will not match the certificate! Hence an SSL error, which goes against the interest of the tutorial.

To easily solve this problem, we will simply use the Wildcard option of Let’s Encrypt: the SSL wildcard allows to create a certificate that is valid for all subdomains of a domain. Example here, we will create the wildcard for *.yourdomain.com, and the certificate will become valid for all subdomains xxxx.yourdomain.com, which solves our problem!

To be able to create this SSL certificate on the primary server, we will use the Certbot utility offered by Let’s Encrypt:

root@unifi1:~# mkdir /opt/certbot
root@unifi1:~# cd /opt/certbot
root@unifi1:~# wget https://dl.eff.org/certbot-auto
root@unifi1:~# chmod a+x certbot-auto
root@unifi1:~# ./certbot-auto --install-only

Then we start creating a Wildcard certificate for your domain (replace the domain in the command below with yours). On the primary server :

root@unifi1:~# cd /opt/eff.org/certbot/venv/bin/
root@unifi1:/opt/eff.org/certbot/venv/bin# ./certbot certonly --server https://acme-v02.api.letsencrypt.org/directory \
                     --manual -d '*.yourdomain.com'

Enter your email address, then answer “A” to accept the terms and conditions. Answer “Y” or “N” depending on whether you agree to share your email address with EFF. Then answer “Y” again so that your IP is logged. Finally, you will receive a line of text with random characters:

root@unifi1:/opt/eff.org/certbot/venv/bin# ./certbot certonly --server https://acme-v02.api.letsencrypt.org/directory \
>                      --manual -d '*.yourdomain.com'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): sebastien@yourdomain.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for yourdomain.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.yourdomain.com with the following value:

TgomGu6_kOYJshtHhiotln5VOgRglrCmHLlhRhA38As

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

At this point, the shell command will pause. You will have to go to your DNS provider’s web interface, create the following TXT record (replace the key with the one Certbot gives you during the order).

_acme-challenge        IN    TXT        6CmoURMbv3F14hJdzR8zqXrhcYJeKWJEhT8xZcz4gUY

Example in my case, my domain name is hosted by OVH:


Creating a TXT record on the OVH interface

Once your TXT record is created, wait for some time for it to be processed, before continuing the command: the propagation may take some time. In my case a few minutes, but depending on your provider, it can take much longer. Then press “Enter” once your TXT has been process, in order to let the command end:

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/yourdomain.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/yourdomain.com/privkey.pem
   Your cert will expire on 2019-02-19. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

If everything goes as planned, your certificate is created! It is available in “/etc/letsencrypt/live/yourdomain.com/fullchain.pem“.

2. IMPLEMENTATION OF CERTIFICATES FOR UNIFI

The certificate is created, however it is not yet used by Unifi: Unifi will have to be told to use the certificate thus created for its web interface. To do this, we will use the “unifi_ssl_import.sh” script available on Steve Jenkins‘ Github:

On the primary server, we get the script in question:

root@unifi1:~# wget https://raw.githubusercontent.com/stevejenkins/unifi-linux-utils/master/unifi_ssl_import.sh -O /usr/local/bin/unifi_ssl_import.sh

We modify it for our use:

root@unifi1:~# nano /usr/local/bin/unifi_ssl_import.sh
# CONFIGURATION OPTIONS
UNIFI_HOSTNAME=unifi1.yourdomain.com
UNIFI_SERVICE=unifi

# Uncomment following three lines for Fedora/RedHat/CentOS
#UNIFI_DIR=/opt/UniFi
#JAVA_DIR=${UNIFI_DIR}
#KEYSTORE=${UNIFI_DIR}/data/keystore

# Uncomment following three lines for Debian/Ubuntu
UNIFI_DIR=/var/lib/unifi
JAVA_DIR=/usr/lib/unifi
KEYSTORE=${UNIFI_DIR}/keystore

# Uncomment following three lines for CloudKey
#UNIFI_DIR=/var/lib/unifi
#JAVA_DIR=/usr/lib/unifi
#KEYSTORE=${JAVA_DIR}/data/keystore

# FOR LET'S ENCRYPT SSL CERTIFICATES ONLY
# Generate your Let's Encrtypt key & cert with certbot before running this script
LE_MODE=no
LE_LIVE_DIR=/etc/letsencrypt/live

# THE FOLLOWING OPTIONS NOT REQUIRED IF LE_MODE IS ENABLED
PRIV_KEY=/etc/letsencrypt/live/yourdomain.com/privkey.pem
SIGNED_CRT=/etc/letsencrypt/live/yourdomain.com/cert.pem
CHAIN_FILE=/etc/letsencrypt/live/yourdomain.com/fullchain.pem

Modify the following information:

  • UNIFI_HOSTNAME=unifi1.yourdomain.com : Replace yourdomain.com with the name of your domain
  • PRIV_KEYSIGNED_CRTCHAIN_FILE : Replaceyourdomain.com with the name of your domain

On the secondary server, the script is also copied. We create the directory /etc/letsencrypt and modify the script:

root@unifi2:~# wget https://raw.githubusercontent.com/stevejenkins/unifi-linux-utils/master/unifi_ssl_import.sh -O /usr/local/bin/unifi_ssl_import.sh
root@unifi2:~# mkdir /etc/letsencrypt
root@unifi2:~# nano /usr/local/bin/unifi_ssl_import.sh
# CONFIGURATION OPTIONS
UNIFI_HOSTNAME=unifi2.yourdomain.com
UNIFI_SERVICE=unifi

# Uncomment following three lines for Fedora/RedHat/CentOS
#UNIFI_DIR=/opt/UniFi
#JAVA_DIR=${UNIFI_DIR}
#KEYSTORE=${UNIFI_DIR}/data/keystore

# Uncomment following three lines for Debian/Ubuntu
UNIFI_DIR=/var/lib/unifi
JAVA_DIR=/usr/lib/unifi
KEYSTORE=${UNIFI_DIR}/keystore

# Uncomment following three lines for CloudKey
#UNIFI_DIR=/var/lib/unifi
#JAVA_DIR=/usr/lib/unifi
#KEYSTORE=${JAVA_DIR}/data/keystore

# FOR LET'S ENCRYPT SSL CERTIFICATES ONLY
# Generate your Let's Encrtypt key & cert with certbot before running this script
LE_MODE=no
LE_LIVE_DIR=/etc/letsencrypt/live

# THE FOLLOWING OPTIONS NOT REQUIRED IF LE_MODE IS ENABLED
PRIV_KEY=/etc/letsencrypt/live/yourdomain.com/privkey.pem
SIGNED_CRT=/etc/letsencrypt/live/yourdomain.com/cert.pem
CHAIN_FILE=/etc/letsencrypt/live/yourdomain.com/fullchain.pem

Modify the following information:

  • UNIFI_HOSTNAME=unifi2.yourdomain.com :  Replaceyourdomain.come with the name of your domain
  • PRIV_KEY & SIGNED_CRTCHAIN_FILE :  Replace yourdomain.com with the name of your domain

On the primary server, we will reuse the Unison package to synchronize the folders /etc/letsencrypt/ between the Unifi1 and Unifi2 server. Launch the following command by replacing yourdomain.com by your domain:

root@unifi1:~# /usr/bin/unison -batch /etc/letsencrypt/ ssh://root@unifi2.yourdomain.com//etc/letsencrypt/

Contacting server...
Connected [//unifi1//etc/letsencrypt -> //unifi2//etc/letsencrypt]
Looking for changes
  Waiting for changes from server
Reconciling changes
new file <-?-> new file   csr/0000_csr-letsencrypt.pem
local        : new file           modified on 2018-11-20 at 15:02:35  size 972       rw-r--r--
unifi2       : new file           modified on 2018-11-20 at 15:31:12  size 972       rw-r--r--
new file <-?-> new file   keys/0000_key-letsencrypt.pem
local        : new file           modified on 2018-11-20 at 15:02:35  size 1704      rw-------
unifi2       : new file           modified on 2018-11-20 at 15:31:12  size 1704      rw-------
props    <-?-> props      live
local        : dir props changed  modified on 2018-11-20 at 15:56:36  size 1520      rwx------
unifi2       : dir props changed  modified on 2018-11-20 at 15:28:53  size 88        rwxr-xr-x
new dir  ---->            accounts/acme-v02.api.letsencrypt.org
local        : new dir            modified on 2018-11-20 at 15:54:23  size 0         rwx------
unifi2       : absent
new dir  ---->            archive/yourdomain.com
local        : new dir            modified on 2018-11-20 at 15:56:36  size 8820      rwxr-xr-x
unifi2       : absent
new file ---->            csr/0000_csr-certbot.pem
local        : new file           modified on 2018-11-20 at 15:54:48  size 924       rw-r--r--
unifi2       : absent
new file ---->            keys/0000_key-certbot.pem
local        : new file           modified on 2018-11-20 at 15:54:48  size 1704      rw-------
unifi2       : absent
new file ---->            live/README
local        : new file           modified on 2018-11-20 at 15:56:36  size 740       rw-r--r--
unifi2       : absent
new dir  ---->            live/yourdomain.com
local        : new dir            modified on 2018-11-20 at 15:56:36  size 692       rwxr-xr-x
unifi2       : absent
new dir  ---->            renewal-hooks
local        : new dir            modified on 2018-11-20 at 15:54:23  size 0         rwxr-xr-x
unifi2       : absent
new file ---->            renewal/yourdomain.com.conf
local        : new file           modified on 2018-11-20 at 15:56:49  size 540       rw-r--r--
unifi2       : absent
Propagating updates
UNISON 2.48.3 started propagating changes at 16:09:26.92 on 20 Nov 2018
[CONFLICT] Skipping csr/0000_csr-letsencrypt.pem
  contents changed on both sides
[CONFLICT] Skipping keys/0000_key-letsencrypt.pem
  contents changed on both sides
[CONFLICT] Skipping live
  properties changed on both sides
[BGN] Copying accounts/acme-v02.api.letsencrypt.org from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying archive/yourdomain.com from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying csr/0000_csr-certbot.pem from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying keys/0000_key-certbot.pem from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying live/README from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying live/yourdomain.com from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying renewal-hooks from /etc/letsencrypt to //unifi2//etc/letsencrypt
[BGN] Copying renewal/yourdomain.com.conf from /etc/letsencrypt to //unifi2//etc/letsencrypt
Shortcut: copied /etc/letsencrypt/archive/votredomaine.fr/chain1.pem from local file /etc/letsencrypt/archive/unifi.yourdomain.com/chain1.pem
Shortcut: copied /etc/letsencrypt/archive/yourdomain.com/privkey1.pem from local file /etc/letsencrypt/keys/.unison.000_key-certbot.pem.9b43691cb1ffe6e6e509719e19b61d5d.unison.tmp
[END] Copying accounts/acme-v02.api.letsencrypt.org
[END] Copying csr/0000_csr-certbot.pem
[END] Copying keys/0000_key-certbot.pem
[END] Copying live/README
[END] Copying renewal/yourdomain.com.conf
[END] Copying renewal-hooks
[END] Copying archive/yourdomain.com
[END] Copying live/yourdomain.com
UNISON 2.48.3 finished propagating changes at 16:09:26.95 on 20 Nov 2018
Saving synchronizer state
Synchronization complete at 16:09:26  (8 items transferred, 3 skipped, 0 failed)
  skipped: csr/0000_csr-letsencrypt.pem (contents changed on both sides)
  skipped: keys/0000_key-letsencrypt.pem (contents changed on both sides)
  skipped: live (properties changed on both sides)

If all goes well, you should see the certificates and associated files appear on the Unifi2 server, in the /etc/letsencrypt/ folder. If the command has worked well, then run the following operations on both servers:

root@unifi1:~# chmod +x /usr/local/bin/unifi_ssl_import.sh
root@unifi1:~# /usr/local/bin/unifi_ssl_import.sh
root@unifi2:~# chmod +x /usr/local/bin/unifi_ssl_import.sh
root@unifi2:~# /usr/local/bin/unifi_ssl_import.sh

If everything goes as planned, the script replaces the standard Unifi certificate with your Wildcard /etc/letsencrypt/live/yourdomain.com/*.pem certificate. Then restart the Unifi service on both servers:

root@unifi1:~# service unifi restart
root@unifi2:~# service unifi restart

Via your favorite web browser, access the following URLs:

https://unifi.yourdomain.com:8443
https://unifi1.yourdomain.com:8443
https://unifi2.yourdomain.com:8443

The 3 URLs must be functional, and secure via HTTPS, without errors. Otherwise, check that the certificates are created and available on both servers, that the Unifi import scripts do not make any errors.

Once our servers are secure, we’ll just plan the renewal of the certificates, and it’ll be fine!

3. PLANNING & RENEWAL OF CERTIFICATES

On the primary server, add to the crontab the renewal of the certificate and the transfer to the secondary server:

root@unifi1:~# crontab -e
# m h  dom mon dow   command
15 */12 * * * /opt/eff.org/certbot/venv/bin/certbot renew
20 */12 * * * /usr/local/bin/unifi_ssl_import.sh
21 */12 * * * /usr/bin/unison -batch /etc/letsencrypt/ ssh://root@unifi2.yourdomain.com//etc/letsencrypt/

On the secondary server, add the execution of the certificate:

 root@unifi2:~# crontab -e
# m h  dom mon dow   command
25 */12 * * * /usr/local/bin/unifi_ssl_import.sh

Conclusion

And that’s it, your security via HTTPS is functional, and automatic! The renewal of the Wildcard certificate will be done on its own, and your web interfaces no longer display HTTPS errors.

This marks the end of this tutorial for the creation of a Unifi cluster! However, other chapters will probably emerge, notably on the security of these servers, and other possible functionalities 🙂

If you have any ideas to improve this tutorial, if you want to react, do not hesitate to contact us or comment! And we’ll see you next time on Wireless.fr !

Post a Comment

avatar
  Subscribe  
Notify of