Skip to content

Commit

Permalink
Include signing CA certificate in kube-apiserver's certificate chain (
Browse files Browse the repository at this point in the history
gardener#7961)

* Support inclusion of CA in server cert chain

Disabled/false by default

* Include CA in `kube-apiserver`'s server cert chain
  • Loading branch information
rfranzke authored May 25, 2023
1 parent 524f0f0 commit 6ff99b3
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 19 deletions.
13 changes: 7 additions & 6 deletions pkg/component/kubeapiserver/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,13 @@ func (k *kubeAPIServer) reconcileSecretServer(ctx context.Context) (*corev1.Secr
}

return k.secretsManager.Generate(ctx, &secretsutils.CertificateSecretConfig{
Name: secretNameServer,
CommonName: deploymentName,
IPAddresses: append(ipAddresses, k.values.ServerCertificate.ExtraIPAddresses...),
DNSNames: append(dnsNames, k.values.ServerCertificate.ExtraDNSNames...),
CertType: secretsutils.ServerCert,
SkipPublishingCACertificate: true,
Name: secretNameServer,
CommonName: deploymentName,
IPAddresses: append(ipAddresses, k.values.ServerCertificate.ExtraIPAddresses...),
DNSNames: append(dnsNames, k.values.ServerCertificate.ExtraDNSNames...),
CertType: secretsutils.ServerCert,
SkipPublishingCACertificate: true,
IncludeCACertificateInServerChain: true,
}, secretsmanager.SignedByCA(v1beta1constants.SecretNameCACluster), secretsmanager.Rotate(secretsmanager.InPlace))
}

Expand Down
31 changes: 21 additions & 10 deletions pkg/utils/secrets/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,20 @@ type CertificateSecretConfig struct {
SigningCA *Certificate
PKCS int

Validity *time.Duration
SkipPublishingCACertificate bool
Validity *time.Duration
SkipPublishingCACertificate bool
IncludeCACertificateInServerChain bool
}

// Certificate contains the private key, and the certificate. It does also contain the CA certificate
// in case it is no CA. Otherwise, the <CA> field is nil.
type Certificate struct {
Name string

CA *Certificate
SkipPublishingCACertificate bool
CA *Certificate
CertType CertType
SkipPublishingCACertificate bool
IncludeCACertificateInServerChain bool

PrivateKey *rsa.PrivateKey
PrivateKeyPEM []byte
Expand All @@ -104,9 +107,11 @@ func (s *CertificateSecretConfig) Generate() (DataInterface, error) {
// GenerateCertificate is the same as Generate but returns a *Certificate instead of the DataInterface.
func (s *CertificateSecretConfig) GenerateCertificate() (*Certificate, error) {
certificateObj := &Certificate{
Name: s.Name,
CA: s.SigningCA,
SkipPublishingCACertificate: s.SkipPublishingCACertificate,
Name: s.Name,
CA: s.SigningCA,
CertType: s.CertType,
SkipPublishingCACertificate: s.SkipPublishingCACertificate,
IncludeCACertificateInServerChain: s.IncludeCACertificateInServerChain,
}

// If no cert type is given then we only return a certificate object that contains the CA.
Expand Down Expand Up @@ -162,11 +167,17 @@ func (c *Certificate) SecretData() map[string][]byte {
// compatibility).
data[DataKeyCertificateCA] = c.CertificatePEM
data[DataKeyPrivateKeyCA] = c.PrivateKeyPEM

case c.CA != nil:
// The certificate is not a CA certificate, so we add the signing CA certificate to it and use different
// keys in the secret data.
cert := c.CertificatePEM
if c.CertType == ServerCert && c.IncludeCACertificateInServerChain {
cert = make([]byte, 0, len(c.CA.CertificatePEM)+len(c.CertificatePEM))
cert = append(cert, c.CertificatePEM...)
cert = append(cert, c.CA.CertificatePEM...)
}

data[DataKeyCertificate] = cert
data[DataKeyPrivateKey] = c.PrivateKeyPEM
data[DataKeyCertificate] = c.CertificatePEM
if !c.SkipPublishingCACertificate {
data[DataKeyCertificateCA] = c.CA.CertificatePEM
}
Expand Down
32 changes: 29 additions & 3 deletions pkg/utils/secrets/certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ var _ = Describe("Certificate Secrets", func() {
})

Describe("#SecretData", func() {
It("should properly return secret data if certificate type is CA", func() {
It("should properly return secret data for CA certificates", func() {
Expect(certificate.SecretData()).To(Equal(map[string][]byte{
DataKeyPrivateKeyCA: []byte("foo"),
DataKeyCertificateCA: []byte("bar"),
}))
})

It("should properly return secret data if certificate type is server, client or both", func() {
It("should properly return secret data for non-CA certificates", func() {
certificate.CA = &Certificate{CertificatePEM: []byte("ca")}

Expect(certificate.SecretData()).To(Equal(map[string][]byte{
Expand All @@ -79,7 +79,7 @@ var _ = Describe("Certificate Secrets", func() {
}))
})

It("should properly return secret data if certificate type is server, client or both w/o publishing CA", func() {
It("should properly return secret data for non-CA certificates w/o publishing CA", func() {
certificate.CA = &Certificate{CertificatePEM: []byte("ca")}
certificate.SkipPublishingCACertificate = true

Expand All @@ -88,6 +88,32 @@ var _ = Describe("Certificate Secrets", func() {
DataKeyCertificate: []byte("bar"),
}))
})

Context("w/ CA included in chain", func() {
BeforeEach(func() {
certificate.CA = &Certificate{CertificatePEM: []byte("ca")}
certificate.IncludeCACertificateInServerChain = true
certificate.SkipPublishingCACertificate = true
})

It("should properly return secret data for server certificates", func() {
certificate.CertType = ServerCert

Expect(certificate.SecretData()).To(Equal(map[string][]byte{
DataKeyPrivateKey: []byte("foo"),
DataKeyCertificate: []byte("barca"),
}))
})

It("should properly return secret data for client certificates", func() {
certificate.CertType = ClientCert

Expect(certificate.SecretData()).To(Equal(map[string][]byte{
DataKeyPrivateKey: []byte("foo"),
DataKeyCertificate: []byte("bar"),
}))
})
})
})
})
})

0 comments on commit 6ff99b3

Please sign in to comment.