Mikrotik Let’s Encrypt Wildcard SSL Certificate

Hey everyone,

From March 13, 2018 – Let’s Encrypt are issuing Wildcard SSL Certificates. One step forward to encrypt the net.

Currently only base DNS validation is available, which means that you have to add a TXT record to the domain that you want to validate. It is a good practice to create different subdomains for the particular cases, for example – if you want to access your networking devices from web using a domain, it is nice to have: .net.maindomain.com and to list all the devices under this domain. Let’s Encrypt Certificates expire after 90 days.

The benefits from using HTTPS are endless.

Webfig is one of the widely used tools to configure Mikrotik devices. Unfortunately it is all through HTTP and in such case many vulnerabilities and exploits are waiting behind the door. With SSL certificate you can secure your device and what’s better – Let’s Encrypt made it even easier for the administrator.

Thankfully it can all be automated.

In the following article I will show you the terminal commands to do that and a bash script, which automatically:
check if certificate presents and if it has expired
check if the required files are uploaded to the device and if they need to be reuploaded

Install the Certbot

Certbot is a tool to obtain certs from Let’s Encrypt. It has to be executed for example once a week in order to check if cert.pem and privkey.pem are not older than 90 days.

wget https://dl.eff.org/certbot-auto
chmod a+x ./certbot-auto
sudo ./certbot-auto

 

Get the Certificate

sudo ./certbot-auto certonly \
--server https://acme-v02.api.letsencrypt.org/directory \
--manual --preferred-challenges dns \
-d *.net.maindomain.com

 

Verify the Domain

In order to verify the domain, you will need to add the txt record provided into the terminal to your subdomain.

You can check if the record has populated correctly by typing:

dig txt net.maindomain.com

 

Fun part, Let’s automate:

Add into your crontab the execution of certbot:

24 4 * * 3 certbot renew

In this way certbot will automatically try to renew and check if the certificate is older than 90 days. If it is, it will generate and replace the cert.pem and privkey.pem

Below, you will find the automation script which checks the files and certificate and keep them up to date. It has to be executed once a week, after the certbot:

 24 4 * * 4 /home/user/name_of_your_script.sh

 

The script below checks if there is a certificate and if the cert.pem and privkey.pem are up to date (certbot is changing them if there are less than 30 days from expiry of domain). If there is no certificate on the Mikrotik device, script automatically upload and configure it. If the cert.pem and privkey.pem are not up to date, it deletes the old ones, upload the new ones to the Mikrotik device and configures new certificates on the devices.

 

#!/bin/bash
#Mikrotik Wildcard Let’s Encrypt SSL Certificate Automation script
#MIlieva_noneblah@sdnix

#Change DEBUG to "on", if you want to debug the script, or "off" if you don't need this function. Currently it acts as "#", when it is in the begging of the line.
_DEBUG="on"
function DEBUG()
{
 [ "$_DEBUG" == "on" ] &&  $@
}

mikUser=yourUser
hostKey=/home/user/.ssh/user_key

#In this section add your own variables - your domain on “maindomain.com”. In most cases the #letsencrypt directory is the same: /etc/letsencrypt/live/.

certDomain=net.maindomain.com
certComNa=*.net.maindomain.com
certFile=cert.pem
prkeyFile=privkey.pem
certPath=/etc/letsencrypt/live/$certDomain/$certFile
prkeyPath=/etc/letsencrypt/live/$certDomain/$prkeyFile

#Here we check the initial stat of the files: cert.pem and privkey.pem and save them into variables, which we check. If certbot has updated the files, the stat will be different.

statCertCU=$(stat -c %Z /etc/letsencrypt/live/$certDomain/$certFile)
statPrkeyCU=$(stat -c %Z /etc/letsencrypt/live/$certDomain/$prkeyFile)

touch /etc/letsencrypt/live/$certDomain/certstat_$(date +'%Y-%m-%d').txt
DEBUG                           echo "certstat_$(date +'%Y-%m-%d').txt for $certDomain has been created. "
touch /etc/letsencrypt/live/$certDomain/prkeystat_$(date +'%Y-%m-%d').txt
DEBUG                           echo "prkeystat_$(date +'%Y-%m-%d').txt for $certDomain has been created."
cat /etc/letsencrypt/live/$certDomain/certstat_$(date +'%Y-%m-%d').txt > statCertIni
cat /etc/letsencrypt/live/$certDomain/prkeystat_$(date +'%Y-%m-%d').txt > statPrkeyIni

#We enter this function if there is no certificate, or if the stats of cert.pem and privkey.pem don't match.
#The script deletes the cert.pem and privkey.pem and scp the new one. 

