Homelab Ssl

I’ve always liked playing around with web stuff, and I’m also getting into self-hosting services strictly on my private network. I’ve got a bit of a funky setup that helps me do that. Let’s say I want to deploy a notes app onto a server I call tombombadil.

This is all well and good, except that I can only access with HTTP, not HTTPS. That would prevent the hosted service from using fancy features like service workers, web bluetooth, or even accessing the clipboard. The solution would be to configure nginx to use an appropriate SSL certificate, and there are plenty of references for how to do that if you have a certificate. I’m documenting here how to get a good certificate.

There are three main options:

  1. Get a “real” certificate. I don’t want to do that, because my services are not going to be on the Internet. No one needs to trust my certificate but me.
  2. Self-signed certificates. There are limitations to self-signed certificates, and a self-signed certificate is only trusted if it is manually trusted on each device each time it changes.
  3. Create a root certificate, and use it to sign the SSL certificate. This means that updating the SSL certificate does not mean it needs to be newly trusted on every device. Root certificates can have a much longer expiration time (e.g., 25 years).

For my use case above, if only tombombadil runs a reverse proxy, then I only need one SSL certificate. However, Chrome will not trust an SSL certificate for *.home.arpa, so each service will need to be individually named in the certificate. (Could I use a wildcard for something like *.c.home.arpa? Not sure, but I want to avoid the typing…) In that case, the third option seems like the right one for me.

To generate an appropriate root certificate:

# Private key
openssl genrsa -out MyHomeCA.key 4096
# Root certificate
openssl req -x509 -new -nodes -key MyHomeCA.key -sha256 -days 3650 \
  -out MyHomeCA.pem \
  -subj "/CN=My Home Root CA/O=Home Lab/C=US"

MyHomeCA.pem is the file you’ll want to add to your trusted root certificates. Note that Windows doesn’t recognize the .pem extension, but if you select it anyway then Windows will accept it.

Here’s the script I used to generate a signed SSL cert; this is what you configure nginx to use.

CA_KEY="MyHomeCA.key"
CA_CERT="MyHomeCA.pem"

# "canonical" host listed first
HOSTS=(tombombadil adminer git links notes paste)

printf -v SAN_LIST 'DNS:%s.home.arpa,' "${HOSTS[@]}"
SAN_LIST=${SAN_LIST%,}

openssl x509 -req \
  -in <( openssl req -new -nodes -newkey rsa:2048 \
           -keyout homearpa.key \
           -subj "/CN=${HOSTS[0]}.home.arpa"
   ) \
  -CA "$CA_CERT" -CAkey "$CA_KEY" -CAcreateserial \
  -out homearpa.crt -days 398 -sha256 \
  -extfile <(cat <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = $SAN_LIST
EOF
)

If I weren’t using a CA, the openssl command could have been simpler:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout homearpa.key -out homearpa.crt -subj "/CN=${HOSTS[0]}.home.arpa" \
  -addext "subjectAltName=${names%,}"