change(docker): use step-ca for CA + cert generation

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-11-27 16:43:29 -08:00
parent e558e979ff
commit b98159d5b9
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
10 changed files with 160 additions and 69 deletions

View file

@ -1,58 +1,123 @@
#!/bin/bash
# Initialize step-ca and request certificates from it.
#
# Certificates created by this service are meant to be used in
# aurweb Docker's nginx service.
#
# If ./data/root_ca.crt is present, CA generation is skipped.
# If ./data/${host}.{cert,key}.pem is available, host certificate
# generation is skipped.
#
set -eou pipefail
if [ -f /data/ca.root.pem ]; then
echo "Already have certs, skipping."
exit 0
# /data-based variables.
DATA_DIR="/data"
DATA_ROOT_CA="$DATA_DIR/root_ca.crt"
DATA_CERT="$DATA_DIR/localhost.cert.pem"
DATA_CERT_KEY="$DATA_DIR/localhost.key.pem"
# Host certificates requested from the CA (separated by spaces).
DATA_CERT_HOSTS='localhost'
# Local step paths and CA configuration values.
STEP_DIR="$(step-cli path)"
STEP_CA_CONFIG="$STEP_DIR/config/ca.json"
STEP_CA_ADDR='127.0.0.1:8443'
STEP_CA_URL='https://localhost:8443'
STEP_CA_PROVISIONER='admin@localhost'
# Password file used for both --password-file and --provisioner-password-file.
STEP_PASSWD_FILE="$STEP_DIR/password.txt"
# Hostnames supported by the CA.
STEP_CA_NAME='aurweb'
STEP_CA_DNS='localhost'
make_password() {
# Create a random 20-length password and write it to $1.
openssl rand -hex 20 > $1
}
setup_step_ca() {
# Cleanup and setup step ca configuration.
rm -rf $STEP_DIR/*
# Initialize `step`
make_password "$STEP_PASSWD_FILE"
step-cli ca init \
--name="$STEP_CA_NAME" \
--dns="$STEP_CA_DNS" \
--address="$STEP_CA_ADDR" \
--password-file="$STEP_PASSWD_FILE" \
--provisioner="$STEP_CA_PROVISIONER" \
--provisioner-password-file="$STEP_PASSWD_FILE" \
--with-ca-url="$STEP_CA_URL"
# Update ca.json max TLS certificate duration to a year.
update-step-config "$STEP_CA_CONFIG"
# Install root_ca.crt as read/writable to /data/root_ca.crt.
install -m666 "$STEP_DIR/certs/root_ca.crt" "$DATA_ROOT_CA"
}
start_step_ca() {
# Start the step-ca web server.
step-ca "$STEP_CA_CONFIG" \
--password-file="$STEP_PASSWD_FILE" &
until printf "" 2>>/dev/null >>/dev/tcp/127.0.0.1/8443; do
sleep 1
done
}
kill_step_ca() {
# Stop the step-ca web server.
killall step-ca >/dev/null 2>&1 || /bin/true
}
install_step_ca() {
# Install step-ca certificate authority to the system.
step-cli certificate install "$STEP_DIR/certs/root_ca.crt"
}
step_cert_request() {
# Request a certificate from the step ca.
step-cli ca certificate \
--not-after=8800h \
--provisioner="$STEP_CA_PROVISIONER" \
--provisioner-password-file="$STEP_PASSWD_FILE" \
$1 $2 $3
chmod 666 /data/${1}.*.pem
}
if [ ! -f $DATA_ROOT_CA ]; then
setup_step_ca
install_step_ca
fi
# Generate a new 2048-bit RSA key for the Root CA.
openssl genrsa -des3 -out /data/ca.key -passout pass:devca 2048
# For all hosts separated by spaces in $DATA_CERT_HOSTS, perform a check
# for their existence in /data and react accordingly.
for host in $DATA_CERT_HOSTS; do
if [ -f /data/${host}.cert.pem ] && [ -f /data/${host}.key.pem ]; then
# Found an override. Move on to running the service after
# printing a notification to the user.
echo "Found '${host}.{cert,key}.pem' override, skipping..."
echo -n "Note: If you need to regenerate certificates, run "
echo '`rm -f data/*.{cert,key}.pem` before starting this service.'
exec "$@"
else
# Otherwise, we had a missing cert or key, so remove both.
rm -f /data/${host}.cert.pem
rm -f /data/${host}.key.pem
fi
done
# Request and self-sign a new Root CA certificate, using
# the RSA key. Output Root CA PEM-format certificate and key:
# /data/ca.root.pem and /data/ca.key.pem
openssl req -x509 -new -nodes -sha256 -days 1825 \
-passin pass:devca \
-subj "/C=US/ST=California/L=Authority/O=aurweb/CN=localhost" \
-in /data/ca.key -out /data/ca.root.pem -keyout /data/ca.key.pem
start_step_ca
for host in $DATA_CERT_HOSTS; do
step_cert_request $host /data/${host}.cert.pem /data/${host}.key.pem
done
kill_step_ca
# Generate a new 2048-bit RSA key for a localhost server.
openssl genrsa -out /data/localhost.key 2048
# Generate a Certificate Signing Request (CSR) for the localhost server
# using the RSA key we generated above.
openssl req -new -key /data/localhost.key -passout pass:devca \
-subj "/C=US/ST=California/L=Server/O=aurweb/CN=localhost" \
-out /data/localhost.csr
# Get our CSR signed by our Root CA PEM-formatted certificate and key
# to produce a fresh /data/localhost.cert.pem PEM-formatted certificate.
openssl x509 -req -in /data/localhost.csr \
-CA /data/ca.root.pem -CAkey /data/ca.key.pem \
-CAcreateserial \
-out /data/localhost.cert.pem \
-days 825 -sha256 \
-passin pass:devca \
-extfile /docker/localhost.ext
# Convert RSA key to a PEM-formatted key: /data/localhost.key.pem
openssl rsa -in /data/localhost.key -text > /data/localhost.key.pem
# At the end here, our notable certificates and keys are:
# - /data/ca.root.pem
# - /data/ca.key.pem
# - /data/localhost.key.pem
# - /data/localhost.cert.pem
#
# When running a server which uses the localhost certificate, a chain
# should be used, starting with localhost.cert.pem:
# - cat /data/localhost.cert.pem /data/ca.root.pem > localhost.chain.pem
#
# The Root CA (ca.root.pem) should be imported into browsers or
# ca-certificates on machines wishing to verify localhost.
#
chmod 666 /data/*
# Set permissions to /data to rwx for everybody.
chmod 777 /data
exec "$@"

2
docker/health/ca.sh Executable file
View file

@ -0,0 +1,2 @@
exec printf "" 2>>/dev/null >>/dev/tcp/127.0.0.1/8443

View file

@ -15,7 +15,7 @@ if [ -f "$CERT" ]; then
cp -vf "$CERT" "$DEST_CERT"
cp -vf "$KEY" "$DEST_KEY"
else
cat /data/localhost.cert.pem /data/ca.root.pem > "$DEST_CERT"
cat /data/localhost.cert.pem /data/root_ca.crt > "$DEST_CERT"
cp -vf /data/localhost.key.pem "$DEST_KEY"
fi

View file

@ -9,6 +9,6 @@ pacman -Syu --noconfirm --noprogressbar \
mariadb mariadb-libs cgit-aurweb uwsgi uwsgi-plugin-cgi \
php php-fpm memcached php-memcached python-pip pyalpm \
python-srcinfo curl libeatmydata cronie python-poetry \
python-poetry-core
python-poetry-core step-cli step-ca
exec "$@"

7
docker/scripts/run-ca.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
STEP_DIR="$(step-cli path)"
STEP_PASSWD_FILE="$STEP_DIR/password.txt"
STEP_CA_CONFIG="$STEP_DIR/config/ca.json"
# Start the step-ca https server.
exec step-ca "$STEP_CA_CONFIG" --password-file="$STEP_PASSWD_FILE"

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python3
import json
import sys
CA_CONFIG = sys.argv[1]
with open(CA_CONFIG) as f:
data = json.load(f)
if "authority" not in data:
data["authority"] = dict()
if "claims" not in data["authority"]:
data["authority"]["claims"] = dict()
# One year of certificate duration.
data["authority"]["claims"] = {"maxTLSCertDuration": "8800h"}
with open(CA_CONFIG, "w") as f:
json.dump(data, f)