Renew etcd certificates generated by custom etcd CA

k0s renews certs issued by a custom cluster CA,
and it should do the same for certs issued by
a custom etcd CA.

Fixes #7795

Signed-off-by: Dmitrii Shishliannikov <dshishliannikov@mirantis.com>
This commit is contained in:
Dmitrii Shishliannikov committed 2026-08-19 08:44:46 -07:00
1 parent 3cd439fbb2
commit 2b9f3a214b
2 files changed
+50 -8

No files matched your search

+34 -8
View File
@@ -17,28 +17,43 @@ type CustomCASuite struct {
common.BootlooseSuite
}
func (s *CustomCASuite) initCA(ssh *common.SSHConnection, dir, cn string) string {
err := ssh.Exec(s.Context(), "sh -e", common.SSHStreams{
//nolint:dupword // this is a script
In: strings.NewReader(strings.NewReplacer("{dir}", dir, "{cn}", cn).Replace(`
mkdir -p {dir}
command -v openssl || apk add openssl
openssl genrsa -out {dir}/ca.key 4096
openssl req -x509 -new -nodes -key {dir}/ca.key -sha256 -days 365 -out {dir}/ca.crt -subj "/CN={cn}"
`)),
})
s.Require().NoError(err)
cert, err := ssh.ExecWithOutput(s.Context(), fmt.Sprintf("cat %s/ca.crt", dir))
s.Require().NoError(err)
return cert
}
func (s *CustomCASuite) TestK0sGetsUp() {
// Create an custom certificate to prove that k0s manage to work with it
ssh, err := s.SSH(s.Context(), s.ControllerNode(0))
s.Require().NoError(err)
defer ssh.Disconnect()
caCert := s.initCA(ssh, "/var/lib/k0s/pki", "Test CA")
etcdCACert := s.initCA(ssh, "/var/lib/k0s/pki/etcd", "Test Etcd CA")
err = ssh.Exec(s.Context(), "sh -e", common.SSHStreams{
//nolint:dupword // this is a script
In: strings.NewReader(fmt.Sprintf(`
K0S_PATH=%q
IP_ADDRESS=%q
mkdir -p /var/lib/k0s/pki /var/lib/k0s/manifests/test
apk add openssl
openssl genrsa -out /var/lib/k0s/pki/ca.key 4096
openssl req -x509 -new -nodes -key /var/lib/k0s/pki/ca.key -sha256 -days 365 -out /var/lib/k0s/pki/ca.crt -subj "/CN=Test CA"
mkdir -p /var/lib/k0s/manifests/test
"$K0S_PATH" token pre-shared --cert /var/lib/k0s/pki/ca.crt --url https://"$IP_ADDRESS":6443/ --out /var/lib/k0s/manifests/test
`, s.K0sFullPath, s.GetControllerIPAddress(0))),
})
s.Require().NoError(err)
cert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/ca.crt")
s.Require().NoError(err)
token, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/manifests/test/token_* && rm /var/lib/k0s/manifests/test/token_*")
s.Require().NoError(err)
@@ -56,11 +71,19 @@ func (s *CustomCASuite) TestK0sGetsUp() {
newCert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/ca.crt")
s.Require().NoError(err)
s.Equal(cert, newCert)
s.Equal(caCert, newCert)
newEtcdCert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/etcd/ca.crt")
s.Require().NoError(err)
s.Equal(etcdCACert, newEtcdCert)
// Validate that k0s renews certificates with custom CA
k0sAPICert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/k0s-api.crt")
s.Require().NoError(err, "Failed to obtain k0s-api certificate")
etcdServerCert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/etcd/server.crt")
s.Require().NoError(err, "Failed to obtain etcd server certificate")
s.Require().NoError(s.StopController(s.ControllerNode(0)))
s.Require().NoError(s.StartController(s.ControllerNode(0)))
@@ -69,6 +92,9 @@ func (s *CustomCASuite) TestK0sGetsUp() {
s.Require().NoError(err, "Failed to obtain new k0s certificate")
s.Require().NotEqual(k0sAPICert, newK0sAPICert, "k0s-api certificate was not renewed")
newEtcdServerCert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/etcd/server.crt")
s.Require().NoError(err, "Failed to obtain new etcd server certificate")
s.Require().NotEqual(etcdServerCert, newEtcdServerCert, "etcd server certificate was not renewed")
}
func TestCustomCASuite(t *testing.T) {
+16
View File
@@ -223,6 +223,16 @@ func (m *Manager) isManagedByK0s(cert *certinfo.Certificate) (bool, error) {
return false, fmt.Errorf("unable to parse ca certificate: %w", err)
}
etcdCA, err := certinfo.ParseCertificateFile(filepath.Join(m.K0sVars.EtcdCertDir, "ca.crt"))
if err != nil {
// etcd CA is created separately from the cluster CA
// and may not available right away on the first start,
// this is expected and should not result in an error
logrus.Warnf("unable to parse etcd ca certificate: %v ", err)
// fall back to the default etcd CA
etcdCA = &certinfo.Certificate{Subject: certinfo.Name{CommonName: "etcd-ca"}}
}
switch cert.Issuer.CommonName {
case "kubernetes-ca":
return true, nil
@@ -236,6 +246,12 @@ func (m *Manager) isManagedByK0s(cert *certinfo.Certificate) (bool, error) {
}
logrus.Warnf("certificate issued by %q, but no ca.key found, not renewing the certificate %q", ca.Subject.CommonName, cert.Subject.CommonName)
return false, nil
case etcdCA.Subject.CommonName:
if file.Exists(filepath.Join(m.K0sVars.EtcdCertDir, "ca.key")) {
return true, nil
}
logrus.Warnf("certificate issued by %q, but no ca.key found, not renewing the certificate %q", etcdCA.Subject.CommonName, cert.Subject.CommonName)
return false, nil
}
return false, nil