Skip Navigation

Do you run a private CA? Could you tell me about your certificate setup if you do?

Hi, I was looking at private CAs since I don't want to pay for a domain to use in my homelab.

What is everyone using for their private CA? I've been looking at plain OpenSSL with some automation scripts but would like more ideas. Also, if you have multiple reverse-proxy instances, how do you distribute domain-specific signed certificates to them? I'm not planning to use a wildcard, and would like to rotate certificates often.

Thanks!


Edit: thank you for everyone who commented! I would like to say that I recognise the technical difficulty in getting such a setup working compared to a simple certbot setup to Let's Encrypt, but it's a personal choice that I have made.

61 comments
  • If you want to run your own pki with self-signed certificate in your homelab I really encourage you to read through this tutorial. There is a lot to process and read and it will take you some time to set everything up and understand every terminology but after that:

    • Own self-signed certificate with SAN wildcards (https://*.home.lab)
    • Certificate chain of trust
    • CSR with your own configuration
    • CRL and certificate revocation
    • X509 extensions

    After everything is in place, you can write your own script that revoks, write and generates your certificate, but that is another story !

    Put everything behind your reverse proxy of choice (traefik in my case) and serve all your docker services with your own self-signed wildcard certificates ! It's complex but if you have spare time and are willing to learn something new, it's worth the effort !

    Keep in mind to never expose such certificates on the wild wild west ! Keep those certificate in a closed homelab you access through a secure tunnel on your LAN !

    edit

    Always take notes, to keep track of what you did and how you solved some issues and always make some visuals to have a better understanding on how things work !

    • never expose such certificates on the wild wild west ! Keep those certificate in a closed homelab you access through a secure tunnel on your LAN !

      I'm curious, what's the reason?

      • In many architectures in which certificates are used, a client with a valid certificate is a trusted client, so a certificate falling into the wrong hands is problematic.

    • Thank you. Could you explain a bit more about your setup and the aspects I should be looking at? Specifically:

      • Certificate chain of trust: I assume you're talking about PKI infrastructure and using root CAs + Derivative CAs? If yes, then I must note that I'm not planning to run derivative CAs because it's just for my lab and I don't need that much of infrastructure.
      • I have yet to figure out the CRL part with OpenSSL, I'll have to read more about it. Thanks.
      • I do not know what X.509 extensions are and why I need them. Could you tell me more?
      • I'm also considering client certificates as an alternative to SSO, am I right in considering them this way? I will also have to think about what I could do what clients without a client certificate or my root CA's certificate in their certificate store (maybe run another instance which doesn't need all of the encryption and setup I'm doing and somehow redirect such clients there whilst logging their traffic?).

      Thanks for the mention, I was looking at a script to automate certificate generation and revocation too.

      Since we're talking about reverse-proxies, I'll mention that I plan to run an instance of HAProxy per podman pod so that I terminate my encrypted traffic inside the pod and exclusively route unencrypted traffic through local host inside the pod. I'm doing this because I do not want to see any unencrypted traffic in my network. Of course, this is some more overhead but I think this is doable. I got this idea from another post I made a while back. Of course, that means that every pod on my network (hosting an HAProxy instance) will be given a distinct subdomain, and I will be producing certificates for specific subdomains, instead of using a wildcard.

      Thanks, I'll be sure to document my progress as I go.

      • Certificate chain of trust: I assume you’re talking about PKI infrastructure and using root CAs + Derivative CAs? If yes, then I must note that I’m not planning to run derivative CAs because it’s just for my lab and I don’t need that much of infrastructure.

        An intermediate CA could potentially be useful, but isn't really needed in self-signed CA. But in case you have to revoke your rootCA, you have to replace that certificate on all your devices, which can become a lot of hassle if you share that trusted root CA with family/friends. By having a intermediate CA and hiding your root CAs private key somewhere offline, you could take away that overheat by just revoking the intermediate CA and updating the server certificate with the newly signed Intermediate bundle and serving that new certificate through the proxy. (Hope that makes sense? :|)

        I do not know what X.509 extensions are and why I need them. Could you tell me more?

        This will probably give you some better explanation than I could :| I have everything written in a markdown file, and reading through my notes I remember I had to put some basic constraints TRUE in my certificates to make them work on my android root store ! Some are necessary to make your root CA work properly (like CA:True). Also if you want SAN certificates (multidomaine) you have to put them in your x509 extensions.

        ’m also considering client certificates as an alternative to SSO, am I right in considering them this way?

        Ohhh, I don't know... I haven't installed or used any SSO service and thinking of MFA/SSO with authelia in the future ! My guess would be that those are 2 different technologies and could work together? Having self-signed CA with a 2FA could possible work in a homelab but I have no idea how because I haven't tested it out. But thinks to consider if you want clients certificates for your family/friends is to have a intermediate CA in case of revocation, you don't have to replace the certificate in their root store every time you sign a new Intermediate CA.

        I’ll mention that I plan to run an instance of HAProxy per podman pod so that I terminate my encrypted traffic inside the pod and exclusively route unencrypted traffic through local host inside the pod.

        I have no idea about HAProxy and podman and how they work to encrypt traffic. All my traffic passes through a wireguard tunnel to my docker containers/proxy which I consider safe enough? Listening to all my traffic with wireshark seamed to do exactly what I'm expecting but I'm not an expert :L So I cannot help you further on that topic. But I will keep your idea in my notes to see If there could be further improvement in my setup with HAProxy and podman compared to docker and traefik through wireguard tunnel.

        Of course, that means that every pod on my network (hosting an HAProxy instance) will be given a distinct subdomain, and I will be producing certificates for specific subdomains, instead of using a wildcard.

        Openssl SAN certificates are going to be a life/time saver in your setup ! One certificat with multidomian !


        I'm just a hobby homelaber/tinkerer so take everything with caution and always double check with other sources ! :) Hope it helps !


        Edit

        Thinking of your use case I would personally create a rootCA and an intermediateCA + certificate bundle. Put the rootCA in the trusted store on all your devices and serve the intermediateCA/certificate bundle with your proxy of choice. Signing the certificate with SAN X.509 extension for all your domains. Save your rootCA's key somwhere offline to keep it save !

        The links I gave you are very useful but every bit of information is a bit dispatched and you have to combine them by yourself, but it's a gold mine of information !

  • I'm not using a private CA for my internal services, just plain self-signed certs. But if I had to, I would probably go as simple as possible in the first time: generate the CA cert using ansible, use ansible to automate signing of all my certs by the CA cert. The openssl_* modules make this easy enough. This is not very different from my current self-signed setup, the benefit is that I'd only have to trust a single CA certificate/bypass a single certificate warning, instead of getting a warning for every single certificate/domain.

    If I wanted to rotate certificates frequently, I'd look into setting up an ACME server like [1], and point mod_md or certbot to it, instead of the default letsencrypt endpoint.

    This still does not solve the problem of how to get your clients to trust your private CA. There are dozens of different mechanisms to get a certificate into the trust store. On Linux machines this is easy enough (add the CA cert to /usr/local/share/ca-certificates/*.crt, run update-ca-certificates), but other operating systems use different methods (ever tried adding a custom CA cert on Android? it's painful. Do other OS even allow it?). Then some apps (Web browsers for example) use their own CA cert store, which is different from the OS... What about clients you don't have admin access to? etc.

    So for simplicity's sake, if I really wanted valid certs for my internal services, I'd use subdomains of an actual, purchased (more like renting...) domain name (e.g. service-name.internal.example.org), and get the certs from Let's Encrypt (using DNS challenge, or HTTP challenge on a public-facing server and sync the certificates to the actual servers that needs them). It's not ideal, but still better than the certificate racket system we had before Let's Encrypt.

    • Finally got around to replying to the comments I got haha!

      Thanks for the explanation. I'm curious why you're not running your own CA since that seems to be a more seamless process than having to deal with ugly SSL errors for every website, every time you rotate the certificate.

      I'm wondering about different the process is between running an ACME server and another daemon/process like certbot to pull certificates from it, vs writing an ansible playbook/simple shell script to automate the rotation of server certificates.

      About my clients: I am likely never going to purchase Apple products since I recognise how much they lock down their device. Unfortunately, there are not that many android devices in the US with custom ROM support. With that said, I do plan to root all of my Android devices when KernelSU matures (in about a year, I think) - I'm currently reading up on how to insert a root and client certificate into Android's certificate store, but I think it's definitely possible. Other than that, I might have a throwaway Windows VM sometimes, which is doable, alongside a Void linux box with a Debian chroot. All in all, fairly doable, just a bit of work to automate.

      Thanks!

      • I’m curious why you’re not running your own CA since that seems to be a more seamless process than having to deal with ugly SSL errors for every website

        It's not, it's another service to deploy, maintain, monitor, backup and troubleshoot. The ugly SSL warning only appears once, I check the certificate fingerprint and bypass the warning, from there it's smooth sailing. The certificate is pinned, so if it ever changes I would get a new warning and would know something shady is going on.

        every time you rotate the certificate.

        I don't really rotate these certs, they have a validity of several years.

        I’m wondering about different the process is between running an ACME server and another daemon/process like certbot to pull certificates from it, vs writing an ansible playbook/simple shell script to automate the rotation of server certificates.

        • Generating self-signed certs is ~40 lines of clean ansible [1], 2 lines of apache config, and one click to get through the self-signed cert warning, once.
        • Obtaining Let's Encrypt certs is 2 lines of apache config with mod_md and the HTTP-01 challenge. But it requires a domain name in the public DNS, and port forwarding.
        • Obtaining certs from a custom ACME CA is 3 lines of apache config (the extra line is to change the ACME endpoint) and a 100k LOC ACME server daemon running somewhere with its own bugs, documentation, deployment and upgrade management tooling, config quirks... and you still have to manage certs for this service. It may be worth it if you have a lot of clients who don't want to see the self-signed cert warning and/or worry about their private keys being compromised and thus needing to rotate the certs frequently (you still need to protect the CA key...)

        likely never going to purchase Apple products since I recognise how much they lock down their device

        hear hear

        there are not that many android devices in the US with custom ROM support. With that said, I do plan to root all of my Android devices when KernelSU mature

        I bought a cheap refurbished Samsung, installed LineageOS on it (Europe, but I don't see why it wouldn't work in the US?), without root - I don't really need root, it's a security liability, and I think the last time I tried Magisk it didn't work. The only downside is that I have to manually tap Update for F-Droid updates to run (fully unattended requires root).

        I’m currently reading up on how to insert a root and client certificate into Android’s certificate store, but I think it’s definitely possible.

        I did it on that LineageOS phone, using adb push, can't remember how exactly (did it require root? I don't know). It works but you get a permanent warning in your notifications telling you that The network might be monitored or something. But some apps would still ignore it.

61 comments