function transKeyCert(){
        local mikHostFun="$1"
        local hostKeyFun="$2"
        local mikUserFun="$3"
        mikKey=$(ssh -i $hostKeyFun $mikUserFun@$mikHostFun '/if ([:len [/file find name='$prkeyFile']] > 0) do={:put "yes"}')
DEBUG                           echo "Info: $mikKey"
                if [[ $mikKey =~ .*yes.* ]]; then
DEBUG                           echo "Key on $mikHost do exist."
DEBUG                           echo "Key on $mikHost will be deleted."
                        ssh -i $hostKeyFun $mikUserFun@$mikHostFun /file remove $prkeyFile
                        scp -i $hostKeyFun $certPath $mikUserFun@$mikHostFun:cert.pem
                else
DEBUG                           echo "Key on $mikHost doesn't exist."
                        scp -i $hostKeyFun $prkeyPath $mikUserFun@$mikHostFun:privkey.pem
                fi

        mikCert=$(ssh -i $hostKeyFun $mikUserFun@$mikHostFun '/if ([:len [/file find name='$prkeyFile']] > 0) do={:put "yes"}')
DEBUG                           echo "Info: $mikCert"
                if [[ $mikCert =~ .*yes.* ]]; then
DEBUG                           echo "Certificate on $mikHost do exist."
DEBUG                           echo "Certificate on $mikHost will be deleted."
                        ssh -i $hostKeyFun $mikUserFun@$mikHostFun /file remove $certFile
                        scp -i $hostKeyFun $certPath $mikUserFun@$mikHostFun:cert.pem
                else
DEBUG                           echo "Certificate on $mikHost doesn't exist."
                        scp -i $hostKeyFun $certPath $mikUserFun@$mikHostFun:cert.pem
                fi
}

DEBUG                           echo "Domain is: $certDomain"
DEBUG                           echo "Path to certificate is: $certPath"
DEBUG                           echo "Path to private key is: $prkeyPath"

#In this section we check every Mikrotik device in the list below if their stats of privkey.pem and cert.pem are the same
# and if they are it gives answer that everything is up to date, otherwise we enter the function: transKeyCert

for  mikHost in `cat /home/user/youMikListFile.txt`;
do

DEBUG                           echo "Debug $mikHost"
DEBUG                           echo "Debug $mikUser"
DEBUG                           echo "Debug $hostKey"

        checkCert=$(ssh -i $hostKey $mikUser@$mikHost '/if ([:len [/certificate find common-name='$certComNa']] > 0) do={:put "yes"}')
                if [[ $checkCert =~ .*yes.* || $statPrkeyCU == $statPrkeyIni || $statCertCU == $statCertIni ]]; then
DEBUG                           echo "On $mikHost device in mikList.txt, the ssl certificates seems to be up to dated."
                else
DEBUG                           echo "Removal of old certificate, cert and key file will be executed. Transfer of the new key and cert file wil function is going to be executed."
                        transKeyCert "$mikHost","$hostKey","$mikUser"
                        ssh -i $hostKey $mikUser@$mikHost /certificate import file-name=cert.pem passphrase=\"\"
DEBUG                           echo "Cert.pem file has been imported."
                        ssh -i $hostKey $mikUser@$mikHost /certificate import file-name=privkey.pem passphrase=\"\"
DEBUG                           echo "PrivKey.pem file has been imported."
                        ssh -i $hostKey $mikUser@$mikHost /ip service set certificate=cert.pem_0 www-ssl
DEBUG                           echo "Cert.pem_0 has been set as certificate."
                        ssh -i $hostKey $mikUser@$mikHost /ip service enable www-ssl
DEBUG                           echo "WWW-SSL service has been enabled."
                        stat -c %Z /etc/letsencrypt/live/$certDomain/$certFile > /etc/letsencrypt/live/$certDomain/certstat_$(date +'%Y-%m-%d').txt
DEBUG                           echo "Stat of $certFile for $certDomain has been saved to certstat_$(date +'%Y-%m-%d').txt in $certDomain ."
                        stat -c %Z /etc/letsencrypt/live/$certDomain/$prkeyFile > /etc/letsencrypt/live/$certDomain/prkeystat_$(date +'%Y-%m-%d').txt
DEBUG                           echo "Stat of $prkeyFile for $certDomain has been saved to prkeystat_$(date +'%Y-%m-%d').txt in $certDomain ."
                fi

done

 

Unfortunately there is no http-to-https redirection in Mikrotik devices, so if you type, just:

mik1.net.maindomain.com

 

Nothing will happen, you will need to type the full https path:

https://mik1.net.maindomain.com
All of the examples are configured on Cloud Virtual Machine and Cloud Router Mikrotik, provided by: CloudBalkan

7 Replies to “Mikrotik Let’s Encrypt Wildcard SSL Certificate”

  1. Hi! This script can be installed directly in mikrotik or it has to be installed in a computer that is connected to the mikotik?

    1. Hey,
      Yeah, currently it is only for linux. The solution for Windows will be to rewrite the script in powershell (I am actually thinking about this 😀 ).

  2. Hi,

    Thanks for the Mikrotik update script, great article!

    How does the Certbot Cron job renew the certificate without the DNS TXT record being updated manually? It looks like, that one step is missing: you need to use a Certbot plugin, eg. certbot-dns-cloudflare if you use Cloudflare, to update the TXT record automatically using the DNS service provider’s API, or the record has to be updated locally if you are hosting your own DNS server for the given domain.

    The challenge string you need to add to the TXT record will change for each renewal.

    Thanks,
    Tamas.

Leave a Reply

Your email address will not be published. Required fields are marked *