Your own private CA authority ? ACME for autoprovisionning ? yep !

Install and configure your own private CA using step-ca and acme.sh on the remote machines

August 10, 2020
sysadmin certificate authority step-ca step-cli pi-hole acme.sh https self hosting

So you want to self host part 4 ?

So you have a bunch of services on your LAN by now, and they are hosted on private IPs, that bothers you because, well, could be nice to have https and a nice hostname for it no ?

Let’s do it with pi-hole, step-ca and acme.sh. For this example, we’ll add https support to a random nginx server and to the pihole web interface.

Prerequisites:

  • 1 pi-hole DNS server
  • 1 server with a vanilla nginx installed
  • 1 server to install your new CA authority.

On the CA server:

/etc/systemd/system/step-ca.service ( since now debian is systemd by default, I assume most of us still running on other init system will have no trouble writing the init )

[Unit]
Description=step-ca
After=syslog.target network.target

[Service]
User=step
Group=step
ExecStart=/bin/sh -c '/usr/bin/step-ca /home/step/.step/config/ca.json --password-file=/home/step/.step/pwd 2>&1'
Type=simple
Restart=on-failure
RestartSec=10
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

Now let’s install step-ca and step-cli

echo 'you should check https://github.com/smallstep/cli and https://github.com/smallstep/certificates for the latest releases'
wget https://github.com/smallstep/cli/releases/download/v0.14.6/step-cli_0.14.6_amd64.deb
wget https://github.com/smallstep/certificates/releases/download/v0.14.6/step-certificates_0.14.6_amd64.deb
sudo dpkg -i *.deb
sudo adduser step
sudo passwd -l step
sudo -u step -s
step ca init # put the password created in /home/step/.step/pw
exit
systemctl daemon-reload
systemctl start step-ca
sudo -u step -s
step ca provisioner add acme-provision --type ACME # if you change acme-provision here change it also in the url you use with acme.sh
killall -1 step-ca
step certificate fingerprint $(step path)/certs/root_ca.crt # NOTE THAT, THIS IS YOUR FINGERPRINT CAFP
echo 'next step is to get your new root certificate:'
echo "The new root certificate is in $(step path)/certs/root_ca.crt"
echo 'read the tips to change the default validity period of the certificates you issue'

On the random nginx webserver:

echo 'Use curl get.acme.sh | bash || install acme.sh using git'
echo 'Now you can either use the option  --ca-bundle /path/to/root_ca.crt with acme, or add your root CA to the trust on the OS ( see tips )'
acme.sh --server https://YOURCAURLORIP/acme/acme-provision/directory --issue -d THEDOMAINTOPROVISION --nginx
acme.sh --server https://YOURCAURLORIP/acme/acme-provision/directory --install-cert  -d THEDOMAINTOPROVISION --key-file WHEREYOUWILLPOINTNGINXFORASSLKEY --fullchain-file WHEREYOUWILLPUTYOURCERTFORNGING --reloadcmd  "service nginx force-reload"
echo 'cron for acme renewal should have been setup but double check just in case'

Now configure nginx to add the newly created ssl /etc/nginx/sites-enabled/default

    ...
    # of course the rest of the config is left to you reader, this is just a very simple example to add to an existing block
    listen 443 ssl http2;
    ssl on;
    ssl_certificate WHEREYOUWILLPUTYOURCERTFORNGING;
    ssl_certificate_key WHEREYOUWILLPOINTNGINXFORASSLKEY;
    ...

Let’s add https for the pi-hole web interface:

echo 'follow the tips to add the root_ca.crt to the trusted root CA on the server, then install acme.sh'
mkdir -p /var/www/ssl
mkdir -p /etc/lightttp/certs
echo 'server.modules += ( "mod_alias", "mod_openssl" )' > /etc/lighttpd/external.conf
echo 'alias.url += ( "/.well-known/acme-challenge/" => "/var/www/ssl/.well-known/acme-challenge/" )' >> /etc/lighttpd/external.conf
service lighttpd restart
echo 'you can use your CA url if you set an entry in /etc/hosts, since this machine should not have its dns resolving locally'
acme.sh --server https://YOURCAIP/acme/acme-provision/directory --issue -d YOURPIHOLEDNSENTRY -w /var/www/ssl
acme.sh --server https://YOURCAIP/acme/acme-provision/directory --install-cert  -d YOURPIHOLEDNSENTRY --key-file /etc/lighttpd/certs/cert.key --fullchain-file /etc/lighttpd/certs/fullchain.cer --cert-file /etc/lighttpd/certs/cert.cer --reloadcmd  "cat /etc/lighttpd/certs/cert.key /etc/lighttpd/certs/cert.cer > /etc/lighttpd/certs/combined.pem;service lighttpd restart"

