Skip to content

Commit

Permalink
fix(cert): checking api server certificate SAN entries
Browse files Browse the repository at this point in the history
Signed-off-by: Dario Tranchitella <[email protected]>
  • Loading branch information
prometherion committed Nov 30, 2024
1 parent 7904b4d commit c9279b0
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 8 deletions.
35 changes: 35 additions & 0 deletions internal/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import (
"fmt"
"math/big"
mathrand "math/rand"
"net"
"time"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"
)

// CheckPublicAndPrivateKeyValidity checks if the given bytes for the private and public keys are valid.
Expand All @@ -37,6 +39,39 @@ func CheckPublicAndPrivateKeyValidity(publicKey []byte, privateKey []byte) (bool
return checkPublicKeys(privKey.PublicKey, *pubKey), nil
}

// CheckCertificateSAN checks if the Kubernetes API Server certificate matches the SAN stored in the kubeadm:
// it must check both IPs and DNS names, and returns a false if the required entry isn't available.
// In case of removal of entries, this function returns true nevertheless to avoid reloading a Control Plane uselessly.
func CheckCertificateSAN(certificateBytes []byte, certSANs []string) (bool, error) {
crt, err := ParseCertificateBytes(certificateBytes)
if err != nil {
return false, err
}

ips := sets.New[string]()
for _, ip := range crt.IPAddresses {
ips.Insert(ip.String())
}

dns := sets.New[string](crt.DNSNames...)

for _, e := range certSANs {
if ip := net.ParseIP(e); ip != nil {
if !ips.Has(ip.String()) {
return false, nil
}

continue
}

if !dns.Has(e) {
return false, nil
}
}

return true, nil
}

// CheckCertificateAndPrivateKeyPairValidity checks if the certificate and private key pair are valid.
func CheckCertificateAndPrivateKeyPairValidity(certificate []byte, privateKey []byte) (bool, error) {
switch {
Expand Down
25 changes: 17 additions & 8 deletions internal/resources/api_server_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ func (r *APIServerCertificate) UpdateTenantControlPlaneStatus(_ context.Context,
func (r *APIServerCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
return func() error {
logger := log.FromContext(ctx, "resource", r.GetName())
// The Kubeadm configuration must be retrieved in advance:
// this is required to check also the certificate SAN
config, kadmErr := getStoredKubeadmConfiguration(ctx, r.Client, r.TmpDirectory, tenantControlPlane)
if kadmErr != nil {
logger.Error(kadmErr, "cannot retrieve stored kubeadm configuration", "err", kadmErr.Error())

return fmt.Errorf("failed to generate certificate and private key: %w", kadmErr)
}
// Retrieving the TenantControlPlane CA:
// this is required to trigger a new generation in case of Certificate Authority rotation.
namespacedName := k8stypes.NamespacedName{Namespace: tenantControlPlane.GetNamespace(), Name: tenantControlPlane.Status.Certificates.CA.SecretName}
Expand Down Expand Up @@ -121,16 +129,17 @@ func (r *APIServerCertificate) mutate(ctx context.Context, tenantControlPlane *k
logger.Info(fmt.Sprintf("%s certificate-private_key pair is not valid: %s", kubeadmconstants.APIServerCertAndKeyBaseName, err.Error()))
}

if isCAValid && isCertValid {
return nil
dnsNamesMatches, dnsErr := crypto.CheckCertificateSAN(r.resource.Data[kubeadmconstants.APIServerCertName], config.InitConfiguration.APIServer.CertSANs)
if dnsErr != nil {
logger.Info(fmt.Sprintf("%s SAN check returned an error: %s", kubeadmconstants.APIServerCertAndKeyBaseName, err.Error()))
}
if !dnsNamesMatches {
logger.Info("HERE")
}
}

config, err := getStoredKubeadmConfiguration(ctx, r.Client, r.TmpDirectory, tenantControlPlane)
if err != nil {
logger.Error(err, "cannot generate certificate and private key in api server certificate", "details", err.Error())

return fmt.Errorf("failed to generate certificate and private key: %w", err)
if isCAValid && isCertValid && dnsNamesMatches {
return nil
}
}

ca := kubeadm.CertificatePrivateKeyPair{
Expand Down

0 comments on commit c9279b0

Please sign in to comment.