Wesley Dean
Using Let's Encrypt SSL / TLS Certificates with Mikrotik RouterOS image

Using Let's Encrypt SSL / TLS Certificates with Mikrotik RouterOS

Background

Mikrotik produces a variety of exceptional networking products including routers, switches, access points, and much more. Many of their devices run an operating system known as RouterOS. RouterOS provides a variety of interfaces, including a terminal, a web interface, API access, and more. That said, RouterOS devices, regardless of the foundation upon which they're built, don't provide a true shell -- you can't get a Bash shell, nor can you install arbitrary applications. Therefore, if we're going to use outside tools like Let's Encrypt or certbot, then we need to run them on a general-purpose system (e.g., Linux).

I use, enjoy, and advocate for Mikrotik devices running RouterOS. I'm reminded of my experiences of working with Cisco hardware -- the good parts, not the anxiety-ridden, late-night service calls to get our 6513s back online. Mikrotik gives me the configurability and stability of enterprise-grade hardware at prices that are closer to consumer-grade hardware.

In my home network, I use all Mikrotik networking equipment. It's rock-solid stable and performs extremely well.

One of my challenges, however, was configuring SSL / TLS on the web interface. Sure, RouterOS provides functionality to generate certificates and even to self-sign them. That's cool. What I really wanted was for the devices to use certificates signed by certificate signing authorities that my browser would trust out of the box.

I use Let's Encrypt in a bunch of places with certbot dutifully doing its thing every night to make sure when renewal time came, we were good to go with little to no interruption or manual intervention.

SSL / TLS Certificates

When a web server supports HTTPS, it does so using SSL / TLS certificates. SSL stands for Secure Socket Layer while TLS stands for Transport-Layer Security. Think of TLS as a backwards-compatible evolution of SSL.

Both SSL and TLS are built on top of public key encryption, otherwise known as PKI (Public Key Infrastructure). PKI uses a cryptographic key with private and public portions. The public portion can be shared with the world. The private portion, on the other hand, is meant to be.. wait for it.... private. The public portion of a PKI key is used to encrypt something that only the private key can unencrypt. SSL / TLS certificates use this concept and combine it with certificates that are cryptographically signed by a certificate signing authority. The authenticity of the signature can be verified by users by examining the chain of signatures associated with the certificate authority. Because of this, a web browser can say that a certificate is trusted (or warn the user if a certificate is not trusted).

This is a good thing. It's not super great if an arbitrary organization can generate and have a trusted signature associated with a certificate that isn't theirs -- it's like me being able to say, "this site is safe because it's example.com when, in fact, it's really example.com."

This is the second core function of SSL / TLS certificates -- to prove the identity of the user. Encryption's great, but encryption plus the ability to prove identity is even better. I know not only that my communications with example.com are encrypted, but I know that the other end of the communication is, in fact, example.com.

Let's Encrypt

That all sounds great. Very cool. How does one get a certificate signed? They use a certificate (signing) authority or CA.

CAs have infrastructure and use bandwidth and there's software that needs to run and there are people who need to support the system. So, there are costs associated with using most CAs. Some CAs provide what amounts to an insurance policy based on the identity assurance of the signing. That is, folks can pay even more money to have a deeper investigation into their identity so that users can be even more certain that they are who they say they are.

The other thing with many CAs is that it takes some time for them to verify the identity of the organization making the request -- it's difficult to automate this initial interaction. Renewals are typically a bit simpler and faster, but it's that first exchange that can take a few days.

Let's Encrypt, on the other hand, is a little different. Instead of operating on a for-profit model, they are a non-profit that relies on donations and support from outside organizations. These outside organizations have an interest in more people encrypting more things -- a safer Internet is safer for everyone.

Identity Assurance Challenges

Let's Encrypt is also different in that they support a mechanism for fully automated certificate signature. That bit about identity assurance? Let's Encrypt addresses this by requiring that each interaction respond to a "challenge." A challenge is where Let's Encrypt says, "you say you're a server on the Internet at example.com. Prove it."

The protocol Let's Encrypt uses ACME (Automatic Certificate Management Environment). ACME supports several challenge types:

  • HTTP-01: the CA provides a random string to the respondent; the respondent must create a file at a well-known address on the server that includes that random string. Because the respondent can serve traffic from the server they say they are, ACME concludes that they are who they say they are.

  • DNS-01: the CA provides a random string to the respondent; the respondent must create a DNS record in a well-known DNS record. Because the respondent can update the DNS records for the server they say they are, ACME concludes that they say they are.

