Install Letsencrypt SSL Certificate for Unifi Controller on Raspberry Pi

One of the most asked questions about the Unifi Controller is how to get rid of the certificate error when you open the controller. And I totally understand, it’s annoying to bypass the error/warning every time you log in to your controller.

So in this article, we are going to install a Letsencrypt SSL Certificate for our Unifi Controller. I am using a Rasberry Pi to run the controller, so this article is mostly written for a Pi.

Letsencrypt and Unifi

Letsencrypt certificates are free to use but need to be renewed every 90 days. The renewing can be automated, as I will show you in this article, but it requires that your controller is accessible from the internet.

To do this you will need a custom domain name that you can use to point a subdomain to your local controller. Also, we need to open and forward a specific port in the router.

Before we continue there is one thing you should know, we can’t create a certificate for a local IP Address. You are probably running your controller locally, so if you want to open your Unifi Controller, you go to 192.168.0.201 for example.

Even if we make the controller accessible from the internet, with unifi.yourdomain.com, then, by default, you still can’t access it locally with a certificate. You can’t open unifi.yourdomain.com, because that will require you to make a U-turn on the internet, which won’t work. Opening the controller from the local IP Address will still give a warning because the cert is issued to unifi.yourdomain.com.

Local DNS Entries

But we have a solution for this, we can create a local DNS entry. You can do this locally on your computer, by changing your host file or on your router, by creating a static DNS entry. I prefer the router because this is device independent and will work in your whole network, but you will have to check if it’s possible with your router.

Static DNS entry EdgeRouter

If you have an EdgeRouter, you can create a static DNS route by opening your router through SSH. Simply use Putty or Windows Terminal for this:

# Open the SSH connection to your EdgeRouter
ssh ubnt@192.168.0.1

# Open configure mode
configure

# Add the DNS route. Use the domain name we create later
# Change 192.168.0.201 to the IP Address of your Unifi Controller
set system static-host-mapping host-name <unifi.yourdomain.com> inet 192.168.0.201

commit
save

Change your host file

On Windows, you can easily add local DNS routes by adding them to your host file. You will need to open the host file with admin rights to save the changes:

  • Open %windir%\System32\drivers\etc
  • Open the file hosts
  • At the end of the file, add: 192.168.0.201 unifi.yourdomain.com
  • Save and close the file

Change 192.168.0.201 to the IP Address of your Unifi Controller.

Setup the Port Forwarding and Domain

So to get a certificate we need a domain name. You can get a domain name for less than $10 a year, for example here at NameCheap Now we don’t want to point the whole domain to our unifi controller, so I suggest you create a subdomain unifi.yourdomain.com and point that to your local network.

Look up your public IP Address, here at myip.com, and create an A record with the name unifi and your IP Address.

Now you need to forward and open the following ports in your router. Port 80 is needed for LetsEncrypt to authenticate the SSL Certificate.

  • Original port: 80
  • Protocol: TCP/UDP
  • Forward-to address: IP Address of your Unifi Controller
  • Forward-to port: 80

If you also want to access your Unifi Controller from the internet, you could also forward the following port number. But keep in mind that your controller is only protected with a username and password. Another, saver, option to access your Unifi Controller is to use unifi.ui.com.

  • Original port: 8443
  • Protocol: TCP/UDP
  • Forward-to address: IP Address of your Unifi Controller
  • Forward-to port: 8443

If you have an EdgeRouter you can enable Auto firewall, the EdgeRouter will open the specific port in the firewall for you then:

Port forwarding EdgeRouter

Install SSL Certificate on Unifi Controller

