Self Hosted: Episode 4 - Certificates

Software used:

  • FreeBSD 14.2 (jail)
  • Step CA 0.28.1 (from ports)

Introduction

If it isn't DNS... then it's certificates!

Certificates are simple... but it's complicated!

Certificates themselves are very simple... ignoring the all the crypto algorithms, hashes, etc. They are just two complementary long numbers that when put through those algorithms are able encrypt and decrypt messages. They are stored in two different files called the "certificate"/"public certificate"/"public cert"/"pem file", etc and the "private cert"/"private key"/"key file".

That's it! Simple!

Here's where things get complicated. There is this notion of "chain of trust". This chain is made up of one or more links. Each link must be trusted in order to trust the cert at the end of the chain. (example to follow) The head of the chain is called the "root". Root(s) (and there are many) "sign" a specific certificate file. This signing says to anyone receiving this certificate file, that if they "trust the root" they can then "trust the certificate file". This trust can go many layers deep.

Example: You trust Jim. (I'm not trying to figure out why you trust Jim, but you do. I mean, Jim is a good guy. Has good hair and a winning smile... so why not). Now Jim has given Sally a card that says that she can be trusted too. In addition Sally's card says that she can issue cards to others that she trusts. Sally gives Larry a card that she trusts him. Now when Larry shows you his card, should you trust Larry? Well you see that Larrys card is signed by Sally and Sallys card is signed by Jim. So if you trust Jim, then you trust Sally and thus you can trust Larry.

Now what happens if you don't trust Jim? Pretty simple, you don't trust anyone who Jim has extended his trust to. Or what happens if suddenly Sally becomes untrustworthy? Now anyone she extended her trust too is not untrust worthy.

The real question is how do you know who to trust. Fortunately?!, device and OS manufactures have created a list of trusted sources (referred to as Certificate Authorities or CAs)... and it's a long list. If you had to choose, you probably wouldn't have as long a list as this. Because anyone in this list is considered totally trust worthy and anyone they say is trustworthy is also totally trust worthy. (most probably are... but not all!)

So who can we really trust?!?! Ourselves! Now, let's create our own Certificate Authority!!!

Wait! What? Why would we do this?

Three reasons. First is that all the "default" CAs are public and only issue certificates to public reachable entities. (We will eventually make use of these public CAs). And our Self Hosted environment will not all be accessible to the greater Internet. (If we setup our firewall correctly!) Second, certificates from these public CAs can cost money. Sometime lots of money. Third, eventually we will want to do what is called mutual authentication (but more on that later).

Now in order to make running our own certificate authority we will want to automate as much of the certificate functions as possible. The issuing, expiration, revocation, etc should be automated. Fortunately, the ACME protocol, made popular by, Lets Encrypt can be used on our systems to automatically allocated and update certificates on our internal servers as long as our CA supports the ACME protocol (and it does).

Update DNS with new address

To begin with we will need to be able to resolve the new certificate authority. To do this update the example.org and 30.168.192.in-addr.arpa zone files on the DNS servers respectively.

certauth     IN      A      192.168.30.11

record for example.org zone file

11      IN      PTR      certauth.example.org.

record for the 30.168.192.in-addr.arpa zone file

Don't forget to update the zone serial number and restart the named service.

Setup the CA

The CA server will be created using a FreeBSD jail. If you have not already setup the Jail infrastructure review Episode 3 - Setting up FreeBSD Jail. For this jail we will be using the following file.

# Certificate server jail file

certauth {
	# STARTUP / LOGGING
	exec.start = "/bin/sh /etc/rc";
	exec.stop = "/bin/sh /etc/rc.shutdown";
	exec.consolelog = "/var/log/jail_console_${name}.log";

	# PERMISSIONS
	allow.raw_sockets;
	exec.clean;
	mount.devfs;
    allow.reserved_ports;

	# HOSTNAME / PATH
	host.hostname = "${name}";
	path = "/usr/local/jails/containers/${name}";

	# NETWORK
	vnet;
	vnet.interface = "${epair}b";

	# NETWORKS/INTERFACES
	$id = "11";
	$ip = "192.168.10.${id}";
	$mask = "255.255.255.0";
	$gateway = "192.168.10.1";
	$bridge = "net-dmz";
	$epair = "epair${id}";

	exec.prestart += "ifconfig ${epair} create up";
	exec.prestart += "ifconfig ${epair}a up descr jail:${name}";
	exec.prestart += "ifconfig ${bridge} addm ${epair}a up";
	exec.start    += "ifconfig ${epair}b ${ip} netmask ${mask} up";
	exec.start    += "route add default ${gateway}";
	exec.poststop += "ifconfig ${bridge} deletem ${epair}a";
	exec.poststop += "ifconfig ${epair}a destroy";
}

/etc/jail.conf.d/certauth.conf

Start the Jail

Start the certauth jail.

service jail start certauth

command to start the jail

Install the CA service in the jail

Once the jail has been started we need to update the FreeBSD package system and install the CA packages into the jail.

pkg -j certauth -y update
pkg -j certauth -y install step-certificates step-cli

commands to install the CA software

Connect to the new jail

jexec certauth /bin/sh

command to enter the jail

Generate the configuration

First export some common used vars and check if directories exist

export SDIR=/usr/local/etc/step
export STEPPATH=$SDIR/ca
test -d $SDIR || mkdir -p $SDIR

commands to create the CA directories

Next generate the password key. This is the key that will be used to encrypt all cert passwords. (KEEP IT SAFE!) It is not out of the realm to have this key be stored on some sort of hardware token. The process for doing that is outside of this article.

openssl rand -base64 20 > $SDIR/password.txt
chown root:wheel $SDIR/password.txt
chmod 400 $SDIR/password.txt

commands to create the CA password

Next initialize the the certificate authority. This will create the root and intermediate certificates.

test -f $SDIR/ca/config/ca.json || /usr/local/bin/step ca init --address :443 --deployment-type standalone --dns $FQDN --name "Root CA" --with-ca-url https://$FQDN --password-file $SDIR/password.txt --provisioner certificate_authority --provisioner-password-file $SDIR/password.txt

commands to create and initialize the CA

Finally setup the ACME provisioner.

/usr/local/bin/step ca provisioner add acme --type ACME

commands to enable an ACME provisioner in the CA

Start the CA service

service step_ca enable
service step_ca start

commands to enable and start the CA

Deploy the new root certificate

To deploy the new root certificate to the certificate host (can confirm things are working) first fetch the cert into the /usr/local/share/certs directory.

test -d /usr/local/share/certs || mkdir -p /usr/local/share/certs
fetch --no-verify-peer --no-verify-hostname -o /usr/local/share/certs/root_ca.crt https://127.0.0.1/roots.pem

commands to fetch the root CA certificate

Finally update the system certificate store

certctl rehash

command to install and trust the new CA certificate

Deploy root cert and pull new cert

DNS server

Add and trust the root cert

test -d /usr/local/share/certs || mkdir -p /usr/local/share/certs
fetch --no-verify-peer --no-verify-hostname -o /usr/local/share/certs/root_ca.crt https://certauth.example.org/roots.pem
certctl rehash

commands to install and trust the new CA certificate

Install the acme package

pkg install -y acme.sh

command to install the acme.sh package

Generate the certificate.

export CRTP=/usr/local/etc/ssl
test -f $CRTP || mkdir -p $CRTP
acme.sh --issue --standalone -d $(hostname) --cert-file $CRTP/$(hostname -s).crt --key-file $CRTP/$(hostname -s).key --fullchain-file $CRTP/$(hostname -s)_fullchain.pem --server https://certauth.example.org/acme/acme/directory

Commands to get a new certificate issued.

Update cron to renew the certificate

echo '0 0 * * * "/usr/local/sbin/acme.sh --cron --home /root/.acme.sh" > /dev/null' > /etc/cron.d/acme_sh

command to deploy a cron job to update certificate when it expires.

Firewall

Add and trust the root cert.

ftp -S dont -o /etc/ssl/example.org_root_ca.pem https://192.168.30.11/roots.pem
cat /etc/ssl/example.org_root_ca.pem >> /etc/ssl/cert.pem

command to install and trust the new CA root certificate.

Create the /etc/acme-client.conf

#
authority example.org {
        api url "https://certauth.example.org/acme/acme/directory"
        account key "/etc/acme/example.org-privkey.pem"
}

domain firewall.example.org {
        domain key "/etc/ssl/private/firewall.key"
        domain full chain certificate "/etc/ssl/firewall.fullchain.pem"
        sign with example.org
}

/etc/acme-client.conf

Create the /etc/httpd.conf

# 
server "*" {
        listen on * port 80
        location "/.well-known/acme-challenge/*" {
                root "/acme"
                request strip 2
        }
        location * {
                block 
        }
}

/etc/httpd.conf

Start the web server

rcctl enable httpd
rcctl start httpd

command to start the web server

Generate the certificate

acme-client $(hostname)

command to install a new device certificate

Update daily cron file to check / renew certificate.

echo '/usr/sbin/acme-client $(hostname)' >> /etc/daily.local

command to setup cron jobe to renew certificate automatically

Create a manual certificates

Host Certificates

To manually create a host certificate the following command can be used on the certauth server.

step certificate create blah.example.org blah.crt blah.key \
  --force --profile=leaf \
  --ca=/usr/local/etc/step/ca/certs/intermediate_ca.crt \
  --ca-key=/usr/local/etc/step/ca/secrets/intermediate_ca_key \
  --ca-password-file=/usr/local/etc/step/password.txt \
  --no-password --insecure --not-after=$((24*390))h

step certificate p12 blah.p12 blah.crt blah.key

User/Email Certificates

At some time we may want to create a certificate for an email address and not a host. To do this create the following file as a template 'email.tpl'.

{
 "sans": {{ toJson .SANs }},
 "keyUsage": ["digitalSignature" ],
 "extKeyUsage": ["clientAuth", "emailProtection" ]
}

email.tpl

Then use this command to create the certificate.

step certificate create [email protected] oscar.crt oscar.key \
  --force --ca=/usr/local/etc/step/ca/certs/intermediate_ca.crt \
  --ca-key=/usr/local/etc/step/ca/secrets/intermediate_ca_key \
  --ca-password-file=/usr/local/etc/step/password.txt \
  --no-password --insecure --not-after=$((24*390))h \
  --template=email.tpl

step certificate p12 oscar.p12 oscar.crt oscar.key

Conclusion

Now that we have DNS, DHCP, Certificates and the firewall working, can we begin to start thinking about adding something that is actually useful? Well not quite yet... :/