echo '$HTTP["host"] == "YOURPIHOLEDNSENTRY" {' >> /etc/lighttpd/external.conf
echo '  # Ensure the Pi-hole Block Page knows that this is not a blocked domain' >> /etc/lighttpd/external.conf
echo '  setenv.add-environment = ("fqdn" => "true")' >> /etc/lighttpd/external.conf
echo '' >> /etc/lighttpd/external.conf
echo '  # Enable the SSL engine only for this specific host' >> /etc/lighttpd/external.conf
echo '  $SERVER["socket"] == ":443" {' >> /etc/lighttpd/external.conf
echo '    ssl.engine = "enable"' >> /etc/lighttpd/external.conf
echo '    ssl.pemfile = "/etc/lighttpd/certs/combined.pem"' >> /etc/lighttpd/external.conf
echo '    ssl.ca-file =  "/etc/lighttpd/certs/fullchain.cer"' >> /etc/lighttpd/external.conf
echo '    ssl.honor-cipher-order = "enable"' >> /etc/lighttpd/external.conf
echo '    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"' >> /etc/lighttpd/external.conf
echo '    ssl.use-sslv2 = "disable"' >> /etc/lighttpd/external.conf
echo '    ssl.use-sslv3 = "disable"       ' >> /etc/lighttpd/external.conf
echo '  }' >> /etc/lighttpd/external.conf
echo '' >> /etc/lighttpd/external.conf
echo '  # Redirect HTTP to HTTPS' >> /etc/lighttpd/external.conf
echo '  $HTTP["scheme"] == "http" {' >> /etc/lighttpd/external.conf
echo '    $HTTP["host"] =~ ".*" {' >> /etc/lighttpd/external.conf
echo '      url.redirect = (".*" => "https://%0$0")' >> /etc/lighttpd/external.conf
echo '    }' >> /etc/lighttpd/external.conf
echo '  }' >> /etc/lighttpd/external.conf
echo '}' >> /etc/lighttpd/external.conf
service lighttpd restart

And now you can visit https://YOURPIHOLEDNSENTRY, https on the pi-hole admin interface !

Additional tips:

  • validity period of your certificates:

by default, step-ca issue certificates for 1day, you can either change the acme.sh cron to run every 12h and add –force option to force renewal or you can modify that to a more sane value editing the file $(step path)/config/ca.json, adding the claims section. Example:

{
	"type": "ACME",
	"name": "acme-provision",
          "claims": {
             "minTLSCertDuration": "168h",
             "maxTLSCertDuration": "8760h",
             "defaultTLSCertDuration": "1440h",
             "disableRenewal": false
          }
}

After modifying the file, reload the daemon or killall -1 step-ca

  • install the root CA on a debian server:

To install the certificate on the system, and allow any tool to trust it:

# first upload your root_ca.crt on the server
# then copy it 
cp root_ca.crt /usr/local/share/ca-certificates/
# and update the trust store
/usr/sbin/update-ca-certificates
  • use step-cli on your laptop/workmachine:

To use step-cli to interact with your step-ca easily from your workstation ( install depends on your OS, see https://github.com/smallstep/cli )

# use the CAFP you got from installing the CA server
step-cli ca bootstrap --fingerprint CAFP --ca-url "CAURL"
# you can then use it to generate a certificate from your workstation:  
step-cli ca certificate YOURDOMAIN YOURDOMAIN.crt YOURDOMAIN.key
# or generate a RSA key instead of ECDSA256:
step-cli ca certificate YOURDOMAIN YOURDOMAIN.crt YOURDOMAIN.key --kty RSA --size 4096
# or use step-cli to install the root certificate to the trust store:
step-cli certificate install ~/.step/certs/root_ca.crt

Conclusion:

you now can setup any kind of service privately hosted in your LAN, and use them as you see fit, you just add a local DNS record on the pi-hole admin interface, install your root certificate and acme.sh on the server and enjoy the simplicity of let’s encrypt, but self hosted :)