With everything set, we can start with installing the certificate for our Unifi Controller.

  1. Install Certbot

    sudo apt-get install certbot

  2. Generate a SSL certificate

    sudo certbot certonly --standalone -d unifi.yourdomain.com

    You will need to enter an email address so you can receive a notification when you need to renew (when the auto-renew fails), Accept the Terms, and Accept or Decline the last request.

  3. Download import script

    We need to import the Letsencrypt cert into the Unifi Controller. Steve Jenkins has created an import script that makes this a lot easier to do, so we are going to use this script.

    Download the script with the following cmd:

    sudo wget https://raw.githubusercontent.com/stevejenkins/unifi-linux-utils/master/unifi_ssl_import.sh -O /usr/local/bin/unifi_ssl_import.sh

    We place it in /usr/local/bin as recommend by Steve.

  4. Make the script executable

    By default, you can’t execute the script. Give it the correct permissions with the following cmd:

    sudo chmod +x /usr/local/bin/unifi_ssl_import.sh

  5. Edit the variables in the script

    Next, we need to edit some of the configuration variables in the script. Open the script with

    sudo nano -w /usr/local/bin/unifi_ssl_import.sh

    Add your domain
    UNIFI_HOSTNAME=unifi.yourdomain.com

    Comment the three lines for Fedora/RedHat/Centos by placing a # for it:
    # Uncomment following three lines for Fedora/RedHat/CentOS
    #UNIFI_DIR=/opt/UniFi
    #JAVA_DIR=${UNIFI_DIR}
    #KEYSTORE=${UNIFI_DIR}/data/keystore


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


    Set the Let’s Encrypt mode to true:
    If you only enable the line, by removing the #, you will get a loop ==Yes when running the script. So set it to true. Will still get some warning about missing [[:, but the doesn’t matter.
    LE_MODE=true

    Save and close the file
    Ctrl + X
    Y
    Enter

  6. Run the import script

    We can now import our new SSL cert into the Unifi Controller. Run the following cmd:

    sudo /usr/local/bin/unifi_ssl_import.sh

    If you get a loop with == yes, press ctrl + c and change LE_MODE to true (see step 5)

  7. Check the new Certificate

    The script will restart the Unifi Controller. Give it a couple of minutes to start.
    If you open your controller you should now have a certificate and no errors or warnings anymore.

  8. Creating the update script

    Cerbot will renew the certificate every 3 months. So we will have to import the certificate as well every 3 months.

    Create a new file
    sudo nano -w /etc/cron.daily/unifi_ssl_import

    And add the following content into it
    #!/bin/bash
    /usr/local/bin/unifi_ssl_import.sh


    Press Ctrl +X followed by Y and Enter to save and close the file.

    Give the file the correct permissions so it can be executed:
    sudo chown root:root /etc/cron.daily/unifi_ssl_import
    sudo chmod +x /etc/cron.daily/unifi_ssl_import


That’s all, you should now be set with a nice SSL certificate for you Unifi Controller and the annoying warning should be gone. If you have any questions, just drop a comment below.

You probably also want to take care of your Unifi Controller backup. By default it’s stored on the controller self, but you can simply upload a copy of the backup to Dropbox for example. You can read all about it in this article.

sources used:
https://www.stevejenkins.com/blog/2016/06/use-existing-ssl-certificate-linux-unifi-controller/
https://crosstalksolutions.com/definitive-guide-to-hosted-unifi/

