Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions inttest/customca/customca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

cert := s.initCA(ssh, "/var/lib/k0s/pki", "Test CA")
etcdCert := 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)

Expand All @@ -58,9 +73,17 @@ func (s *CustomCASuite) TestK0sGetsUp() {
s.Require().NoError(err)
s.Equal(cert, newCert)

newEtcdCert, err := ssh.ExecWithOutput(s.Context(), "cat /var/lib/k0s/pki/etcd/ca.crt")
s.Require().NoError(err)
s.Equal(etcdCert, 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)))

Expand All @@ -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) {
Expand Down
14 changes: 14 additions & 0 deletions pkg/certificate/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ 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)
}

switch cert.Issuer.CommonName {
case "kubernetes-ca":
return true, nil
Expand All @@ -236,6 +244,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
Expand Down
Loading