RouterOS plus Let's Encrypt

So.. let's talk about RouterOS and Let's Encrypt. Can they play nicely? Yes!

RouterOS 7 (September 2024) supports HTTP-01 ACME challenges natively. Awesome, right?!

Yup. Very awesome!

Then why are we here? Well, remember that bit about HTTP-01 challenges requiring the placement of a random string in a file at a well-known location? Well, in order for the other end of the connection to verify that the file at that location has the random string that was provided, it has to connect back to the server and request that file. This means that there needs to be a server listening for incoming web traffic and this server needs to be exposed to the Internet. That is, port 80 (unencrypted HTTP traffic) needs to be open to the entire Internet. One of the most popular ports carrying a very large portion of traffic on the Internet -- that is, one of the biggest targets for potential attack -- needs to be open to the entire Internet.

That's a risk. Mikrotik does a lot of work to minimize and mitigate that risk but it is still a risk -- a risk that needs to be evaluated.

Alternative Challenges

If one does not wish to accept the risk of leaving port 80 open to the Internet, ACME provides an alternative challenge: DNS-01.

In order for DNS-01 to work, a DNS record needs to be created with a random string. The good news is that this doesn't need to be done on the server that will serve traffic where the certificate will be used (i.e., it can be created from a completely different server). More good news is that it doesn't involve opening up any ports on any server to anywhere.

That is, we can run a tool on some back-office system that only needs to be able to run tools to update DNS records.

There are a bunch of DNS services that support mechanisms to automate the changing of DNS records using API calls. There are a bunch of DNS services that Let's Encrypt supports. Fortunately for us, there's a lot of overlap between these two: there are automated services we can use with Let's Encrypt, such as Amazon's Route 53 DNS service.

In the background, when someone requests a certificate signature and requests a DNS-01 challenge, Let's Encrypt provides a string and then starts querying the public DNS servers for proof that the provided string is there. On our side, certbot connects to the DNS service and puts that string where it needs to be. Once the challenge is complete, it will remove that record.

Updating RouterOS with a Let's Encrypt Certificate

Now that we can create certificates and get them signed with Let's Encrypt, we can take the next step of configuring RouterOS to use them. We can't run the scripts to generate or submit certificates for signature with DNS-01 challenges, so we need to run that scripting from.. not the RouterOS device. This can be a desktop, a server, virtual machine, a container, a Raspberry Pi.. all it needs is to be able to run certbot and make outgoing API calls. It doesn't need to be accessible from the Internet.

A Script to Update RouterOS Devices with Let's Encrypt Certificates

Because I enjoy automating things and I don't care to do the exact same thing twice, I put together a tool to automate the certificate upload, import, and configuration process for RouterOS devices.

Looking around the Internet, I found a script by kiprox and uploaded to GitHub. The kiprox/mikrotik-ssl repo included the majority of what wanted and served as an excellent starting point. It hadn't been updated in a few years, so I put some polish on the excellent and functional work they started.

The script I wrote is available on GitHub as a public repository; the code is made available under the GPL-3 license.

The script is written in Bash and makes extensive use of OpenSSH and key-based authentication. Additional documentation is available in the repository.

Generating a Signed Certificate

Just like with the original source script, my script doesn't generate or sign certificates -- all it does is upload them -- the signed certificate and the private key -- to a RouterOS device, import them into the RouterOS certificate store, and configure services on the RouterOS device to use them.

That said, all of the usual steps to generate and sign a certificate using certbot and Let's Encrypt all work just fine and the script can import them without modification.

Configuring the Script

The script can be configured either through configuration files or with command line parameters. The script's documentation goes into detail about how each parameter is used and how to set it.

Running the Script

Once the signed certificate is ready and the configuration is complete, the script can take care of the rest of the work. It can be run from a cron job, as a container, as a post-validation hook, through an automation system like Jenkins, or as an ad-hoc process.

Outcome

I'm using this script with all of my RouterOS-based devices. By default, the script will configure www-ssl, api-ssl, and SSTP services to use the signed certificates. This means that when I point my browser at my devices' web interfaces or use scripts to interact with the API or use Secure Socket Tunneling Protocol (SSTP), I can verify the connections cryptographically based on signed certificates that are fully chained to trusted root CAs. I know that my connections are encrypted and I am assured that the systems with which I'm communicating are, in fact, those with whom I intend to communicate.