LetsEncrypt for Security Onion

When doing a proof of concept for this amazing bundle of open source software, it became apparent that one small Achilles heel was the provisioning of a self-signed certificate as part of the install, which then leads to a number of ‘compromises’ with setup in the future.

Just want the code? Go here

To be more exact- the installer won’t accept an FQDN as the hostname, and generates a self-signed certificate based on the hostname you supply, so it’s impossible for it to match seamlessly into your Fleet install. You either have to set –insecure’ flags which theoretically makes your install a bit flaky (renewing requires you to replace the intca.crt on the clients), or use ‘real’ certificates (this is just a proof of concept…), but I wasn’t happy with either of those solutions.
In an attempt to fix this, I spent a few days looking at how things were set up and came up with this.
Warning- in a production environment you should (probably?) use real certificates, but this solves a number of issues with the quick deployment of security onion. There are a couple of other docs about this

https://github.com/Security-Onion-Solutions/securityonion/discussions/2883

https://github.com/Security-Onion-Solutions/securityonion/discussions/5117

But these are both out of date (so-firewall has changed), leave out bits of instructions or just get us ‘almost there’. I thank these previous posters as I have heavily leaned on their info, as well as this previous post about setting up Fleet

Here’s what we need to do-

  1. Install a cert method
  2. Get certs
  3. Move them into place
  4. Make sure our software uses them

Install Lego for Letsencrypt

But we can’t use certbot because it requires port 80, which nginx is already using. So let’s use Lego as we have in other projects, it can be used as a binary or as a Docker container. Maybe we can convince Security Onion to use it in their default shipping state?
Install lego so we can get a lets encrypt cert-

https://go-acme.github.io/lego/usage/cli/obtain-a-certificate/

wget https://github.com/go-acme/lego/releases/download/v4.8.0/lego_v4.8.0_linux_amd64.tar.gz

Unpack it

gzip -d lego_v4.8.0_linux_amd64.tar.gz && tar -xvf lego_v4.8.0_linux_amd64.tar

Move the binary to /usr/bin

sudo mv lego /usr/bin/

I’m using Cloudflare, so go to the Cloudflare dashboard and create a token that can be used to generate a CSR for Letsencrypt (not covering this bit, you can do it yourself and it is fairly easy). Result-
Cloudflare API Token
A9fGkL2uXANNZPiHqBvwQIthpcBYEQI

(Remember these credentials are fake, script kiddies!). Move to the directory that contains lego- this isn’t needed but it does mean you don’t have to add the binary to your $PATH

cd /usr/bin

Now, we’re going to cheat a bit here because we’ll get the certs first manually, then set up the renewals automatically.
Execute command to get certs-
CF_DNS_API_TOKEN=A9fGkL2uXANNZPiHqBvwQIthpcBYEQI \
lego --dns cloudflare --domains xxx.servicemax.com.au --email xxx@servicemax.com.au run

You should get a response like this-
Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
Do you accept the TOS? Y/n

y
2022/06/13 12:23:22 [INFO] acme: Registering account for xxx@servicemax.com.au
!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "/usr/bin/.lego/accounts".
You should make a secure backup of this folder now. This configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular backups of this folder is ideal.
........
2022/06/13 12:23:30 [INFO] [xxx.servicemax.com.au] Server responded with a certificate.

And as you see above the lego account config has been saved in /usr/bin/.lego/accounts and should be backed up!

Our shiny new certs are created, verify there’s now 4 new files –

/usr/bin/.lego/certificates/xxx.servicemax.com.au.crt
/usr/bin/.lego/certificates/xxx.servicemax.com.au.issuer.crt
/usr/bin/.lego/certificates/xxx.servicemax.com.au.json
/usr/bin/.lego/certificates/xxx.servicemax.com.au.key

Lego lists these certs as-

  • example.com.crt is the server certificate (including the CA certificate),
  • example.com.key is the private key needed for the server certificate,
  • example.com.issuer.crt is the CA certificate, and
  • example.com.json contains some JSON encoded meta information.

The .crt and .key files are PEM-encoded x509 certificates and private keys. If you’re looking for a cert.pem and privkey.pem, you can just use example.com.crt and example.com.key.

The nginx config file in
/opt/so/saltstack/default/salt/nginx/etc/nginx.conf
Tells us that it is expecting the certs in
ssl_certificate "/etc/pki/nginx/server.crt";
ssl_certificate_key "/etc/pki/nginx/server.key"

BUT- the certs that nginx will actually use are-
/etc/pki/managerssl.crt
/etc/pki/managerssl.key

So we need to move the new certs there with a script, this one will also renew the certs
So let’s make a directory for our script
sudo mkdir -p /etc/letsencrypt-scripts
Now make your first script-
sudo vi /etc/letsencrypt-scripts/le-renew.sh

and add the contents from GitHub here

Then do the same for this second script that renames, moves the certs into place and restarts the web server

sudo vi /etc/letsencrypt-scripts/move-certs.sh

Make them executable
sudo a+x /etc/letsencrypt-scripts/le-renew.sh
sudo a+x /etc/letsencrypt-scripts/move-certs.sh

Remember we need to add our variables, you can add a simple file containing each of these to your letsencrypt-scripts directory with

echo xx.servicemax.com.au >> domain
echo email@servicemax.com.au >> email
echo A9fGkL2uXANNZPiHqBvwQIthpcBYEQI >> token

Backup the old/ existing self signed certs (location noted above /etc/pki )and test the new script
sudo sh ./le-renew.sh

Now reboot and check if your server is using the new certificate (didn’t actually need the reboot)

All good? Now add a cron job to renew the cert. Open the crontab editor with

crontab -e

and add something like this to the bottom of that file-

5 2 20 * * /etc/letsencrypt-scripts/le-renew.sh 2> /dev/null

This is set to fire at 2:05am on the 20th day of each month. I think that’s random enough!

Anyway, if you can find ways to improve this please let me know…

Recent posts