Commit ca574678 authored by Brad Davidson's avatar Brad Davidson Committed by Brad Davidson

Update/rename certs.sh; add default cert rotation script

parent 9360022b
...@@ -23,11 +23,6 @@ ...@@ -23,11 +23,6 @@
set -e set -e
umask 027 umask 027
CONFIG="
[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints=CA:true"
TIMESTAMP=$(date +%s) TIMESTAMP=$(date +%s)
PRODUCT="${PRODUCT:-k3s}" PRODUCT="${PRODUCT:-k3s}"
DATA_DIR="${DATA_DIR:-/var/lib/rancher/${PRODUCT}}" DATA_DIR="${DATA_DIR:-/var/lib/rancher/${PRODUCT}}"
...@@ -38,21 +33,45 @@ else ...@@ -38,21 +33,45 @@ else
OPENSSL=openssl OPENSSL=openssl
fi fi
echo "Using $(which ${OPENSSL}): $(${OPENSSL} version)" echo "Using $(type -p ${OPENSSL}): $(${OPENSSL} version)"
if ! ${OPENSSL} ecparam -help &>/dev/null; then if ! ${OPENSSL} ecparam -name prime256v1 -genkey -noout -out /dev/null &>/dev/null; then
echo "openssl not found or missing Elliptic Curve (ecparam) support." echo "openssl not found or missing Elliptic Curve (ecparam) support."
exit 1 exit 1
fi fi
if ! ${OPENSSL} req -help 2>&1 | grep -q CAkey; then ${OPENSSL} version | grep -qF 'OpenSSL 3' && OPENSSL_GENRSA_FLAGS=-traditional
echo "openssl req missing -CAkey support; please use OpenSSL 3.0.0 or newer"
exit 1
fi
mkdir -p "${DATA_DIR}/server/tls/etcd" mkdir -p "${DATA_DIR}/server/tls/etcd"
cd "${DATA_DIR}/server/tls" cd "${DATA_DIR}/server/tls"
# Set up temporary openssl configuration
mkdir -p ".ca/certs"
trap "rm -rf .ca" EXIT
touch .ca/index
openssl rand -hex 8 > .ca/serial
cat >.ca/config <<'EOF'
[ca]
default_ca = ca_default
[ca_default]
dir = ./.ca
database = $dir/index
serial = $dir/serial
new_certs_dir = $dir/certs
default_md = sha256
policy = policy_anything
[policy_anything]
commonName = supplied
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
EOF
# Don't overwrite the service account issuer key; we pass the key into both the controller-manager # Don't overwrite the service account issuer key; we pass the key into both the controller-manager
# and the apiserver instead of passing a cert list into the apiserver, so there's no facility for # and the apiserver instead of passing a cert list into the apiserver, so there's no facility for
# rotation and things will get very angry if all the SA keys are invalidated. # rotation and things will get very angry if all the SA keys are invalidated.
...@@ -62,7 +81,7 @@ if [[ -e service.key ]]; then ...@@ -62,7 +81,7 @@ if [[ -e service.key ]]; then
else else
echo "Generating Kubernetes service account issuer RSA key" echo "Generating Kubernetes service account issuer RSA key"
fi fi
${OPENSSL} genrsa -traditional -out service.key 2048 ${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out service.key 2048
echo "${OLD_SERVICE_KEY}" >> service.key echo "${OLD_SERVICE_KEY}" >> service.key
# Use existing root CA if present # Use existing root CA if present
...@@ -70,8 +89,13 @@ if [[ -e root-ca.pem ]]; then ...@@ -70,8 +89,13 @@ if [[ -e root-ca.pem ]]; then
echo "Using existing root certificate" echo "Using existing root certificate"
else else
echo "Generating root certificate authority RSA key and certificate" echo "Generating root certificate authority RSA key and certificate"
${OPENSSL} genrsa -out root-ca.key 4096 ${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out root-ca.key 4096
${OPENSSL} req -x509 -new -nodes -key root-ca.key -sha256 -days 7300 -out root-ca.pem -subj "/CN=${PRODUCT}-root-ca@${TIMESTAMP}" -config <(echo "${CONFIG}") -extensions v3_ca ${OPENSSL} req -x509 -new -nodes -sha256 -days 7300 \
-subj "/CN=${PRODUCT}-root-ca@${TIMESTAMP}" \
-key root-ca.key \
-out root-ca.pem \
-config .ca/config \
-extensions v3_ca
fi fi
cat root-ca.pem > root-ca.crt cat root-ca.pem > root-ca.crt
...@@ -85,8 +109,17 @@ else ...@@ -85,8 +109,17 @@ else
fi fi
echo "Generating intermediate certificate authority RSA key and certificate" echo "Generating intermediate certificate authority RSA key and certificate"
${OPENSSL} genrsa -out intermediate-ca.key 4096 ${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out intermediate-ca.key 4096
${OPENSSL} req -x509 -new -nodes -CAkey root-ca.key -CA root-ca.crt -key intermediate-ca.key -sha256 -days 7300 -out intermediate-ca.pem -subj "/CN=${PRODUCT}-intermediate-ca@${TIMESTAMP}" -config <(echo "${CONFIG}, pathlen:1") -extensions v3_ca ${OPENSSL} req -new -nodes \
-subj "/CN=${PRODUCT}-intermediate-ca@${TIMESTAMP}" \
-key intermediate-ca.key |
${OPENSSL} ca -batch -notext -days 3700 \
-in /dev/stdin \
-out intermediate-ca.pem \
-keyfile root-ca.key \
-cert root-ca.pem \
-config .ca/config \
-extensions v3_ca
fi fi
cat intermediate-ca.pem root-ca.pem > intermediate-ca.crt cat intermediate-ca.pem root-ca.pem > intermediate-ca.crt
...@@ -96,32 +129,32 @@ if [[ ! -e intermediate-ca.key ]]; then ...@@ -96,32 +129,32 @@ if [[ ! -e intermediate-ca.key ]]; then
fi fi
# Generate new leaf CAs for all the control-plane and etcd components # Generate new leaf CAs for all the control-plane and etcd components
echo "Generating Kubernetes server leaf certificate authority EC key and certificate" for TYPE in client server request-header etcd/peer etcd/server; do
${OPENSSL} ecparam -name prime256v1 -genkey -out client-ca.key CERT_NAME="${PRODUCT}-$(echo ${TYPE} | tr / -)-ca"
${OPENSSL} req -x509 -new -nodes -CAkey intermediate-ca.key -CA intermediate-ca.crt -key client-ca.key -sha256 -days 3650 -out client-ca.pem -subj "/CN=${PRODUCT}-client-ca@${TIMESTAMP}" -config <(echo "${CONFIG}, pathlen:0") -extensions v3_ca echo "Generating ${CERT_NAME} leaf certificate authority EC key and certificate"
cat client-ca.pem intermediate-ca.pem root-ca.pem > client-ca.crt ${OPENSSL} ecparam -name prime256v1 -genkey -noout -out ${TYPE}-ca.key
${OPENSSL} req -new -nodes \
echo "Generating Kubernetes client leaf certificate authority EC key and certificate" -subj "/CN=${CERT_NAME}@${TIMESTAMP}" \
${OPENSSL} ecparam -name prime256v1 -genkey -out server-ca.key -key ${TYPE}-ca.key |
${OPENSSL} req -x509 -new -nodes -CAkey intermediate-ca.key -CA intermediate-ca.crt -key server-ca.key -sha256 -days 3650 -out server-ca.pem -subj "/CN=${PRODUCT}-server-ca@${TIMESTAMP}" -config <(echo "${CONFIG}, pathlen:0") -extensions v3_ca ${OPENSSL} ca -batch -notext -days 3700 \
cat server-ca.pem intermediate-ca.pem root-ca.pem > server-ca.crt -in /dev/stdin \
-out ${TYPE}-ca.pem \
echo "Generating Kubernetes request-header leaf certificate authority EC key and certificate" -keyfile intermediate-ca.key \
${OPENSSL} ecparam -name prime256v1 -genkey -out request-header-ca.key -cert intermediate-ca.pem \
${OPENSSL} req -x509 -new -nodes -CAkey intermediate-ca.key -CA intermediate-ca.crt -key request-header-ca.key -sha256 -days 3560 -out request-header-ca.pem -subj "/CN=${PRODUCT}-request-header-ca@${TIMESTAMP}" -config <(echo "${CONFIG}, pathlen:0") -extensions v3_ca -config .ca/config \
cat request-header-ca.pem intermediate-ca.pem root-ca.pem > request-header-ca.crt -extensions v3_ca
cat ${TYPE}-ca.pem \
echo "Generating etcd peer leaf certificate authority EC key and certificate" intermediate-ca.pem \
${OPENSSL} ecparam -name prime256v1 -genkey -out etcd/peer-ca.key root-ca.pem > ${TYPE}-ca.crt
${OPENSSL} req -x509 -new -nodes -CAkey intermediate-ca.key -CA intermediate-ca.crt -key etcd/peer-ca.key -sha256 -days 3650 -out etcd/peer-ca.pem -subj "/CN=etcd-peer-ca@${TIMESTAMP}" -config <(echo "${CONFIG}, pathlen:0") -extensions v3_ca done
cat etcd/peer-ca.pem intermediate-ca.pem root-ca.pem > etcd/peer-ca.crt
echo "Generating etcd server leaf certificate authority EC key and certificate"
${OPENSSL} ecparam -name prime256v1 -genkey -out etcd/server-ca.key
${OPENSSL} req -x509 -new -nodes -CAkey intermediate-ca.key -CA intermediate-ca.crt -key etcd/server-ca.key -sha256 -days 3650 -out etcd/server-ca.pem -subj "/CN=etcd-server-ca@${TIMESTAMP}" -config <(echo "${CONFIG}, pathlen:0") -extensions v3_ca
cat etcd/server-ca.pem intermediate-ca.pem root-ca.pem > etcd/server-ca.crt
echo echo
echo "CA certificate generation complete. Required files are now present in: ${DATA_DIR}/server/tls" echo "CA certificate generation complete. Required files are now present in: ${DATA_DIR}/server/tls"
echo "For security purposes, you should make a secure copy of the following files and remove them from cluster members:" echo "For security purposes, you should make a secure copy of the following files and remove them from cluster members:"
ls ${DATA_DIR}/server/tls/root-ca.* ${DATA_DIR}/server/tls/intermediate-ca.* | xargs -n1 echo -e "\t" ls ${DATA_DIR}/server/tls/root-ca.* ${DATA_DIR}/server/tls/intermediate-ca.* | xargs -n1 echo -e "\t"
if [ "${DATA_DIR}" != "/var/lib/rancher/${PRODUCT}" ]; then
echo
echo "To update certificates on an existing cluster, you may now run:"
echo " k3s certificate rotate-ca --path=${DATA_DIR}/server"
fi
#!/usr/bin/env bash
set -e
umask 027
# Example K3s self-signed CA rotation script.
#
# This script will generate new self-signed root CA certificates, and cross-sign them with the
# current self-signed root CA certificates. It will then generate new leaf CA certificates
# signed by the new self-signed/cross-signed root CAs. The resulting cluster CA bundle will
# allow existing certificates to be trusted up until the original root CAs expire.
#
CONFIG="
[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints=CA:true"
TIMESTAMP=$(date +%s)
PRODUCT="${PRODUCT:-k3s}"
DATA_DIR="${DATA_DIR:-/var/lib/rancher/${PRODUCT}}"
TEMP_DIR="${DATA_DIR}/server/rotate-ca"
if type -t openssl-3 &>/dev/null; then
OPENSSL=openssl-3
else
OPENSSL=openssl
fi
echo "Using $(type -p ${OPENSSL}): $(${OPENSSL} version)"
if ! ${OPENSSL} ecparam -name prime256v1 -genkey -out /dev/null &>/dev/null; then
echo "openssl not found or missing Elliptic Curve (ecparam) support."
exit 1
fi
${OPENSSL} version | grep -qF 'OpenSSL 3' && OPENSSL_GENRSA_FLAGS=-traditional
mkdir -p ${TEMP_DIR}/tls/etcd
cd ${TEMP_DIR}/tls
# Set up temporary openssl configuration
mkdir -p ".ca/certs"
trap "rm -rf .ca" EXIT
touch .ca/index
openssl rand -hex 8 > .ca/serial
cat >.ca/config <<'EOF'
[ca]
default_ca = ca_default
[ca_default]
dir = ./.ca
database = $dir/index
serial = $dir/serial
new_certs_dir = $dir/certs
default_md = sha256
policy = policy_anything
[policy_anything]
commonName = supplied
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
EOF
for TYPE in client server request-header etcd/peer etcd/server; do
if [ ! -f ${DATA_DIR}/server/tls/${TYPE}-ca.crt ]; then
echo "Current ${TYPE} CA cert does not exist; cannot continue"
exit 1
fi
if [ "$(grep -cF 'END CERTIFICATE' ${DATA_DIR}/server/tls/${TYPE}-ca.crt)" -gt "1" ]; then
awk 'BEGIN { RS = "-----END CERTIFICATE-----\n" } NR==2 { print $0 RS }' ${DATA_DIR}/server/tls/${TYPE}-ca.crt > ${TYPE}-root-old.pem
awk 'BEGIN { RS = "-----END EC PRIVATE KEY-----\n" } NR==2 { print $0 RS }' ${DATA_DIR}/server/tls/${TYPE}-ca.key > ${TYPE}-root-old.key
else
cat ${DATA_DIR}/server/tls/${TYPE}-ca.crt > ${TYPE}-root-old.pem
cat ${DATA_DIR}/server/tls/${TYPE}-ca.key > ${TYPE}-root-old.key
fi
CERT_NAME="${PRODUCT}-$(echo ${TYPE} | tr / -)-root"
echo "Generating ${CERT_NAME} root and cross-signed certificate authority key and certificates"
${OPENSSL} ecparam -name prime256v1 -genkey -noout -out ${TYPE}-root.key
${OPENSSL} req -x509 -new -nodes -sha256 -days 7300 \
-subj "/CN=${CERT_NAME}@${TIMESTAMP}" \
-key ${TYPE}-root.key \
-out ${TYPE}-root-ssigned.pem \
-config ./.ca/config \
-extensions v3_ca
${OPENSSL} req -new -nodes \
-subj "/CN=${CERT_NAME}@${TIMESTAMP}" \
-key ${TYPE}-root.key |
${OPENSSL} ca -batch -notext -days 7300 \
-in /dev/stdin \
-out ${TYPE}-root-xsigned.pem \
-keyfile ${TYPE}-root-old.key \
-cert ${TYPE}-root-old.pem \
-config ./.ca/config \
-extensions v3_ca
CERT_NAME="${PRODUCT}-$(echo ${TYPE} | tr / -)-ca"
echo "Generating ${CERT_NAME} intermediate certificate authority key and certificates"
${OPENSSL} ecparam -name prime256v1 -genkey -noout -out ${TYPE}-ca.key
${OPENSSL} req -new -nodes \
-subj "/CN=${CERT_NAME}@${TIMESTAMP}" \
-key ${TYPE}-ca.key |
${OPENSSL} ca -batch -notext -days 7300 \
-in /dev/stdin \
-out ${TYPE}-ca.pem \
-keyfile ${TYPE}-root.key \
-cert ${TYPE}-root-ssigned.pem \
-config ./.ca/config \
-extensions v3_ca
cat ${TYPE}-ca.pem \
${TYPE}-root-ssigned.pem \
${TYPE}-root-xsigned.pem \
${TYPE}-root-old.pem > ${TYPE}-ca.crt
cat ${TYPE}-root.key >> ${TYPE}-ca.key
done
${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out service.key 2048
cat ${DATA_DIR}/server/tls/service.key >> service.key
export SERVER_CA_HASH=$(${OPENSSL} x509 -noout -fingerprint -sha256 -in server-ca.pem | awk -F= '{ gsub(/:/, "", $2); print tolower($2) }')
SERVER_TOKEN=$(awk -F:: '{print "K10" ENVIRON["SERVER_CA_HASH"] FS $2}' ${DATA_DIR}/server/token)
AGENT_TOKEN=$(awk -F:: '{print "K10" ENVIRON["SERVER_CA_HASH"] FS $2}' ${DATA_DIR}/server/agent-token)
echo
echo "Cross-signed CA certs and keys now available in ${TEMP_DIR}"
echo "Updated server token: ${SERVER_TOKEN}"
echo "Updated agent token: ${AGENT_TOKEN}"
echo
echo "To update certificates, you may now run:"
echo " k3s certificate rotate-ca --path=${TEMP_DIR}"
...@@ -28,7 +28,7 @@ cluster-pre-hook() { ...@@ -28,7 +28,7 @@ cluster-pre-hook() {
rancher/mirrored-pause:3.6 \ rancher/mirrored-pause:3.6 \
>/dev/null >/dev/null
DATA_DIR="$TEST_DIR/pause/0/k3s" ./contrib/util/certs.sh DATA_DIR="$TEST_DIR/pause/0/k3s" ./contrib/util/generate-custom-ca-certs.sh
docker cp "$TEST_DIR/pause/0/k3s" $name:/var/lib/rancher docker cp "$TEST_DIR/pause/0/k3s" $name:/var/lib/rancher
} }
export -f cluster-pre-hook export -f cluster-pre-hook
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment