Merge pull request #5842 from jabellard/external_ca_cert
Support Custom API Server CA Certificate for Karmada Instance in Operator
This commit is contained in:
commit
4dbcfaf9b6
|
@ -3673,6 +3673,32 @@ spec:
|
|||
type: string
|
||||
type: object
|
||||
type: object
|
||||
customCertificate:
|
||||
description: |-
|
||||
CustomCertificate specifies the configuration to customize the certificates
|
||||
for Karmada components or control the certificate generation process, such as
|
||||
the algorithm, validity period, etc.
|
||||
Currently, it only supports customizing the CA certificate for limited components.
|
||||
properties:
|
||||
apiServerCACert:
|
||||
description: |-
|
||||
APIServerCACert references a Kubernetes secret containing the CA certificate
|
||||
for component karmada-apiserver.
|
||||
The secret must contain the following data keys:
|
||||
- tls.crt: The TLS certificate.
|
||||
- tls.key: The TLS private key.
|
||||
If specified, this CA will be used to issue client certificates for
|
||||
all components that access the APIServer as clients.
|
||||
properties:
|
||||
name:
|
||||
description: Name is the name of resource being referenced.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace is the namespace for the resource being
|
||||
referenced.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
featureGates:
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
|
|
|
@ -3673,6 +3673,32 @@ spec:
|
|||
type: string
|
||||
type: object
|
||||
type: object
|
||||
customCertificate:
|
||||
description: |-
|
||||
CustomCertificate specifies the configuration to customize the certificates
|
||||
for Karmada components or control the certificate generation process, such as
|
||||
the algorithm, validity period, etc.
|
||||
Currently, it only supports customizing the CA certificate for limited components.
|
||||
properties:
|
||||
apiServerCACert:
|
||||
description: |-
|
||||
APIServerCACert references a Kubernetes secret containing the CA certificate
|
||||
for component karmada-apiserver.
|
||||
The secret must contain the following data keys:
|
||||
- tls.crt: The TLS certificate.
|
||||
- tls.key: The TLS private key.
|
||||
If specified, this CA will be used to issue client certificates for
|
||||
all components that access the APIServer as clients.
|
||||
properties:
|
||||
name:
|
||||
description: Name is the name of resource being referenced.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace is the namespace for the resource being
|
||||
referenced.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
featureGates:
|
||||
additionalProperties:
|
||||
type: boolean
|
||||
|
|
|
@ -113,6 +113,26 @@ type KarmadaSpec struct {
|
|||
// By default, the operator will only attempt to download the tarball if it's not yet present in the local cache.
|
||||
// +optional
|
||||
CRDTarball *CRDTarball `json:"crdTarball,omitempty"`
|
||||
|
||||
// CustomCertificate specifies the configuration to customize the certificates
|
||||
// for Karmada components or control the certificate generation process, such as
|
||||
// the algorithm, validity period, etc.
|
||||
// Currently, it only supports customizing the CA certificate for limited components.
|
||||
// +optional
|
||||
CustomCertificate *CustomCertificate `json:"customCertificate,omitempty"`
|
||||
}
|
||||
|
||||
// CustomCertificate holds the configuration for generating the certificate.
|
||||
type CustomCertificate struct {
|
||||
// APIServerCACert references a Kubernetes secret containing the CA certificate
|
||||
// for component karmada-apiserver.
|
||||
// The secret must contain the following data keys:
|
||||
// - tls.crt: The TLS certificate.
|
||||
// - tls.key: The TLS private key.
|
||||
// If specified, this CA will be used to issue client certificates for
|
||||
// all components that access the APIServer as clients.
|
||||
// +optional
|
||||
APIServerCACert *LocalSecretReference `json:"apiServerCACert,omitempty"`
|
||||
}
|
||||
|
||||
// ImageRegistry represents an image registry as well as the
|
||||
|
|
|
@ -106,6 +106,27 @@ func (in *CommonSettings) DeepCopy() *CommonSettings {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CustomCertificate) DeepCopyInto(out *CustomCertificate) {
|
||||
*out = *in
|
||||
if in.APIServerCACert != nil {
|
||||
in, out := &in.APIServerCACert, &out.APIServerCACert
|
||||
*out = new(LocalSecretReference)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomCertificate.
|
||||
func (in *CustomCertificate) DeepCopy() *CustomCertificate {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CustomCertificate)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Etcd) DeepCopyInto(out *Etcd) {
|
||||
*out = *in
|
||||
|
@ -637,6 +658,11 @@ func (in *KarmadaSpec) DeepCopyInto(out *KarmadaSpec) {
|
|||
*out = new(CRDTarball)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CustomCertificate != nil {
|
||||
in, out := &in.CustomCertificate, &out.CustomCertificate
|
||||
*out = new(CustomCertificate)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -218,6 +218,11 @@ type KarmadaCert struct {
|
|||
key []byte
|
||||
}
|
||||
|
||||
// NewKarmadaCert is used to create a new Karmada cert
|
||||
func NewKarmadaCert(pairName, caName string, cert, key []byte) *KarmadaCert {
|
||||
return &KarmadaCert{pairName: pairName, caName: caName, cert: cert, key: key}
|
||||
}
|
||||
|
||||
// CertData returns certificate cert data.
|
||||
func (cert *KarmadaCert) CertData() []byte {
|
||||
return cert.cert
|
||||
|
|
|
@ -42,13 +42,14 @@ var (
|
|||
|
||||
// InitOptions defines all the init workflow options.
|
||||
type InitOptions struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Kubeconfig *rest.Config
|
||||
KarmadaVersion string
|
||||
CRDTarball operatorv1alpha1.CRDTarball
|
||||
KarmadaDataDir string
|
||||
Karmada *operatorv1alpha1.Karmada
|
||||
Name string
|
||||
Namespace string
|
||||
Kubeconfig *rest.Config
|
||||
KarmadaVersion string
|
||||
CRDTarball operatorv1alpha1.CRDTarball
|
||||
CustomCertificateConfig operatorv1alpha1.CustomCertificate
|
||||
KarmadaDataDir string
|
||||
Karmada *operatorv1alpha1.Karmada
|
||||
}
|
||||
|
||||
// Validate is used to validate the initOptions before creating initJob.
|
||||
|
@ -75,19 +76,20 @@ var _ tasks.InitData = &initData{}
|
|||
type initData struct {
|
||||
sync.Once
|
||||
certs.CertStore
|
||||
name string
|
||||
namespace string
|
||||
karmadaVersion *utilversion.Version
|
||||
controlplaneConfig *rest.Config
|
||||
controlplaneAddress string
|
||||
remoteClient clientset.Interface
|
||||
karmadaClient clientset.Interface
|
||||
dnsDomain string
|
||||
CRDTarball operatorv1alpha1.CRDTarball
|
||||
karmadaDataDir string
|
||||
privateRegistry string
|
||||
featureGates map[string]bool
|
||||
components *operatorv1alpha1.KarmadaComponents
|
||||
name string
|
||||
namespace string
|
||||
karmadaVersion *utilversion.Version
|
||||
controlplaneConfig *rest.Config
|
||||
controlplaneAddress string
|
||||
remoteClient clientset.Interface
|
||||
karmadaClient clientset.Interface
|
||||
dnsDomain string
|
||||
CRDTarball operatorv1alpha1.CRDTarball
|
||||
CustomCertificateConfig operatorv1alpha1.CustomCertificate
|
||||
karmadaDataDir string
|
||||
privateRegistry string
|
||||
featureGates map[string]bool
|
||||
components *operatorv1alpha1.KarmadaComponents
|
||||
}
|
||||
|
||||
// NewInitJob initializes a job with list of init sub-task. and build
|
||||
|
@ -165,18 +167,19 @@ func newRunData(opt *InitOptions) (*initData, error) {
|
|||
}
|
||||
|
||||
return &initData{
|
||||
name: opt.Name,
|
||||
namespace: opt.Namespace,
|
||||
karmadaVersion: version,
|
||||
controlplaneAddress: address,
|
||||
remoteClient: remoteClient,
|
||||
CRDTarball: opt.CRDTarball,
|
||||
karmadaDataDir: opt.KarmadaDataDir,
|
||||
privateRegistry: privateRegistry,
|
||||
components: opt.Karmada.Spec.Components,
|
||||
featureGates: opt.Karmada.Spec.FeatureGates,
|
||||
dnsDomain: *opt.Karmada.Spec.HostCluster.Networking.DNSDomain,
|
||||
CertStore: certs.NewCertStore(),
|
||||
name: opt.Name,
|
||||
namespace: opt.Namespace,
|
||||
karmadaVersion: version,
|
||||
controlplaneAddress: address,
|
||||
remoteClient: remoteClient,
|
||||
CRDTarball: opt.CRDTarball,
|
||||
CustomCertificateConfig: opt.CustomCertificateConfig,
|
||||
karmadaDataDir: opt.KarmadaDataDir,
|
||||
privateRegistry: privateRegistry,
|
||||
components: opt.Karmada.Spec.Components,
|
||||
featureGates: opt.Karmada.Spec.FeatureGates,
|
||||
dnsDomain: *opt.Karmada.Spec.HostCluster.Networking.DNSDomain,
|
||||
CertStore: certs.NewCertStore(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -226,6 +229,10 @@ func (data *initData) CrdTarball() operatorv1alpha1.CRDTarball {
|
|||
return data.CRDTarball
|
||||
}
|
||||
|
||||
func (data *initData) CustomCertificate() operatorv1alpha1.CustomCertificate {
|
||||
return data.CustomCertificateConfig
|
||||
}
|
||||
|
||||
func (data *initData) KarmadaVersion() string {
|
||||
return data.karmadaVersion.String()
|
||||
}
|
||||
|
@ -278,6 +285,9 @@ func NewInitOptWithKarmada(karmada *operatorv1alpha1.Karmada) InitOpt {
|
|||
if karmada.Spec.CRDTarball != nil {
|
||||
o.CRDTarball = *karmada.Spec.CRDTarball
|
||||
}
|
||||
if karmada.Spec.CustomCertificate != nil {
|
||||
o.CustomCertificateConfig = *karmada.Spec.CustomCertificate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,12 @@ import (
|
|||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
operatorv1alpha1 "github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1"
|
||||
"github.com/karmada-io/karmada/operator/pkg/certs"
|
||||
"github.com/karmada-io/karmada/operator/pkg/constants"
|
||||
"github.com/karmada-io/karmada/operator/pkg/util"
|
||||
"github.com/karmada-io/karmada/operator/pkg/workflow"
|
||||
)
|
||||
|
@ -101,6 +104,26 @@ func runCATask(kc *certs.CertConfig) func(d workflow.RunData) error {
|
|||
if kc.CAName != "" {
|
||||
return fmt.Errorf("this function should only be used for CAs, but cert %s has CA %s", kc.Name, kc.CAName)
|
||||
}
|
||||
|
||||
customCertConfig := data.CustomCertificate()
|
||||
if kc.Name == constants.CaCertAndKeyName && customCertConfig.APIServerCACert != nil {
|
||||
secretRef := customCertConfig.APIServerCACert
|
||||
klog.V(4).InfoS("[certs] Loading custom CA certificate", "secret", secretRef.Name, "namespace", secretRef.Namespace)
|
||||
|
||||
certData, keyData, err := loadCACertFromSecret(data.RemoteClient(), secretRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load custom CA certificate: %w", err)
|
||||
}
|
||||
|
||||
klog.V(2).InfoS("[certs] Successfully loaded custom CA certificate", "secret", secretRef.Name)
|
||||
|
||||
customKarmadaCert := certs.NewKarmadaCert(kc.Name, kc.CAName, certData, keyData)
|
||||
|
||||
data.AddCert(customKarmadaCert)
|
||||
klog.V(2).InfoS("[certs] Successfully added custom CA certificate to cert store", "certName", kc.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
klog.V(4).InfoS("[certs] Creating a new certificate authority", "certName", kc.Name)
|
||||
|
||||
cert, err := certs.NewCertificateAuthority(kc)
|
||||
|
@ -115,6 +138,22 @@ func runCATask(kc *certs.CertConfig) func(d workflow.RunData) error {
|
|||
}
|
||||
}
|
||||
|
||||
func loadCACertFromSecret(client clientset.Interface, ref *operatorv1alpha1.LocalSecretReference) ([]byte, []byte, error) {
|
||||
secret, err := client.CoreV1().Secrets(ref.Namespace).Get(context.TODO(), ref.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to retrieve secret %s/%s: %w", ref.Namespace, ref.Name, err)
|
||||
}
|
||||
|
||||
certData := secret.Data[constants.TLSCertDataKey]
|
||||
keyData := secret.Data[constants.TLSPrivateKeyDataKey]
|
||||
|
||||
if len(certData) == 0 || len(keyData) == 0 {
|
||||
return nil, nil, fmt.Errorf("secret %s/%s is missing required keys: %s and %s", ref.Namespace, ref.Name, constants.TLSCertDataKey, constants.TLSPrivateKeyDataKey)
|
||||
}
|
||||
|
||||
return certData, keyData, nil
|
||||
}
|
||||
|
||||
func runCertTask(cc, caCert *certs.CertConfig) func(d workflow.RunData) error {
|
||||
return func(r workflow.RunData) error {
|
||||
data, ok := r.(InitData)
|
||||
|
|
|
@ -36,6 +36,7 @@ type InitData interface {
|
|||
KarmadaClient() clientset.Interface
|
||||
DataDir() string
|
||||
CrdTarball() operatorv1alpha1.CRDTarball
|
||||
CustomCertificate() operatorv1alpha1.CustomCertificate
|
||||
KarmadaVersion() string
|
||||
Components() *operatorv1alpha1.KarmadaComponents
|
||||
FeatureGates() map[string]bool
|
||||
|
|
|
@ -46,18 +46,19 @@ func (m *MyTestData) Get() string {
|
|||
|
||||
// TestInitData contains the configuration and state required to initialize Karmada components.
|
||||
type TestInitData struct {
|
||||
Name string
|
||||
Namespace string
|
||||
ControlplaneConfigREST *rest.Config
|
||||
DataDirectory string
|
||||
CrdTarballArchive operatorv1alpha1.CRDTarball
|
||||
KarmadaVersionRelease string
|
||||
ComponentsUnits *operatorv1alpha1.KarmadaComponents
|
||||
FeatureGatesOptions map[string]bool
|
||||
RemoteClientConnector clientset.Interface
|
||||
KarmadaClientConnector clientset.Interface
|
||||
ControlplaneAddr string
|
||||
Certs []*certs.KarmadaCert
|
||||
Name string
|
||||
Namespace string
|
||||
ControlplaneConfigREST *rest.Config
|
||||
DataDirectory string
|
||||
CrdTarballArchive operatorv1alpha1.CRDTarball
|
||||
CustomCertificateConfig operatorv1alpha1.CustomCertificate
|
||||
KarmadaVersionRelease string
|
||||
ComponentsUnits *operatorv1alpha1.KarmadaComponents
|
||||
FeatureGatesOptions map[string]bool
|
||||
RemoteClientConnector clientset.Interface
|
||||
KarmadaClientConnector clientset.Interface
|
||||
ControlplaneAddr string
|
||||
Certs []*certs.KarmadaCert
|
||||
}
|
||||
|
||||
// Ensure TestInitData implements InitData interface at compile time.
|
||||
|
@ -108,6 +109,11 @@ func (t *TestInitData) CrdTarball() operatorv1alpha1.CRDTarball {
|
|||
return t.CrdTarballArchive
|
||||
}
|
||||
|
||||
// CustomCertificate returns the custom certificate config.
|
||||
func (t *TestInitData) CustomCertificate() operatorv1alpha1.CustomCertificate {
|
||||
return t.CustomCertificateConfig
|
||||
}
|
||||
|
||||
// KarmadaVersion returns the version of Karmada being used.
|
||||
func (t *TestInitData) KarmadaVersion() string {
|
||||
return t.KarmadaVersionRelease
|
||||
|
|
Loading…
Reference in New Issue