diff --git a/contrib/util/certs.sh b/contrib/util/certs.sh deleted file mode 100755 index 67c572ac909f..000000000000 --- a/contrib/util/certs.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env bash - -# Example K3s CA certificate generation script. -# -# This script will generate files sufficient to bootstrap K3s cluster certificate -# authorities. By default, the script will create the required files under -# /var/lib/rancher/k3s/server/tls, where they will be found and used by K3s during initial -# cluster startup. Note that these files MUST be present before K3s is started the first -# time; certificate data SHOULD NOT be changed once the cluster has been initialized. -# -# The output path may be overridden with the DATA_DIR environment variable. -# -# This script will also auto-generate certificates and keys for both root and intermediate -# certificate authorities if none are found. -# If you have only an existing root CA, provide: -# root-ca.pem -# root-ca.key. -# If you have an existing root and intermediate CA, provide: -# root-ca.pem -# intermediate-ca.pem -# intermediate-ca.key. - -set -e -umask 027 - -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}}" - -if type -t openssl-3 &>/dev/null; then - OPENSSL=openssl-3 -else - OPENSSL=openssl -fi - -echo "Using $(which ${OPENSSL}): $(${OPENSSL} version)" - -if ! ${OPENSSL} ecparam -help &>/dev/null; then - echo "openssl not found or missing Elliptic Curve (ecparam) support." - exit 1 -fi - -if ! ${OPENSSL} req -help 2>&1 | grep -q CAkey; then - echo "openssl req missing -CAkey support; please use OpenSSL 3.0.0 or newer" - exit 1 -fi - -mkdir -p "${DATA_DIR}/server/tls/etcd" -cd "${DATA_DIR}/server/tls" - -# 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 -# rotation and things will get very angry if all the SA keys are invalidated. -if [[ -e service.key ]]; then - echo "Generating additional Kubernetes service account issuer RSA key" - OLD_SERVICE_KEY="$(cat service.key)" -else - echo "Generating Kubernetes service account issuer RSA key" -fi -${OPENSSL} genrsa -traditional -out service.key 2048 -echo "${OLD_SERVICE_KEY}" >> service.key - -# Use existing root CA if present -if [[ -e root-ca.pem ]]; then - echo "Using existing root certificate" -else - echo "Generating root certificate authority RSA key and certificate" - ${OPENSSL} genrsa -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 -fi -cat root-ca.pem > root-ca.crt - -# Use existing intermediate CA if present -if [[ -e intermediate-ca.pem ]]; then - echo "Using existing intermediate certificate" -else - if [[ ! -e root-ca.key ]]; then - echo "Cannot generate intermediate certificate without root certificate private key" - exit 1 - fi - - echo "Generating intermediate certificate authority RSA key and certificate" - ${OPENSSL} genrsa -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 -fi -cat intermediate-ca.pem root-ca.pem > intermediate-ca.crt - -if [[ ! -e intermediate-ca.key ]]; then - echo "Cannot generate leaf certificates without intermediate certificate private key" - exit 1 -fi - -# Generate new leaf CAs for all the control-plane and etcd components -echo "Generating Kubernetes server leaf certificate authority EC key and certificate" -${OPENSSL} ecparam -name prime256v1 -genkey -out client-ca.key -${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 -cat client-ca.pem intermediate-ca.pem root-ca.pem > client-ca.crt - -echo "Generating Kubernetes client leaf certificate authority EC key and certificate" -${OPENSSL} ecparam -name prime256v1 -genkey -out server-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 -cat server-ca.pem intermediate-ca.pem root-ca.pem > server-ca.crt - -echo "Generating Kubernetes request-header leaf certificate authority EC key and certificate" -${OPENSSL} ecparam -name prime256v1 -genkey -out request-header-ca.key -${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 -cat request-header-ca.pem intermediate-ca.pem root-ca.pem > request-header-ca.crt - -echo "Generating etcd peer leaf certificate authority EC key and certificate" -${OPENSSL} ecparam -name prime256v1 -genkey -out etcd/peer-ca.key -${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 -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 "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:" -ls ${DATA_DIR}/server/tls/root-ca.* ${DATA_DIR}/server/tls/intermediate-ca.* | xargs -n1 echo -e "\t" diff --git a/contrib/util/generate-custom-ca-certs.sh b/contrib/util/generate-custom-ca-certs.sh new file mode 100755 index 000000000000..e6177eefe43c --- /dev/null +++ b/contrib/util/generate-custom-ca-certs.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +# Example K3s CA certificate generation script. +# +# This script will generate files sufficient to bootstrap K3s cluster certificate +# authorities. By default, the script will create the required files under +# /var/lib/rancher/k3s/server/tls, where they will be found and used by K3s during initial +# cluster startup. Note that these files MUST be present before K3s is started the first +# time; certificate data SHOULD NOT be changed once the cluster has been initialized. +# +# The output path may be overridden with the DATA_DIR environment variable. +# +# This script will also auto-generate certificates and keys for both root and intermediate +# certificate authorities if none are found. +# If you have only an existing root CA, provide: +# root-ca.pem +# root-ca.key. +# If you have an existing root and intermediate CA, provide: +# root-ca.pem +# intermediate-ca.pem +# intermediate-ca.key. + +set -e +umask 027 + +TIMESTAMP=$(date +%s) +PRODUCT="${PRODUCT:-k3s}" +DATA_DIR="${DATA_DIR:-/var/lib/rancher/${PRODUCT}}" + +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 -noout -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 "${DATA_DIR}/server/tls/etcd" +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 +# 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. +if [[ -e service.key ]]; then + echo "Generating additional Kubernetes service account issuer RSA key" + OLD_SERVICE_KEY="$(cat service.key)" +else + echo "Generating Kubernetes service account issuer RSA key" +fi +${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out service.key 2048 +echo "${OLD_SERVICE_KEY}" >> service.key + +# Use existing root CA if present +if [[ -e root-ca.pem ]]; then + echo "Using existing root certificate" +else + echo "Generating root certificate authority RSA key and certificate" + ${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out root-ca.key 4096 + ${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 +cat root-ca.pem > root-ca.crt + +# Use existing intermediate CA if present +if [[ -e intermediate-ca.pem ]]; then + echo "Using existing intermediate certificate" +else + if [[ ! -e root-ca.key ]]; then + echo "Cannot generate intermediate certificate without root certificate private key" + exit 1 + fi + + echo "Generating intermediate certificate authority RSA key and certificate" + ${OPENSSL} genrsa ${OPENSSL_GENRSA_FLAGS:-} -out intermediate-ca.key 4096 + ${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 +cat intermediate-ca.pem root-ca.pem > intermediate-ca.crt + +if [[ ! -e intermediate-ca.key ]]; then + echo "Cannot generate leaf certificates without intermediate certificate private key" + exit 1 +fi + +# Generate new leaf CAs for all the control-plane and etcd components +for TYPE in client server request-header etcd/peer etcd/server; do + CERT_NAME="${PRODUCT}-$(echo ${TYPE} | tr / -)-ca" + echo "Generating ${CERT_NAME} leaf certificate authority EC key and certificate" + ${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 3700 \ + -in /dev/stdin \ + -out ${TYPE}-ca.pem \ + -keyfile intermediate-ca.key \ + -cert intermediate-ca.pem \ + -config .ca/config \ + -extensions v3_ca + cat ${TYPE}-ca.pem \ + intermediate-ca.pem \ + root-ca.pem > ${TYPE}-ca.crt +done + +echo +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:" +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 diff --git a/contrib/util/rotate-default-ca-certs.sh b/contrib/util/rotate-default-ca-certs.sh new file mode 100755 index 000000000000..0ddd456f9159 --- /dev/null +++ b/contrib/util/rotate-default-ca-certs.sh @@ -0,0 +1,134 @@ +#!/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}" diff --git a/pkg/daemons/control/deps/deps.go b/pkg/daemons/control/deps/deps.go index be10d9ebca5f..6753e6c792ed 100644 --- a/pkg/daemons/control/deps/deps.go +++ b/pkg/daemons/control/deps/deps.go @@ -24,6 +24,7 @@ import ( "github.com/k3s-io/k3s/pkg/daemons/config" "github.com/k3s-io/k3s/pkg/passwd" "github.com/k3s-io/k3s/pkg/token" + "github.com/k3s-io/k3s/pkg/util" "github.com/k3s-io/k3s/pkg/version" certutil "github.com/rancher/dynamiclistener/cert" "github.com/sirupsen/logrus" @@ -592,7 +593,7 @@ func createClientCertKey(regen bool, commonName string, organization []string, a return false, err } - caCert, err := certutil.CertsFromFile(caCertFile) + caCerts, err := certutil.CertsFromFile(caCertFile) if err != nil { return false, err } @@ -615,12 +616,12 @@ func createClientCertKey(regen bool, commonName string, organization []string, a if altNames != nil { cfg.AltNames = *altNames } - cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCert[0], caKey.(crypto.Signer)) + cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCerts[0], caKey.(crypto.Signer)) if err != nil { return false, err } - return true, certutil.WriteCert(certFile, append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) + return true, certutil.WriteCert(certFile, util.EncodeCertsPEM(cert, caCerts)) } func exists(files ...string) bool { diff --git a/pkg/server/cert.go b/pkg/server/cert.go index e90e2c6c4e35..db26f632cd7b 100644 --- a/pkg/server/cert.go +++ b/pkg/server/cert.go @@ -143,10 +143,6 @@ func validateCA(oldCAPath, newCAPath string) error { return err } - if len(oldCerts) == 1 { - return errors.New("old CA is self-signed") - } - newCerts, err := certutil.CertsFromFile(newCAPath) if err != nil { return err @@ -158,16 +154,26 @@ func validateCA(oldCAPath, newCAPath string) error { roots := x509.NewCertPool() intermediates := x509.NewCertPool() - for i, cert := range oldCerts { + + // Load all certs from the old bundle + for _, cert := range oldCerts { + if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) { + roots.AddCert(cert) + } else { + intermediates.AddCert(cert) + } + } + + // Include any intermediates from the new bundle, in case they're cross-signed by a cert in the old bundle + for i, cert := range newCerts { if i > 0 { - if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) { - roots.AddCert(cert) - } else { + if len(cert.AuthorityKeyId) > 0 { intermediates.AddCert(cert) } } } + // Verify the first cert in the bundle, using the combined roots and intermediates _, err = newCerts[0].Verify(x509.VerifyOptions{Roots: roots, Intermediates: intermediates}) if err != nil { err = errors.Wrap(err, "new CA cert cannot be verified using old CA chain") diff --git a/pkg/server/router.go b/pkg/server/router.go index 37169e1fe1b1..435e6b7fdf45 100644 --- a/pkg/server/router.go +++ b/pkg/server/router.go @@ -219,7 +219,7 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo return } - caCert, caKey, key, err := getCACertAndKeys(server.Runtime.ServerCA, server.Runtime.ServerCAKey, server.Runtime.ServingKubeletKey) + caCerts, caKey, key, err := getCACertAndKeys(server.Runtime.ServerCA, server.Runtime.ServerCAKey, server.Runtime.ServingKubeletKey) if err != nil { sendError(err, resp) return @@ -245,7 +245,7 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo DNSNames: []string{nodeName, "localhost"}, IPs: ips, }, - }, key, caCert[0], caKey) + }, key, caCerts[0], caKey) if err != nil { sendError(err, resp) return @@ -257,7 +257,7 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo return } - resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) + resp.Write(util.EncodeCertsPEM(cert, caCerts)) resp.Write(keyBytes) }) } @@ -275,7 +275,7 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot return } - caCert, caKey, key, err := getCACertAndKeys(server.Runtime.ClientCA, server.Runtime.ClientCAKey, server.Runtime.ClientKubeletKey) + caCerts, caKey, key, err := getCACertAndKeys(server.Runtime.ClientCA, server.Runtime.ClientCAKey, server.Runtime.ClientKubeletKey) if err != nil { sendError(err, resp) return @@ -285,7 +285,7 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot CommonName: "system:node:" + nodeName, Organization: []string{user.NodesGroup}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - }, key, caCert[0], caKey) + }, key, caCerts[0], caKey) if err != nil { sendError(err, resp) return @@ -297,7 +297,7 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot return } - resp.Write(append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCert[0])...)) + resp.Write(util.EncodeCertsPEM(cert, caCerts)) resp.Write(keyBytes) }) } diff --git a/pkg/util/cert.go b/pkg/util/cert.go new file mode 100644 index 000000000000..bad59d8d9a4f --- /dev/null +++ b/pkg/util/cert.go @@ -0,0 +1,17 @@ +package util + +import ( + "crypto/x509" + + certutil "github.com/rancher/dynamiclistener/cert" +) + +// EncodeCertsPEM is a wrapper around the EncodeCertPEM function to return the +// PEM encoding of a cert and chain, instead of just a single cert. +func EncodeCertsPEM(cert *x509.Certificate, caCerts []*x509.Certificate) []byte { + pemBytes := certutil.EncodeCertPEM(cert) + for _, caCert := range caCerts { + pemBytes = append(pemBytes, certutil.EncodeCertPEM(caCert)...) + } + return pemBytes +} diff --git a/scripts/test-run-cacerts b/scripts/test-run-cacerts index b591b0e81fd6..89170bafe520 100755 --- a/scripts/test-run-cacerts +++ b/scripts/test-run-cacerts @@ -28,7 +28,7 @@ cluster-pre-hook() { rancher/mirrored-pause:3.6 \ >/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 } export -f cluster-pre-hook