Skip to content

Commit

Permalink
operator: Customizable Certificate Configuration
Browse files Browse the repository at this point in the history
Signed-off-by: zhzhuang-zju <[email protected]>
  • Loading branch information
zhzhuang-zju committed Dec 12, 2024
1 parent 8457cd2 commit 03aad2b
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 35 deletions.
34 changes: 34 additions & 0 deletions charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3698,6 +3698,40 @@ spec:
referenced.
type: string
type: object
certConfig:
description: CertConfig represents the config to generate certificate
by karmada-operator.
properties:
expiry:
description: |-
Expiry is the duration of the certificate's validity period.
The unit is days, where 1 represents one day.
Defaults to 365.
Ignored when NotAfter and NotBefore are not nil.
format: int32
type: integer
notAfter:
description: |-
NotAfter specifies the end date of the certificate's validity period.
This field can be nil, indicating that the end time will be calculated based on Expiry and NotBefore.
format: date-time
type: string
notBefore:
description: |-
NotBefore specifies the start date of the certificate's validity period.
If NotBefore and NotAfter are both nil, the certificate will inherit the NotBefore date from the CA certificate during its generation.
If NotBefore is nil, the start date of the certificate's validity period will be calculated based on Expiry and NotAfter.
format: date-time
type: string
publicKeyAlgorithm:
description: |-
PublicKeyAlgorithm is the public key algorithm used for the certificate.
This can be RSA or ECDSA.
For ECDSA, using the P-256 elliptic curve.
For RSA, the key is generated with a size of 3072 bits.
Defaults to RSA
type: string
type: object
type: object
featureGates:
additionalProperties:
Expand Down
34 changes: 34 additions & 0 deletions operator/config/crds/operator.karmada.io_karmadas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3698,6 +3698,40 @@ spec:
referenced.
type: string
type: object
certConfig:
description: CertConfig represents the config to generate certificate
by karmada-operator.
properties:
expiry:
description: |-
Expiry is the duration of the certificate's validity period.
The unit is days, where 1 represents one day.
Defaults to 365.
Ignored when NotAfter and NotBefore are not nil.
format: int32
type: integer
notAfter:
description: |-
NotAfter specifies the end date of the certificate's validity period.
This field can be nil, indicating that the end time will be calculated based on Expiry and NotBefore.
format: date-time
type: string
notBefore:
description: |-
NotBefore specifies the start date of the certificate's validity period.
If NotBefore and NotAfter are both nil, the certificate will inherit the NotBefore date from the CA certificate during its generation.
If NotBefore is nil, the start date of the certificate's validity period will be calculated based on Expiry and NotAfter.
format: date-time
type: string
publicKeyAlgorithm:
description: |-
PublicKeyAlgorithm is the public key algorithm used for the certificate.
This can be RSA or ECDSA.
For ECDSA, using the P-256 elliptic curve.
For RSA, the key is generated with a size of 3072 bits.
Defaults to RSA
type: string
type: object
type: object
featureGates:
additionalProperties:
Expand Down
18 changes: 18 additions & 0 deletions operator/pkg/apis/operator/v1alpha1/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func SetObjectDefaultsKarmada(in *Karmada) {
func setDefaultsKarmada(obj *Karmada) {
setDefaultsHostCluster(obj)
setDefaultsKarmadaComponents(obj)
setDefaultsCertConfig(obj)
}

func setDefaultsKarmadaComponents(obj *Karmada) {
Expand Down Expand Up @@ -108,6 +109,23 @@ func setDefaultsHostCluster(obj *Karmada) {
}
}

func setDefaultsCertConfig(obj *Karmada) {
if obj.Spec.CustomCertificate == nil {
obj.Spec.CustomCertificate = &CustomCertificate{
CertConfig: &CertConfig{},
}
}
if obj.Spec.CustomCertificate.CertConfig == nil {
obj.Spec.CustomCertificate.CertConfig = &CertConfig{}
}
if obj.Spec.CustomCertificate.CertConfig.Expiry == 0 {
obj.Spec.CustomCertificate.CertConfig.Expiry = 365
}
if obj.Spec.CustomCertificate.CertConfig.PublicKeyAlgorithm == nil {
obj.Spec.CustomCertificate.CertConfig.PublicKeyAlgorithm = ptr.To[string]("RSA")
}
}

func setDefaultsEtcd(obj *KarmadaComponents) {
if obj.Etcd == nil {
obj.Etcd = &Etcd{}
Expand Down
31 changes: 31 additions & 0 deletions operator/pkg/apis/operator/v1alpha1/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,37 @@ type CustomCertificate struct {
// all components that access the APIServer as clients.
// +optional
APIServerCACert *LocalSecretReference `json:"apiServerCACert,omitempty"`

// CertConfig represents the config to generate certificate by karmada-operator.
// +optional
CertConfig *CertConfig `json:"certConfig,omitempty"`
}

// CertConfig represents the configuration for creating a signing certificate.
// It includes settings such as expiry, not_before, not_after, and public key algorithm.
type CertConfig struct {
// Expiry is the duration of the certificate's validity period.
// The unit is days, where 1 represents one day.
// Defaults to 365.
// Ignored when NotAfter and NotBefore are not nil.
// +optional
Expiry int32 `json:"expiry,omitempty"`
// NotBefore specifies the start date of the certificate's validity period.
// If NotBefore and NotAfter are both nil, the certificate will inherit the NotBefore date from the CA certificate during its generation.
// If NotBefore is nil, the start date of the certificate's validity period will be calculated based on Expiry and NotAfter.
// +optional
NotBefore *metav1.Time `json:"notBefore,omitempty"`
// NotAfter specifies the end date of the certificate's validity period.
// This field can be nil, indicating that the end time will be calculated based on Expiry and NotBefore.
// +optional
NotAfter *metav1.Time `json:"notAfter,omitempty"`
// PublicKeyAlgorithm is the public key algorithm used for the certificate.
// This can be RSA or ECDSA.
// For ECDSA, using the P-256 elliptic curve.
// For RSA, the key is generated with a size of 3072 bits.
// Defaults to RSA
// +optional
PublicKeyAlgorithm *string `json:"publicKeyAlgorithm,omitempty"`
}

// ImageRegistry represents an image registry as well as the
Expand Down
34 changes: 34 additions & 0 deletions operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 20 additions & 14 deletions operator/pkg/certs/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ type altNamesMutatorFunc func(*AltNamesMutatorConfig, *CertConfig) error
type CertConfig struct {
Name string
CAName string
NotAfter *time.Time
PublicKeyAlgorithm x509.PublicKeyAlgorithm // TODO: All public key of karmada cert use the RSA algorithm by default
NotAfter time.Time
Expiry int32
PublicKeyAlgorithm x509.PublicKeyAlgorithm
Config certutil.Config
AltNamesMutatorFunc altNamesMutatorFunc
}
Expand All @@ -77,13 +78,6 @@ func (config *CertConfig) defaultPublicKeyAlgorithm() {
}
}

func (config *CertConfig) defaultNotAfter() {
if config.NotAfter == nil {
notAfter := time.Now().Add(constants.CertificateValidity).UTC()
config.NotAfter = &notAfter
}
}

// GetDefaultCertList returns all of karmada certConfigs, it include karmada, front and etcd.
func GetDefaultCertList() []*CertConfig {
return []*CertConfig{
Expand Down Expand Up @@ -301,9 +295,6 @@ func CreateCertAndKeyFilesWithCA(cc *CertConfig, caCertData, caKeyData []byte) (
return nil, fmt.Errorf("must specify at least one ExtKeyUsage")
}

cc.defaultNotAfter()
cc.defaultPublicKeyAlgorithm()

key, err := GeneratePrivateKey(cc.PublicKeyAlgorithm)
if err != nil {
return nil, fmt.Errorf("unable to create private key, err: %w", err)
Expand Down Expand Up @@ -358,6 +349,21 @@ func NewSignedCert(cc *CertConfig, key crypto.Signer, caCert *x509.Certificate,

RemoveDuplicateAltNames(&cc.Config.AltNames)

var notBefore time.Time
if cc.Config.NotBefore.IsZero() && cc.NotAfter.IsZero() {
notBefore = caCert.NotBefore
} else if cc.Config.NotBefore.IsZero() && !cc.NotAfter.IsZero() {
notBefore = cc.NotAfter.Add(-time.Hour * 24 * time.Duration(cc.Expiry))
} else {
notBefore = cc.Config.NotBefore
}
var notAfter time.Time
if cc.NotAfter.IsZero() {
notAfter = notBefore.Add(time.Hour * 24 * time.Duration(cc.Expiry))
} else {
notAfter = cc.NotAfter
}

certTmpl := x509.Certificate{
Subject: pkix.Name{
CommonName: cc.Config.CommonName,
Expand All @@ -366,8 +372,8 @@ func NewSignedCert(cc *CertConfig, key crypto.Signer, caCert *x509.Certificate,
DNSNames: cc.Config.AltNames.DNSNames,
IPAddresses: cc.Config.AltNames.IPs,
SerialNumber: serial,
NotBefore: caCert.NotBefore,
NotAfter: cc.NotAfter.UTC(),
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: keyUsage,
ExtKeyUsage: cc.Config.Usages,
BasicConstraintsValid: true,
Expand Down
22 changes: 2 additions & 20 deletions operator/pkg/certs/certs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,6 @@ func TestCertConfig_defaultPublicKeyAlgorithm(t *testing.T) {
}
}

func TestCertConfig_defaultNotAfter(t *testing.T) {
c := &CertConfig{}
c.defaultNotAfter()

if c.NotAfter == nil {
t.Error("expected NotAfter to be set, but it was nil")
}

if !c.NotAfter.After(time.Now()) {
t.Errorf("expected NotAfter to be a future time, got %v", c.NotAfter)
}

expectedTime := time.Now().Add(constants.CertificateValidity).UTC()
if c.NotAfter.Sub(expectedTime) > time.Minute {
t.Errorf("NotAfter time is too far from expected, got %v, expected %v", c.NotAfter, expectedTime)
}
}

func TestKarmadaCertRootCA(t *testing.T) {
certConfig := KarmadaCertRootCA()

Expand Down Expand Up @@ -590,7 +572,7 @@ func TestNewSignedCert_Success(t *testing.T) {
AltNames: certutil.AltNames{DNSNames: expectedCertDNSNames},
Usages: expectedUsages,
},
NotAfter: &certNotAfter,
NotAfter: certNotAfter,
}

cert, err := NewSignedCert(cc, key, caCert, caKey, false)
Expand Down Expand Up @@ -638,7 +620,7 @@ func TestNewSignedCert_ErrorOnEmptyCommonName(t *testing.T) {
AltNames: certutil.AltNames{DNSNames: expectedCertDNSNames},
Usages: expectedUsages,
},
NotAfter: &certNotAfter,
NotAfter: certNotAfter,
}

_, err = NewSignedCert(cc, key, &x509.Certificate{}, key, false)
Expand Down
12 changes: 12 additions & 0 deletions operator/pkg/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ func (opt *InitOptions) Validate() error {
return fmt.Errorf("unexpected karmada invalid version %s", opt.KarmadaVersion)
}

if opt.CustomCertificateConfig.CertConfig.Expiry < 0 {
return fmt.Errorf("invalid custom cert expiry: %d", opt.CustomCertificateConfig.CertConfig.Expiry)
}

if opt.CustomCertificateConfig.CertConfig.NotAfter.Before(opt.CustomCertificateConfig.CertConfig.NotBefore) {
return fmt.Errorf("invalid custom cert config: notbefore, %s. notafter, %s", opt.CustomCertificateConfig.CertConfig.NotBefore, opt.CustomCertificateConfig.CertConfig.NotAfter)
}

if *opt.CustomCertificateConfig.CertConfig.PublicKeyAlgorithm != "RSA" && *opt.CustomCertificateConfig.CertConfig.PublicKeyAlgorithm != "ECDSA" {
return fmt.Errorf("invalid custom cert config: publicKeyAlgorithm, %s", *opt.CustomCertificateConfig.CertConfig.PublicKeyAlgorithm)
}

return nil
}

Expand Down
23 changes: 23 additions & 0 deletions operator/pkg/tasks/init/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package tasks

import (
"context"
"crypto/x509"
"errors"
"fmt"

Expand Down Expand Up @@ -187,6 +188,28 @@ func runCertTask(cc, caCert *certs.CertConfig) func(d workflow.RunData) error {
}

func mutateCertConfig(data InitData, cc *certs.CertConfig) error {
cc.PublicKeyAlgorithm = x509.RSA

customCertConfig := data.CustomCertificate().CertConfig
if customCertConfig != nil {
if customCertConfig.PublicKeyAlgorithm != nil {
switch *customCertConfig.PublicKeyAlgorithm {
case "ECDSA":
cc.PublicKeyAlgorithm = x509.ECDSA
default:
cc.PublicKeyAlgorithm = x509.RSA
}
}

cc.Expiry = customCertConfig.Expiry
if customCertConfig.NotBefore != nil {
cc.Config.NotBefore = customCertConfig.NotBefore.UTC()
}
if customCertConfig.NotAfter != nil {
cc.NotAfter = customCertConfig.NotAfter.UTC()
}
}

if cc.AltNamesMutatorFunc != nil {
err := cc.AltNamesMutatorFunc(&certs.AltNamesMutatorConfig{
Name: data.GetName(),
Expand Down
2 changes: 1 addition & 1 deletion operator/pkg/tasks/init/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func buildKubeConfigFromSpec(data InitData, serverURL string) (*clientcmdapi.Con
cc := certs.KarmadaCertClient()

if err := mutateCertConfig(data, cc); err != nil {
return nil, fmt.Errorf("error when mutate cert altNames for %s, err: %w", cc.Name, err)
return nil, fmt.Errorf("error when mutate cert config for %s, err: %w", cc.Name, err)
}
client, err := certs.CreateCertAndKeyFilesWithCA(cc, ca.CertData(), ca.KeyData())
if err != nil {
Expand Down

0 comments on commit 03aad2b

Please sign in to comment.