Homelab Certs
What Exactly is this all about
When interacting with the various pieces of my home network over the years I’ve frequently gotten irritated with certificate issues, mostly in the form of rejected connections and having to click through browser warnings. In the interests of saving myself a lot of annoying (and probably slightly dangerous) clicking, I decided to deploy TLS certs across my various devices. But I’m also very lazy and I didn’t want to have to deal with cert expirations or rolling internal certs on a regular basis. Ideally I would use something like Let’s Encrypt, but for the automated challenges to work I would have to expose parts of my network to the internet that I’d really rather not. So I decided to run my own CA and then automate the renewal process inside of my network.
Setup CA Server
Pick an ednpoint that will act as certificate authority. In my case I have a machine that acts as a central point for a variety of network functions and so it makes sense to use it as a CA as well. We’ll use smallstep to configure it to act as a CA.
Install the smallstep tool
Follow the [installation instructions]. In my case I am using a NUC running Ubuntu so the commands look like:
sudo curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg -o /etc/apt/trusted.gpg.d/smallstep.asc && \
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/smallstep.asc] https://packages.smallstep.com/stable/debian debs main' \
| sudo tee /etc/apt/sources.list.d/smallstep.list
sudo apt update && sudo apt install -y step-cli
Intitialize the CA
Getting your own CA started is realtively straightforward. The first command will be:
step ca init
This will alunch an interactive process that will create a few important files and give you some important data that will be needed later. It will create 2 pairs of certificate and key files. The root cert/key pair forms the base layer of cryptographic trust for the chain. You want to back these up somewhere and keep them safe. The intermediate cert/key pair will be used to actually sign certs. In order to create trust across the network this intermediate cert will get installed in a variety of places, which is why it is deliberately offset from the root cert. For more info about this checkout this guide from Digicert
Client Setup
Now to configure the client devices to use the CA and setup automatic renewal. If you wanted you could use CertBot to do this, though it can be a bit finnicky if you aren’t using a valid TLD. For simplicity and consistency I continued to use step-cli.
Common
The following steps apply to all linux based client devices: Install step cli on client devices
sudo curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg -o /etc/apt/trusted.gpg.d/smallstep.asc && \
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/smallstep.asc] https://packages.smallstep.com/stable/debian debs main' \
| sudo tee /etc/apt/sources.list.d/smallstep.list
sudo apt update && sudo apt install -y step-cli
Bootstrap CA and install in trust store:
sudo step ca bootstrap --ca-url https://ca.<domain>.home:8443 --fingerprint <fingerprint> --install
allow step to bind to 80 for ACME:
sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/bin/step-cli
This last step is important as the step executable will need to bind to port 80 (which is a privileged port) and you don’t want your renewals to be running as root for security reasons.
Service Specific
Now to configure a few services that I care about to use certs off of the CA and renew them.
Pihole
First create an appropriate certificate for the pihole. You want to ensure that the SANs match the IP addresses and DNS names associated to each service.
Create Cert
step ca certificate pihole.<domain>.home pihole.crt pihole.key --san <IP> --san pihole.<domain>.home --ca-url 'https://ca.<domain>.home:8443'
Renewal Service
[Unit]
Description=Pihole Dashboard cert renewal
[Service]
Restart=on-failure
RestartSec=1
User=pihole
ExecStart=/bin/bash -c "/opt/pihole-cert-scripts/renew-cert.sh"
WorkingDirectory=/opt/pihole-cert-scripts
StandardOutput=append:/var/log/pihole-dashboard-renew.log
StandardError=append:/var/log/pihole-dashboard-renew.log
[Install]
WantedBy=multi-user.target
It depends on two smaller script files. renew-cert.sh, as its name implies, simply renews the certificate. It looks like this:
#!/bin/sh
/usr/bin/step ca renew --daemon --force \
--exec "/opt/pihole-cert-scripts/concatenate-restart.sh" \
--ca-url https://ca.<domain>.home:8443 \
/opt/pihole-cert-scripts/pihole.crt /opt/pihole-cert-scripts/pihole.key;
However, this script by itself is not sufficient because the pihole expects the certificate to be in a single PEM formatted file rather than a cert/key pair. To accomplish this the renewal is called with the --exec flag to execute a script that concatenates the two files into a single PEM and then restarts the service. That script looks like this:
#!/bin/sh
/usr/bin/cat pihole.key pihole.crt > ./pihole.pem;
/usr/bin/systemctl daemon-reload;
/usr/bin/systemctl restart pihole-FTL
Proxmox
Proxmox is nice because it has native acme support built in and it will set all of the vms that it creates to automatically trust your CA as well. Configure ACME certs on proxmox:
pvenode acme account register <domain>-ca root@<domain>.home --directory https://ca.<domain>.home:8443/acme/acme/directory
Wazuh Dashboard
Wazuh is also fairly straightforward.
step ca certificate wazuh.<domain>.home wazuh.pem wazuh.key --san <IP> --ca-url 'https://ca.<domain>.home:8443' --provisioner acme
sudo cp wazuh.pem /etc/wazuh-dashboard/certs/
sudo cp wazuh.key /etc/wazuh-dashboard/certs/
sudo chown wazuh-dashboard:wazuh-dashboard /etc/wazuh-dashboard/certs/wazuh.pem
sudo chown wazuh-dashboard:wazuh-dashboard /etc/wazuh-dashboard/certs/wazuh.key
sudo chmod 440 /etc/wazuh-dashboard/certs/wazuh.key
sudo chmod 444 /etc/wazuh-dashboard/certs/wazuh.pem
Automate renewal with daemon mode:
sudo step ca renew --daemon --ca-url https://ca.<domain>.home:8443 /etc/wazuh-dashboard/certs/wazuh.pem /etc/wazuh-dashboard/certs/wazuh.key &
This will actually die on logout so I need to create service. It is very similar to the service used for the pihole so I won’t repost the code here. The main thing is that you have to repoint the config in /etc/wazuh-dashboard/opensearch_dashboards.yml and afterwards remember to restart the dashboard service.
sudo systemctl daemon-reload
sudo systemctl restart wazuh-dashboard