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-
- Install a cert method
- Get certs
- Move them into place
- 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, andexample.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…