41 thoughts on “Install Letsencrypt SSL Certificate for Unifi Controller on Raspberry Pi”

  1. The cron job (pos. 8 in tutorial) seems to be not necessary. It is better to create certbot hook. Simply create such executable file as “/etc/letsencrypt/renewal-hooks/deploy/unifi_ssl_import”:

    #!/bin/bash
    /usr/local/bin/unifi_ssl_import.sh 2>&1

    That’s it! Certificate will be automatically replaced after every renewal.

  2. I installed the Unifi controller about half a year ago, including the Let’s Encrypt certificate. It worked fine, until after a few months I got the ‘Missing one or more required files. Check your settings’ error.

    It turns out that it was caused by PiHole, which I installed later. PiHole installs a webserver (lighttpd), which listens on port 80. As a result Let’s Encrypt cannot check the certifcate on port 80. After 90 days the certificate will expire.

    The solution is easy, though you’ll have to update the certificate manually every now and then.

    Stop lighttpd: $ sudo /etc/init.d/lighttpd stop
    Import a new certificate: $ sudo certbot certonly –standalone -d unifi.yourdomain.com
    Run the import script: $ sudo /usr/local/bin/unifi_ssl_import.sh
    The new certificate is activated and the Unifi controller is restarted.
    Restart lighttpd: $ sudo /etc/init.d/lighttpd start

    • If you’re behind a NAT router, there’s a better solution:

      Run certbot on a different port internally and forward port 80 external to this internal port.

      Edit the file /etc/letsencrypt/cli.ini and add the following row:

      http-01-port = 81

      You can change 81 to any available port you desire.

  3. Not sure if something has changed or an extra step is needed with the keystore side of the controller.

    Post run which goes seamlessly I now get from Chrome:
    This site can’t provide a secure connection controllername.domain.net.nz uses an unsupported protocol.
    ERR_SSL_VERSION_OR_CIPHER_MISMATCH
    Unsupported protocol
    The client and server don’t support a common SSL protocol version or cipher suite.

    Script output:
    root@server:~# /usr/local/bin/unifi_ssl_import.sh

    Starting UniFi Controller SSL Import…

    Running in Let’s Encrypt Mode…

    Inspecting current SSL certificate…

    Updated SSL certificate available. Proceeding with import…

    Importing the following files:
    Private Key: /etc/letsencrypt/live/controllername.domain.net.nz/privkey.pem
    CA File: /etc/letsencrypt/live/controllername.domain.net.nz/fullchain.pem

    Stopping UniFi Controller…

    Updating certificate MD5 checksum…

    No original keystore backup found.

    Creating backup as keystore.orig…

    Exporting SSL certificate and key data into temporary PKCS12 file…

    Removing previous certificate data from UniFi keystore…

    Importing SSL certificate into UniFi keystore…
    Importing keystore /tmp/tmp.WiTuaeVWXO to /var/lib/unifi/keystore…
    Removing temporary files…

    Restarting UniFi Controller to apply new Let’s Encrypt SSL certificate…

    Done!

    • I have the same problem running this script on Debian 12. All solutions for the ERR_SSL_VERSION_OR_CIPHER_MISMATCH error mentioned online seem to break the guest portal. Can anyone provide me with a real solution?

    • I found a solution to the ERR_SSL_VERSION_OR_CIPHER_MISMATCH error!

      On some operating systems (or newer versions of certbot, I haven’t really looked into the exact cause yet) the command sudo certbot certonly –standalone -d unifi.yourdomain.com will generate an ECDSA certificate. UniFi only supports RSA certificates:

      The solution is to force certbot to generate a RSA certificate:
      sudo certbot certonly –standalone –key-type rsa -d ynternet.dezwart.frl

      @Ruud could you add this to your tutorial to help out other users facing the same problem?

  4. Thank you for the awesome guide! It was seriously helpful. Some notes that may help others:

    1) If you need to rollback to the original keystore file, make sure you set the owner:group of the keystone file to unifi:unifi. If you don’t, the Unifi service will start but the web interface will not load.

    2) If the Devices or Topology tabs in Unifi Network do not load (running blue line), switch to the legacy interface, go to Settings->Maintenance and compact the database. Then switch back to the modern interface.

    3) If you run into the “missing files” error, you need to double check that your LetsEncrypt files match the names the script is expecting. For example, my Acme client (OPNsense) names the private keys as key.pem, but the script is looking for privkey.pem.

  5. It looks as if the unifi sourcebase doesn’t like modern certificates. I had to ensure certbot used RSA 2048-bit keys to make it work.

    • I am using 4096 bit LetsEncrypt key without problems on Unifi Network 7.3.83 on a Raspberry Pi running Bullseye. Perhaps there are other differences in our environments that would prevent the use of 4096 bit keys.

  6. Hi Rudy,
    I love your detailed instructions but I seem to have fallen at the final hurdle:

    root@raspbx:/var/lib# sudo /usr/local/bin/unifi_ssl_import.sh

    Starting UniFi Controller SSL Import…
    Running in Let’s Encrypt Mode…
    Inspecting current SSL certificate…
    Updated SSL certificate available. Proceeding with import…
    Missing one or more required files. Check your settings.

    Any ideas where to check what’s missing?

    Thanks

    • Hi,

      I’m having the exact same issue on Ubuntu 22.03:
      Starting UniFi Controller SSL Import…
      Running in Let’s Encrypt Mode…
      Inspecting current SSL certificate…
      Updated SSL certificate available. Proceeding with import…
      Missing one or more required files. Check your settings.

      Tried the changing to port 81 instead of 80 but that didn’t help.
      Any ideas anyone?

  7. Great tutorial and I’ve got my controller working fine with SSL now. However, when I ran import script, I saw this message:

    “The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format”

    And gives this command:

    keytool -importkeystore -srckeystore /var/lib/unifi/keystore -destkeystore /var/lib/unifi/keystore -deststoretype pkcs12

    Do I need to do anything about that?

  8. not sure if this page is still supported but I have tried following the instruction and run into a firewall issue.

    I am not clear on a few things.
    1. I have the controller installed on a VM ipaddress 192.1681.1.120
    I have the controller configured on the ipaddress 192.168.1.1 (gateway)
    there is no firewall on either the gateway nor the VM.
    I can open the URL icanunifi.e2snail.com:xxxx
    I have added both the vm and gateway ipaddresses into my hosts file and they both point to icanunifi.e2snail.com
    when I run the command certbot certonly –standalone -d icanunifi.e2snail.com

    I get the following error.
    http-01 challenge for icanunifi.e2snail.com
    Waiting for verification…
    Challenge failed for domain icanunifi.e2snail.com
    http-01 challenge for icanunifi.e2snail.com
    Cleaning up challenges
    Some challenges have failed.

    IMPORTANT NOTES:
    – The following errors were reported by the server:

    Domain: icanunifi.e2snail.com
    Type: connection
    Detail: xxx.xxx.xxx.xxx: Fetching
    http://icanunifi.e2snail.com/.well-known/acme-challenge/-pYodsIrfP3u1JIBUoBqzxVzu-ZJ9Q6iTKiRlCFKt:
    Timeout during connect (likely firewall problem)

    What firewall is possibly causing my issue, what have I done wrong?

    Lawrence

    • This page is an exceptional guide and works like a dream. Only there is a little ambiguity as to some of the tasks to carry out.
      The distinction between the server where the controller software is run and the controller itself.
      You refer to your machine, this is the machine where you have the unifi controller software installed on.
      After following the process, I was not clear about what exactly should be added into the unifi server local hosts file.

      I first had the controllers ipaddress and the local server ipaddress, but the controller ipaddress is not needed.
      You should only have the local server ipaddress. (the machine where the unifi software is installed on.
      host file should have the following entry added

      local ipaddress unifi.domain.com

      The rest just simply works.

      Thanks
      Lawrence

  9. This worked perfectly for us, thanks for the tutorial!

    Regarding the automated update. The cert needs to be renewed every 30 days but putting the /etc/cron.daily/ directory will cause the script to run each day at 3:05. Surely weekly or monthly should be enough?

  10. Thanks a ton!
    BTW If you have to run certbot multiple times on the same domain because of mistakes or whatnot, remember to use ‘certbot delete’ to get rid of old files, otherwise, your key names will be your-domain.example.com-0001 and the SSL import script will not recognize it.

  11. If you’re already using port 80 on your Raspberry Pi for something else (PiHole in my case), you can add –http-01-port to the command in step 2 to change the internal port. Just make sure the port on the outside is still 80. I forwarded external port 80 to internal port 81 on my Raspberry Pi. Example of the command I used instead of the one in step 2:

    sudo certbot certonly –standalone –http-01-port 81 -d unifi.yourdomain.com

  12. I have followed the entire guide, but I still keep a non-secure website. If I check the certificate, it is the correct one. unifi.mywebsite.nl. I am calling the website on port 8443, could there still be a problem here?

  13. I have figured out the if I delete the md5sum file then I can run the script again, even thought it says that the keystore is updated, it does not seem to actually be updated.. Here is the output of the script. As you can see no errors.

    Starting UniFi Controller SSL Import…
    Running in Let’s Encrypt Mode…
    Inspecting current SSL certificate…
    Updated SSL certificate available. Proceeding with import…
    Importing the following files:
    Private Key: /etc/letsencrypt/live/icanunifi.e2snail.com/privkey.pem
    CA File: /etc/letsencrypt/live/icanunifi.e2snail.com/fullchain.pem
    Stopping UniFi Controller…
    Updating certificate MD5 checksum…
    Backup of original keystore exists!
    Creating non-destructive backup as keystore.bak…
    Exporting SSL certificate and key data into temporary PKCS12 file…
    Removing previous certificate data from UniFi keystore…
    Importing SSL certificate into UniFi keystore…
    Importing keystore /tmp/tmp.igmJBtNZRe to /var/lib/unifi/keystore…
    Removing temporary files…
    Restarting UniFi Controller to apply new Let’s Encrypt SSL certificate…
    Done!

    I am confused and don’t really understand. I managed to view the keystore contents and have no clue.

    Without disclosing too much, I searched for the word Creation and Valid. As you can see below the creation date is today as expected, but I do not understand why there are two valid dates. The first expires on Feb 07, and the second expires on Mar 17. So 3 months time, but if I look at the certificate within my browser on the site, it clearly shows expiry on Feb 07.

    So is the cert updated or not?

    Creation date: Jan 29, 2021
    Valid from: Mon Nov 09 12:35:43 CET 2020 until: Sun Feb 07 12:35:43 CET 2021
    Valid from: Thu Mar 17 17:40:46 CET 2016 until: Wed Mar 17 17:40:46 CET 2021
    Creation date: Jan 29, 2021
    Valid from: Mon Nov 09 12:35:43 CET 2020 until: Sun Feb 07 12:35:43 CET 2021
    Valid from: Thu Mar 17 17:40:46 CET 2016 until: Wed Mar 17 17:40:46 CET 2021

    Lawrence

  14. I ran this, with all the advice, it seemed to run successfully, but after unifi was restarted, I open my browser and checked the certificate and it still showed that it would expire in 7 days. So even though there were not any errors, the cert did not seem to be updated.

    I had a look at the location of the certificate and there is a softlink to the archive directory and a file location. As you can see below, the archive file has not changed at all, and is still showing Nov 9, only the checksum of the private key has the date that I ran the update.
    692 nov 9 13:35 README
    48 nov 9 13:35 privkey.pem -> ../../archive/icanunifi.e2snail.com/privkey1.pem
    50 nov 9 13:35 fullchain.pem -> ../../archive/icanunifi.e2snail.com/fullchain1.pem
    46 nov 9 13:35 chain.pem -> ../../archive/icanunifi.e2snail.com/chain1.pem
    45 nov 9 13:35 cert.pem -> ../../archive/icanunifi.e2snail.com/cert1.pem
    4256 nov 9 13:35 fullchain.p12
    90 jan 29 00:57 privkey.pem.md5

    I tried to use keytool and openssl to read the private key but with no success.

    Does anyone know how I can check / validate if the key has in fact been updated?
    Thanks

  15. The guide is not lazy enough 🙂
    Because, in this setup, you need to have a running httpd server, as the unifi controller doesn’t have one. Not so obvious from the guide. Has the RPI httpd running in standard setup?

  16. Can a third level domain be used for this? Like from afraid? Or chinsey domains in TLD tk? ml? ga? They typically have reputation problems

  17. I somehow buggered up my unifi install and had to wipe the install, now its back to a self signed cert. When I run the script it says

    “Starting UniFi Controller SSL Import…

    Running in Let’s Encrypt Mode…

    Inspecting current SSL certificate…

    Certificate is unchanged, no update is necessary.”

    What do I need to delete to re-import the cert?

  18. Nice!
    Quick question:

    I have a paid SSL cert right now for my old company domain (unifi.oldcompany.com).
    This works fine but i want to create a 2nd SSL cert for my new company name (unifi.newcompany.com). Can i have both on my VPS controller working together without breaking stuff?

    I need to change the overwrite inform after this to the newcompany domain name but before i do so, the SSL needs to work..

    Cheers

  19. Never mind!

    Found a page with this command:

    certbot -d bristol3.pki.enigmabridge.com –manual –preferred-challenges dns certonly

    Lets see if that’s right?

    Yep, I finished and get the lock.

    So step 8 – ‘certbot will renew the cert every 3 months’…. is that because of something detailed above that it knows to do that? (and did the command line I used vs yours break that autorenew?)

    And the script to import the cert. Is that actually running daily? (cron.daily)? I should be able to go the website in 2 1/2 months and see new expiration date for the cert?

    THANKS!

    • Hi Ed,

      Step 8, renewing the cert, won’t break. Certbot knows which cert it has and which it needs to renew. If you click on the lock icon before the URL, and click on Certificate, then you can see the expiration date.

  20. I am stuck at step 2 (not to far along)

    sudo certbot certonly –standalone -d unifi.yourdomain.com

    I am on home verizon fios. Port 80 is likely blocked? Trying to figure how to do the DNS-01 method – add text to the DNS zone? I can do that if I can run the right command (and with the right command, it;ll tell me the text to include in the zone?

    THANKS!

Leave a Comment

0 Shares
Tweet
Pin
Share
Share