From 1dd6f8d3b4d7dc93095e662aaca190d3fe1be264 Mon Sep 17 00:00:00 2001 From: Soispha Date: Wed, 4 Oct 2023 20:11:42 +0200 Subject: feat(system/services/taskserver): Integrate Let's Encrypt certificates The current setup now runs the `taskserver.vhack.eu` domain with a Let's Encrypt certificate and additionally uses a self-signed CA certificate to validate clients. The shell scripts used to generate the CA certificate and the derived client certificate (and keys) are taken nearly unmodified from the upstream repository [1]. [1]: https://github.com/GothenburgBitFactory/taskserver/tree/9794cff61e56bdfb193c6aa4cebb57970ac68aef/pki --- system/services/taskserver/ca.cert.pem | 52 +++++++++++++++++++++++ system/services/taskserver/certs/README.md | 39 +++++++++++++++++ system/services/taskserver/certs/check_expire | 7 +++ system/services/taskserver/certs/generate | 41 ++++++++++++++++++ system/services/taskserver/certs/generate.ca | 47 +++++++++++++++++++++ system/services/taskserver/certs/generate.client | 54 ++++++++++++++++++++++++ system/services/taskserver/certs/generate.crl | 42 ++++++++++++++++++ system/services/taskserver/certs/vars | 7 +++ system/services/taskserver/default.nix | 30 +++---------- 9 files changed, 295 insertions(+), 24 deletions(-) create mode 100644 system/services/taskserver/ca.cert.pem create mode 100644 system/services/taskserver/certs/README.md create mode 100755 system/services/taskserver/certs/check_expire create mode 100755 system/services/taskserver/certs/generate create mode 100755 system/services/taskserver/certs/generate.ca create mode 100755 system/services/taskserver/certs/generate.client create mode 100755 system/services/taskserver/certs/generate.crl create mode 100644 system/services/taskserver/certs/vars diff --git a/system/services/taskserver/ca.cert.pem b/system/services/taskserver/ca.cert.pem new file mode 100644 index 0000000..d6e5513 --- /dev/null +++ b/system/services/taskserver/ca.cert.pem @@ -0,0 +1,52 @@ +-----BEGIN CERTIFICATE----- +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 +q3QPnPpVzFvh+K/1A81CSQ== +-----END CERTIFICATE----- diff --git a/system/services/taskserver/certs/README.md b/system/services/taskserver/certs/README.md new file mode 100644 index 0000000..846379c --- /dev/null +++ b/system/services/taskserver/certs/README.md @@ -0,0 +1,39 @@ +> This is taken from: https://github.com/GothenburgBitFactory/taskserver/blob/9794cff61e56bdfb193c6aa4cebb57970ac68aef/pki/README + +PKI is a complex subject. These scripts and this description are not intended +to be a complete and accurate example of PKI. + +Ideally you would purchase a server cert signed by a known CA, such as one of +the following: + +- Symantec +- Comodo +- GoDaddy +- GlobalSign +- (Let's Encrypt) + +That cert would need the 'encryption_key' and 'signing_key' attributes. +Using that server cert, you would then issue a server CRL and client keys. + +If you are developing, testing, or running your own private server, you may +choose instead to generate the above yourself. In this case you would generate +a CA key and cert, then use that to generate a server key, cert, and CRL. Then +you would use the server key and cert to create a client key and cert. But as +there is no trusted CA in this example, just yourself, the resultant client key +and cert will not be trusted by anyone, for good reasons. + +Note, you can inspect any cert with the command: + + $ gnutls-certtool -i --infile $CERT + +There is a 'generate' script here that will perform the above steps. Take a +look at it to see the individual steps it takes to generate the proper set of +keys and certs. + +Note that you need to modify the 'vars' file to provide your own identity and +chosen parameters. + +Validate a certificate with: + + $ gnutls-certtool --verify --infile client.cert.pem --load-ca-certificate ca.cert.pem + diff --git a/system/services/taskserver/certs/check_expire b/system/services/taskserver/certs/check_expire new file mode 100755 index 0000000..59f9dc6 --- /dev/null +++ b/system/services/taskserver/certs/check_expire @@ -0,0 +1,7 @@ +#!/bin/sh + +for cert in *.cert.pem; do + echo $cert + openssl x509 -noout -in $cert -dates + echo +done diff --git a/system/services/taskserver/certs/generate b/system/services/taskserver/certs/generate new file mode 100755 index 0000000..253e4bb --- /dev/null +++ b/system/services/taskserver/certs/generate @@ -0,0 +1,41 @@ +#!/bin/sh + +# For a public or production server, purchase a cert from a known CA, and skip +# the next step. + +# For development, testing and personal server management, create a CA key and +# cert, and use that to generate a server key and cert. Creates: +# ca.key.pem +# ca.cert.pem +# server.key.pem +# server.cert.pem + +GENERATION_LOCATION="/run/user/$(id -u)/taskserver/keys"; + +mkdir -p "$GENERATION_LOCATION" +cp ./vars ./generate.ca ./generate.crl ./generate.client "$GENERATION_LOCATION" +cd "$GENERATION_LOCATION" || echo "(BUG?) No possible location fould!" 1>&2 + +./generate.ca + +# Generate a certificate revocation list (CRL). The initial CRL is empty, but +# can grow over time. Creates: +# server.crl.pem + +./generate.crl + +# The above is sufficient to operate a server. You now need to run a client cert creation +# process per client; Add the required client names and uncomment +# ./generate.client +# +./generate.client soispha +./generate.client android-mobile +./generate.client android-tab +# +# Creates: +# .key.pem +# .cert.pem + + +rm ./vars ./generate.ca ./generate.crl ./generate.client +echo "(INFO) Look for the keys at: $GENERATION_LOCATION" diff --git a/system/services/taskserver/certs/generate.ca b/system/services/taskserver/certs/generate.ca new file mode 100755 index 0000000..4ffc6e9 --- /dev/null +++ b/system/services/taskserver/certs/generate.ca @@ -0,0 +1,47 @@ +#!/bin/sh + +# Take the correct binary to create the certificates +CERTTOOL=$(command -v gnutls-certtool 2>/dev/null || command -v certtool 2>/dev/null) +if [ -z "$CERTTOOL" ] +then + echo "ERROR: No certtool found" >&2 + exit 1 +fi + +. ./vars + +if ! [ -f ca.key.pem ] +then + # Create a CA key. + $CERTTOOL \ + --generate-privkey \ + --sec-param $SEC_PARAM \ + --outfile ca.key.pem +fi + +chmod 600 ca.key.pem + +if ! [ -f ca.template ] +then + # Sign a CA cert. + cat <ca.template +organization = $ORGANIZATION +cn = $CN CA +country = $COUNTRY +expiration_days = $EXPIRATION_DAYS +ca +EOF +#state = $STATE +#locality = $LOCALITY +fi + +if ! [ -f ca.cert.pem ] || [ ca.template -nt ca.cert.pem ] +then + $CERTTOOL \ + --generate-self-signed \ + --load-privkey ca.key.pem \ + --template ca.template \ + --outfile ca.cert.pem +fi + +chmod 600 ca.cert.pem diff --git a/system/services/taskserver/certs/generate.client b/system/services/taskserver/certs/generate.client new file mode 100755 index 0000000..976cb82 --- /dev/null +++ b/system/services/taskserver/certs/generate.client @@ -0,0 +1,54 @@ +#!/bin/sh + +# Take the correct binary to create the certificates +CERTTOOL=$(command -v gnutls-certtool 2>/dev/null || command -v certtool 2>/dev/null) +if [ -z "$CERTTOOL" ] +then + echo "ERROR: No certtool found" >&2 + exit 1 +fi + +. ./vars + +NAME=client +if [ $# -gt 0 ] +then + NAME=$1 +fi + +if ! [ -f ${NAME}.key.pem ] +then + # Create a client key. + $CERTTOOL \ + --generate-privkey \ + --sec-param $SEC_PARAM \ + --outfile ${NAME}.key.pem +fi + +chmod 600 ${NAME}.key.pem + +if ! [ -f ${NAME}.template ] +then + # Sign a client cert with the key. + cat <${NAME}.template +organization = $ORGANIZATION +cn = $CN +expiration_days = $EXPIRATION_DAYS +tls_www_client +encryption_key +signing_key +EOF +fi + +if ! [ -f ${NAME}.cert.pem ] || [ ${NAME}.template -nt ${NAME}.cert.pem ] +then + $CERTTOOL \ + --generate-certificate \ + --load-privkey ${NAME}.key.pem \ + --load-ca-certificate ca.cert.pem \ + --load-ca-privkey ca.key.pem \ + --template ${NAME}.template \ + --outfile ${NAME}.cert.pem +fi + +chmod 600 ${NAME}.cert.pem diff --git a/system/services/taskserver/certs/generate.crl b/system/services/taskserver/certs/generate.crl new file mode 100755 index 0000000..6a9daa8 --- /dev/null +++ b/system/services/taskserver/certs/generate.crl @@ -0,0 +1,42 @@ +#!/bin/sh + +# Take the correct binary to create the certificates +CERTTOOL=$(command -v gnutls-certtool 2>/dev/null || command -v certtool 2>/dev/null) +if [ -z "$CERTTOOL" ] +then + echo "ERROR: No certtool found" >&2 + exit 1 +fi + +. ./vars + +if ! [ -f crl.template ] +then + # CRL - Certificate Revocation List + cat <crl.template +expiration_days = $EXPIRATION_DAYS +EOF +fi + +if ! [ -f server.crl.pem ] || [ crl.template -nt server.crl.pem ] +then + $CERTTOOL \ + --generate-crl \ + --load-ca-privkey ca.key.pem \ + --load-ca-certificate ca.cert.pem \ + --template crl.template \ + --outfile server.crl.pem +fi + +chmod 600 server.crl.pem + +# To create a CRL that contains some revoked certificates, place the +# certificates in a file and use --load-certificate as follows: +# $CERTTOOL \ +# --generate-crl \ +# --load-ca-privkey ca.key.pem \ +# --load-ca-certificate ca.cert.pem \ +# --load-certificate revoked-certs.pem + +# To verify a CRL: +# $CERTTOOL --verify-crl --load-ca-certificate ca.cert.pem --infile server.crl.pem diff --git a/system/services/taskserver/certs/vars b/system/services/taskserver/certs/vars new file mode 100644 index 0000000..50d753a --- /dev/null +++ b/system/services/taskserver/certs/vars @@ -0,0 +1,7 @@ +SEC_PARAM=ultra +EXPIRATION_DAYS=365 +ORGANIZATION="Vhack" +CN=taskserver.vhack.eu +COUNTRY=EU +#STATE="Germany" +#LOCALITY="Göteborg" diff --git a/system/services/taskserver/default.nix b/system/services/taskserver/default.nix index afbd09c..7595700 100644 --- a/system/services/taskserver/default.nix +++ b/system/services/taskserver/default.nix @@ -3,28 +3,13 @@ in { services.taskserver = { enable = true; - config = { + pki.manual = { + ca.cert = ./ca.cert.pem; server = { - cert = "${taskStore}/fullchain.pem"; - key = "${taskStore}/privkey.pem"; - }; - }; - pki = { - auto = { - expiration = { - server = 365; - crl = 365; - client = 365; - ca = 365; - }; - bits = 4096; - }; - manual = { - ca.cert = builtins.toPath "${taskStore}/cert.pem"; - server = { - cert = builtins.toPath "${taskStore}/fullchain.pem"; - key = builtins.toPath "${taskStore}/privkey.pem"; - }; + # FIXME(@soispha): These are put _world-readable_ in the nix store, which is + # obviously very bad. These values should be strings <2023-10-04> + cert = /. + "${taskStore}/fullchain.pem"; + key = /. + "${taskStore}/privkey.pem"; }; }; organisations = import ./organisations.nix; @@ -43,15 +28,12 @@ in { set -x rm "${taskStore}/key.pem" rm "${taskStore}/fullchain.pem" - rm "${taskStore}/cert.pem" cp key.pem "${taskStore}"; cp fullchain.pem "${taskStore}"; - cp cert.pem "${taskStore}"; chown taskd:taskd "${taskStore}/key.pem" chown taskd:taskd "${taskStore}/fullchain.pem" - chown taskd:taskd "${taskStore}/cert.pem" ''; }; } -- cgit 1.4.1