From 6f6a58a64a5da3cb33d146f2c7c077cb9869cea0 Mon Sep 17 00:00:00 2001 From: Haseeb Tariq Date: Wed, 3 Jun 2026 11:26:04 -0700 Subject: [PATCH] pki: add PKI configuration types, validation, and CR manifest generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the configurable PKI API surface to InstallConfig behind the ConfigurablePKI feature gate. When the gate is active, the installer generates a config.openshift.io/v1alpha1 PKI custom resource manifest that day-2 operators use for certificate rotation parameters. The default PKI profile uses RSA-4096 until all day-2 operators (CKAO, CKMO, etc.) support ECDSA rotation. When pki is not specified in install-config the PKI CR uses mode: Default. When pki is specified the PKI CR uses mode: Custom with DefaultPKIProfile as the base and user signerCertificates overrides layered on top. No certificate generation changes are included — all certs remain RSA-2048. Non-TechPreview clusters are completely unaffected. Assisted-by: Claude Code (Opus 4.6) --- .../install.openshift.io_installconfigs.yaml | 76 +++++ pkg/asset/manifests/operators.go | 5 +- pkg/asset/manifests/pki.go | 102 +++++++ pkg/asset/manifests/pki_test.go | 164 +++++++++++ pkg/explain/printer_test.go | 6 + pkg/types/defaults/installconfig.go | 1 + pkg/types/installconfig.go | 107 +++++++ pkg/types/pki/conversion.go | 18 ++ pkg/types/pki/defaults.go | 55 ++++ pkg/types/pki/defaults_test.go | 104 +++++++ pkg/types/pki/validation.go | 116 ++++++++ pkg/types/pki/validation_test.go | 263 ++++++++++++++++++ pkg/types/validation/featuregate_test.go | 35 +++ pkg/types/validation/featuregates.go | 5 + pkg/types/validation/installconfig.go | 6 + pkg/types/validation/installconfig_test.go | 49 ++++ pkg/types/zz_generated.deepcopy.go | 89 ++++++ 17 files changed, 1200 insertions(+), 1 deletion(-) create mode 100644 pkg/asset/manifests/pki.go create mode 100644 pkg/asset/manifests/pki_test.go create mode 100644 pkg/types/pki/conversion.go create mode 100644 pkg/types/pki/defaults.go create mode 100644 pkg/types/pki/defaults_test.go create mode 100644 pkg/types/pki/validation.go create mode 100644 pkg/types/pki/validation_test.go diff --git a/data/data/install.openshift.io_installconfigs.yaml b/data/data/install.openshift.io_installconfigs.yaml index 37c2e9c4021..9ffb030ec83 100644 --- a/data/data/install.openshift.io_installconfigs.yaml +++ b/data/data/install.openshift.io_installconfigs.yaml @@ -5084,6 +5084,82 @@ spec: - rhel-9 - rhel-10 type: string + pki: + description: |- + PKI configures cryptographic parameters for installer-generated + signer certificates. When specified, all signer certificates use the + algorithm and parameters from signerCertificates. + Feature gated by ConfigurablePKI. + properties: + signerCertificates: + description: |- + signerCertificates specifies key parameters for all installer-generated + certificate authority (CA) certificates. + When set, all signer certificates use the specified algorithm and parameters. + minProperties: 1 + properties: + key: + description: key specifies the cryptographic parameters for the + certificate's key pair. + properties: + algorithm: + description: |- + algorithm specifies the key generation algorithm. + Valid values are "RSA" and "ECDSA". + enum: + - RSA + - ECDSA + type: string + ecdsa: + description: |- + ecdsa specifies ECDSA key parameters. + Required when algorithm is ECDSA, and forbidden otherwise. + properties: + curve: + description: |- + curve specifies the NIST elliptic curve for ECDSA keys. + Valid values are "P256", "P384", and "P521". + enum: + - P256 + - P384 + - P521 + type: string + required: + - curve + type: object + rsa: + description: |- + rsa specifies RSA key parameters. + Required when algorithm is RSA, and forbidden otherwise. + properties: + keySize: + description: |- + keySize specifies the size of RSA keys in bits. + Valid values are multiples of 1024 from 2048 to 8192. + format: int32 + maximum: 8192 + minimum: 2048 + multipleOf: 1024 + type: integer + required: + - keySize + type: object + required: + - algorithm + type: object + x-kubernetes-validations: + - message: rsa is required when algorithm is RSA, and forbidden + otherwise + rule: 'has(self.algorithm) && self.algorithm == ''RSA'' ? has(self.rsa) + : !has(self.rsa)' + - message: ecdsa is required when algorithm is ECDSA, and forbidden + otherwise + rule: 'has(self.algorithm) && self.algorithm == ''ECDSA'' ? has(self.ecdsa) + : !has(self.ecdsa)' + type: object + required: + - signerCertificates + type: object platform: description: |- Platform is the configuration for the specific platform upon which to diff --git a/pkg/asset/manifests/operators.go b/pkg/asset/manifests/operators.go index d57be7cab8a..0685d7d5693 100644 --- a/pkg/asset/manifests/operators.go +++ b/pkg/asset/manifests/operators.go @@ -90,6 +90,7 @@ func (m *Manifests) Dependencies() []asset.Asset { &bootkube.InternalReleaseImageTLSSecret{}, &bootkube.InternalReleaseImageRegistryAuthSecret{}, &BMCVerifyCAConfigMap{}, + &PKIConfiguration{}, } } @@ -107,8 +108,9 @@ func (m *Manifests) Generate(_ context.Context, dependencies asset.Parents) erro imageDigestMirrorSet := &ImageDigestMirrorSet{} mcoCfgTemplate := &manifests.MCO{} bmcVerifyCAConfigMap := &BMCVerifyCAConfigMap{} + pkiConfig := &PKIConfiguration{} - dependencies.Get(installConfig, ingress, dns, network, infra, proxy, scheduler, imageContentSourcePolicy, imageDigestMirrorSet, clusterCSIDriverConfig, mcoCfgTemplate, bmcVerifyCAConfigMap) + dependencies.Get(installConfig, ingress, dns, network, infra, proxy, scheduler, imageContentSourcePolicy, imageDigestMirrorSet, clusterCSIDriverConfig, mcoCfgTemplate, bmcVerifyCAConfigMap, pkiConfig) redactedConfig, err := redactedInstallConfig(*installConfig.Config) if err != nil { @@ -147,6 +149,7 @@ func (m *Manifests) Generate(_ context.Context, dependencies asset.Parents) erro m.FileList = append(m.FileList, clusterCSIDriverConfig.Files()...) m.FileList = append(m.FileList, imageDigestMirrorSet.Files()...) m.FileList = append(m.FileList, bmcVerifyCAConfigMap.Files()...) + m.FileList = append(m.FileList, pkiConfig.Files()...) asset.SortFiles(m.FileList) diff --git a/pkg/asset/manifests/pki.go b/pkg/asset/manifests/pki.go new file mode 100644 index 00000000000..b35c8a64262 --- /dev/null +++ b/pkg/asset/manifests/pki.go @@ -0,0 +1,102 @@ +package manifests + +import ( + "context" + "path" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" + + configv1alpha1 "github.com/openshift/api/config/v1alpha1" + features "github.com/openshift/api/features" + "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/installconfig" + pkidefaults "github.com/openshift/installer/pkg/types/pki" +) + +var pkiCfgFilename = path.Join(manifestDir, "cluster-pki-02-config.yaml") + +// PKIConfiguration generates the PKI custom resource manifest. +type PKIConfiguration struct { + FileList []*asset.File +} + +var _ asset.WritableAsset = (*PKIConfiguration)(nil) + +// Name returns a human friendly name for the asset. +func (*PKIConfiguration) Name() string { + return "PKI Config" +} + +// Dependencies returns all of the dependencies directly needed to generate +// the asset. +func (*PKIConfiguration) Dependencies() []asset.Asset { + return []asset.Asset{ + &installconfig.InstallConfig{}, + } +} + +// Generate generates the PKI custom resource manifest. +// The manifest is only generated when the ConfigurablePKI feature gate is enabled. +func (p *PKIConfiguration) Generate(_ context.Context, dependencies asset.Parents) error { + installConfig := &installconfig.InstallConfig{} + dependencies.Get(installConfig) + + if !installConfig.Config.Enabled(features.FeatureGateConfigurablePKI) { + return nil + } + + certMgmt := configv1alpha1.PKICertificateManagement{ + Mode: configv1alpha1.PKICertificateManagementModeDefault, + } + + if installConfig.Config.PKI != nil { + profile := pkidefaults.DefaultPKIProfile() + profile.SignerCertificates = pkidefaults.CertificateConfigToUpstream(installConfig.Config.PKI.SignerCertificates) + + certMgmt = configv1alpha1.PKICertificateManagement{ + Mode: configv1alpha1.PKICertificateManagementModeCustom, + Custom: configv1alpha1.CustomPKIPolicy{ + PKIProfile: profile, + }, + } + } + + config := &configv1alpha1.PKI{ + TypeMeta: metav1.TypeMeta{ + APIVersion: configv1alpha1.SchemeGroupVersion.String(), + Kind: "PKI", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1alpha1.PKISpec{ + CertificateManagement: certMgmt, + }, + } + + configData, err := yaml.Marshal(config) + if err != nil { + return errors.Wrapf(err, "failed to marshal PKI config") + } + + p.FileList = []*asset.File{ + { + Filename: pkiCfgFilename, + Data: configData, + }, + } + + return nil +} + +// Files returns the files generated by the asset. +func (p *PKIConfiguration) Files() []*asset.File { + return p.FileList +} + +// Load returns false since this asset is not written to disk by the installer. +func (p *PKIConfiguration) Load(f asset.FileFetcher) (bool, error) { + return false, nil +} diff --git a/pkg/asset/manifests/pki_test.go b/pkg/asset/manifests/pki_test.go new file mode 100644 index 00000000000..c807aa496dd --- /dev/null +++ b/pkg/asset/manifests/pki_test.go @@ -0,0 +1,164 @@ +package manifests + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/yaml" + + configv1 "github.com/openshift/api/config/v1" + configv1alpha1 "github.com/openshift/api/config/v1alpha1" + "github.com/openshift/installer/pkg/asset" + "github.com/openshift/installer/pkg/asset/installconfig" + "github.com/openshift/installer/pkg/types" +) + +func TestPKIConfigurationGenerate(t *testing.T) { + cases := []struct { + name string + installConfig *types.InstallConfig + expectEmpty bool + expectMode configv1alpha1.PKICertificateManagementMode + expectSignerAlgo configv1alpha1.KeyAlgorithm + expectSignerRSA int32 + expectSignerCurve configv1alpha1.ECDSACurve + expectDefaultsAlgo configv1alpha1.KeyAlgorithm + expectDefaultsRSA int32 + expectDefaultsCurve configv1alpha1.ECDSACurve + }{ + { + name: "feature gate disabled - no manifest generated", + installConfig: &types.InstallConfig{ + FeatureSet: configv1.Default, + }, + expectEmpty: true, + }, + { + name: "feature gate enabled, pki nil - mode Default", + installConfig: &types.InstallConfig{ + FeatureSet: configv1.TechPreviewNoUpgrade, + }, + expectEmpty: false, + expectMode: configv1alpha1.PKICertificateManagementModeDefault, + }, + { + name: "feature gate enabled, pki RSA-4096", + installConfig: &types.InstallConfig{ + FeatureSet: configv1.TechPreviewNoUpgrade, + PKI: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 4096}, + }, + }, + }, + }, + expectEmpty: false, + expectMode: configv1alpha1.PKICertificateManagementModeCustom, + expectSignerAlgo: configv1alpha1.KeyAlgorithmRSA, + expectSignerRSA: 4096, + expectDefaultsAlgo: configv1alpha1.KeyAlgorithmRSA, + expectDefaultsRSA: 4096, + }, + { + name: "feature gate enabled, pki ECDSA P-384", + installConfig: &types.InstallConfig{ + FeatureSet: configv1.TechPreviewNoUpgrade, + PKI: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP384}, + }, + }, + }, + }, + expectEmpty: false, + expectMode: configv1alpha1.PKICertificateManagementModeCustom, + expectSignerAlgo: configv1alpha1.KeyAlgorithmECDSA, + expectSignerCurve: configv1alpha1.ECDSACurveP384, + expectDefaultsAlgo: configv1alpha1.KeyAlgorithmRSA, + expectDefaultsRSA: 4096, + }, + { + name: "feature gate enabled, pki RSA-2048 explicit", + installConfig: &types.InstallConfig{ + FeatureSet: configv1.TechPreviewNoUpgrade, + PKI: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 2048}, + }, + }, + }, + }, + expectEmpty: false, + expectMode: configv1alpha1.PKICertificateManagementModeCustom, + expectSignerAlgo: configv1alpha1.KeyAlgorithmRSA, + expectSignerRSA: 2048, + expectDefaultsAlgo: configv1alpha1.KeyAlgorithmRSA, + expectDefaultsRSA: 4096, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + parents := asset.Parents{} + parents.Add(installconfig.MakeAsset(tc.installConfig)) + + pkiAsset := &PKIConfiguration{} + err := pkiAsset.Generate(context.Background(), parents) + if !assert.NoError(t, err) { + return + } + + if tc.expectEmpty { + assert.Empty(t, pkiAsset.Files()) + return + } + + if !assert.Len(t, pkiAsset.Files(), 1) { + return + } + assert.Equal(t, "manifests/cluster-pki-02-config.yaml", pkiAsset.Files()[0].Filename) + + // Unmarshal and verify the CR structure + var pkiCR configv1alpha1.PKI + err = yaml.Unmarshal(pkiAsset.Files()[0].Data, &pkiCR) + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, "config.openshift.io/v1alpha1", pkiCR.APIVersion) + assert.Equal(t, "PKI", pkiCR.Kind) + assert.Equal(t, "cluster", pkiCR.Name) + assert.Equal(t, tc.expectMode, pkiCR.Spec.CertificateManagement.Mode) + + if tc.expectMode != configv1alpha1.PKICertificateManagementModeCustom { + return + } + + profile := pkiCR.Spec.CertificateManagement.Custom.PKIProfile + + // Verify defaults + assert.Equal(t, tc.expectDefaultsAlgo, profile.Defaults.Key.Algorithm) + if tc.expectDefaultsAlgo == configv1alpha1.KeyAlgorithmECDSA { + assert.Equal(t, tc.expectDefaultsCurve, profile.Defaults.Key.ECDSA.Curve) + } else { + assert.Equal(t, tc.expectDefaultsRSA, profile.Defaults.Key.RSA.KeySize) + } + + // Verify signerCertificates + assert.Equal(t, tc.expectSignerAlgo, profile.SignerCertificates.Key.Algorithm) + if tc.expectSignerAlgo == configv1alpha1.KeyAlgorithmRSA { + assert.Equal(t, tc.expectSignerRSA, profile.SignerCertificates.Key.RSA.KeySize) + } + if tc.expectSignerAlgo == configv1alpha1.KeyAlgorithmECDSA { + assert.Equal(t, tc.expectSignerCurve, profile.SignerCertificates.Key.ECDSA.Curve) + } + }) + } +} diff --git a/pkg/explain/printer_test.go b/pkg/explain/printer_test.go index 6333877a5ed..cc07374bf5c 100644 --- a/pkg/explain/printer_test.go +++ b/pkg/explain/printer_test.go @@ -139,6 +139,12 @@ the cluster. Valid Values: "rhel-9","rhel-10" OSImageStream is the global OS Image Stream to be used for all machines in the cluster. + pki + PKI configures cryptographic parameters for installer-generated +signer certificates. When specified, all signer certificates use the +algorithm and parameters from signerCertificates. +Feature gated by ConfigurablePKI. + platform -required- Platform is the configuration for the specific platform upon which to perform the installation. diff --git a/pkg/types/defaults/installconfig.go b/pkg/types/defaults/installconfig.go index 6e88ba5aba7..afef381e64a 100644 --- a/pkg/types/defaults/installconfig.go +++ b/pkg/types/defaults/installconfig.go @@ -142,4 +142,5 @@ func SetInstallConfigDefaults(c *types.InstallConfig) { if c.AdditionalTrustBundlePolicy == "" { c.AdditionalTrustBundlePolicy = types.PolicyProxyOnly } + } diff --git a/pkg/types/installconfig.go b/pkg/types/installconfig.go index fcfda23cc9e..9666d0ca172 100644 --- a/pkg/types/installconfig.go +++ b/pkg/types/installconfig.go @@ -231,8 +231,115 @@ type InstallConfig struct { // OSImageStream is the global OS Image Stream to be used for all machines in the cluster. // +optional OSImageStream OSImageStream `json:"osImageStream,omitempty"` + + // PKI configures cryptographic parameters for installer-generated + // signer certificates. When specified, all signer certificates use the + // algorithm and parameters from signerCertificates. + // Feature gated by ConfigurablePKI. + // +openshift:enable:FeatureGate=ConfigurablePKI + // +optional + PKI *PKIConfig `json:"pki,omitempty"` +} + +// PKIConfig configures cryptographic parameters for installer-generated +// signer certificates. When pki is present in the install config, +// signerCertificates must be fully specified with algorithm and key parameters. +type PKIConfig struct { + // signerCertificates specifies key parameters for all installer-generated + // certificate authority (CA) certificates. + // When set, all signer certificates use the specified algorithm and parameters. + // +required + SignerCertificates CertificateConfig `json:"signerCertificates"` } +// The types below (CertificateConfig, KeyConfig, RSAKeyConfig, ECDSAKeyConfig, +// KeyAlgorithm, ECDSACurve) mirror configv1alpha1 types from openshift/api. +// We maintain local copies so that new fields added upstream do not silently +// appear in the install-config YAML API without explicit opt-in and validation +// by the installer. Conversion to the upstream type happens at the manifest +// boundary (see pkg/types/pki/conversion.go). + +// CertificateConfig specifies configuration parameters for certificates. +// +kubebuilder:validation:MinProperties=1 +type CertificateConfig struct { + // key specifies the cryptographic parameters for the certificate's key pair. + // +optional + Key KeyConfig `json:"key,omitzero"` +} + +// KeyConfig specifies cryptographic parameters for key generation. +// +// +kubebuilder:validation:XValidation:rule="has(self.algorithm) && self.algorithm == 'RSA' ? has(self.rsa) : !has(self.rsa)",message="rsa is required when algorithm is RSA, and forbidden otherwise" +// +kubebuilder:validation:XValidation:rule="has(self.algorithm) && self.algorithm == 'ECDSA' ? has(self.ecdsa) : !has(self.ecdsa)",message="ecdsa is required when algorithm is ECDSA, and forbidden otherwise" +// +union +// +//nolint:godot +type KeyConfig struct { + // algorithm specifies the key generation algorithm. + // Valid values are "RSA" and "ECDSA". + // +required + // +unionDiscriminator + Algorithm KeyAlgorithm `json:"algorithm,omitempty"` + + // rsa specifies RSA key parameters. + // Required when algorithm is RSA, and forbidden otherwise. + // +optional + // +unionMember + RSA RSAKeyConfig `json:"rsa,omitzero"` + + // ecdsa specifies ECDSA key parameters. + // Required when algorithm is ECDSA, and forbidden otherwise. + // +optional + // +unionMember + ECDSA ECDSAKeyConfig `json:"ecdsa,omitzero"` +} + +// RSAKeyConfig specifies parameters for RSA key generation. +type RSAKeyConfig struct { + // keySize specifies the size of RSA keys in bits. + // Valid values are multiples of 1024 from 2048 to 8192. + // +required + // +kubebuilder:validation:Minimum=2048 + // +kubebuilder:validation:Maximum=8192 + // +kubebuilder:validation:MultipleOf=1024 + KeySize int32 `json:"keySize,omitempty"` +} + +// ECDSAKeyConfig specifies parameters for ECDSA key generation. +type ECDSAKeyConfig struct { + // curve specifies the NIST elliptic curve for ECDSA keys. + // Valid values are "P256", "P384", and "P521". + // +required + Curve ECDSACurve `json:"curve,omitempty"` +} + +// KeyAlgorithm specifies the cryptographic algorithm used for key generation. +// +kubebuilder:validation:Enum=RSA;ECDSA +type KeyAlgorithm string + +const ( + // KeyAlgorithmRSA specifies the RSA algorithm for key generation. + KeyAlgorithmRSA KeyAlgorithm = "RSA" + + // KeyAlgorithmECDSA specifies the ECDSA algorithm for key generation. + KeyAlgorithmECDSA KeyAlgorithm = "ECDSA" +) + +// ECDSACurve specifies the elliptic curve used for ECDSA key generation. +// +kubebuilder:validation:Enum=P256;P384;P521 +type ECDSACurve string + +const ( + // ECDSACurveP256 specifies the NIST P-256 curve. + ECDSACurveP256 ECDSACurve = "P256" + + // ECDSACurveP384 specifies the NIST P-384 curve. + ECDSACurveP384 ECDSACurve = "P384" + + // ECDSACurveP521 specifies the NIST P-521 curve. + ECDSACurveP521 ECDSACurve = "P521" +) + // ClusterDomain returns the DNS domain that all records for a cluster must belong to. func (c *InstallConfig) ClusterDomain() string { return fmt.Sprintf("%s.%s", c.ObjectMeta.Name, strings.TrimSuffix(c.BaseDomain, ".")) diff --git a/pkg/types/pki/conversion.go b/pkg/types/pki/conversion.go new file mode 100644 index 00000000000..8a262a433c6 --- /dev/null +++ b/pkg/types/pki/conversion.go @@ -0,0 +1,18 @@ +package pki + +import ( + configv1alpha1 "github.com/openshift/api/config/v1alpha1" + "github.com/openshift/installer/pkg/types" +) + +// CertificateConfigToUpstream converts the installer-local CertificateConfig +// to the upstream configv1alpha1.CertificateConfig for use in the PKI CR manifest. +func CertificateConfigToUpstream(local types.CertificateConfig) configv1alpha1.CertificateConfig { + return configv1alpha1.CertificateConfig{ + Key: configv1alpha1.KeyConfig{ + Algorithm: configv1alpha1.KeyAlgorithm(local.Key.Algorithm), + RSA: configv1alpha1.RSAKeyConfig{KeySize: local.Key.RSA.KeySize}, + ECDSA: configv1alpha1.ECDSAKeyConfig{Curve: configv1alpha1.ECDSACurve(local.Key.ECDSA.Curve)}, + }, + } +} diff --git a/pkg/types/pki/defaults.go b/pkg/types/pki/defaults.go new file mode 100644 index 00000000000..a8dd15aff44 --- /dev/null +++ b/pkg/types/pki/defaults.go @@ -0,0 +1,55 @@ +package pki + +import ( + configv1alpha1 "github.com/openshift/api/config/v1alpha1" + features "github.com/openshift/api/features" + "github.com/openshift/installer/pkg/types" +) + +// DefaultPKIProfile returns the default PKI profile for OpenShift clusters. +// Currently uses RSA-4096 until all day-2 operators (CKAO, CKMO, etc.) support +// ECDSA certificate rotation. Once operator support lands, switch to ECDSA P-384 +// signers and ECDSA P-256 defaults to match the upstream library-go profile: +// https://github.com/openshift/library-go/blob/12d8376369b7c5b76f688d01089882ca28e351c3/pkg/pki/profile.go#L11-L26 +func DefaultPKIProfile() configv1alpha1.PKIProfile { + return configv1alpha1.PKIProfile{ + Defaults: configv1alpha1.DefaultCertificateConfig{ + Key: configv1alpha1.KeyConfig{ + Algorithm: configv1alpha1.KeyAlgorithmRSA, + RSA: configv1alpha1.RSAKeyConfig{KeySize: 4096}, + }, + }, + SignerCertificates: configv1alpha1.CertificateConfig{ + Key: configv1alpha1.KeyConfig{ + Algorithm: configv1alpha1.KeyAlgorithmRSA, + RSA: configv1alpha1.RSAKeyConfig{KeySize: 4096}, + }, + }, + } +} + +// EffectiveSignerPKIConfig returns the effective PKI config for signer certificate generation. +// - If user specified pki in install-config, returns that config unchanged. +// - If ConfigurablePKI feature gate is enabled and pki is nil, returns a +// PKIConfig derived from DefaultPKIProfile().SignerCertificates. +// - If feature gate is disabled, returns nil (RSA-2048 legacy path). +func EffectiveSignerPKIConfig(ic *types.InstallConfig) *types.PKIConfig { + if ic.PKI != nil { + return ic.PKI + } + + if ic.Enabled(features.FeatureGateConfigurablePKI) { + profile := DefaultPKIProfile() + return &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithm(profile.SignerCertificates.Key.Algorithm), + RSA: types.RSAKeyConfig{KeySize: profile.SignerCertificates.Key.RSA.KeySize}, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurve(profile.SignerCertificates.Key.ECDSA.Curve)}, + }, + }, + } + } + + return nil +} diff --git a/pkg/types/pki/defaults_test.go b/pkg/types/pki/defaults_test.go new file mode 100644 index 00000000000..745d84c5b4b --- /dev/null +++ b/pkg/types/pki/defaults_test.go @@ -0,0 +1,104 @@ +package pki + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + configv1 "github.com/openshift/api/config/v1" + configv1alpha1 "github.com/openshift/api/config/v1alpha1" + "github.com/openshift/installer/pkg/types" +) + +func TestDefaultPKIProfile(t *testing.T) { + profile := DefaultPKIProfile() + + assert.Equal(t, configv1alpha1.KeyAlgorithmRSA, profile.Defaults.Key.Algorithm) + assert.Equal(t, int32(4096), profile.Defaults.Key.RSA.KeySize) + assert.Equal(t, configv1alpha1.KeyAlgorithmRSA, profile.SignerCertificates.Key.Algorithm) + assert.Equal(t, int32(4096), profile.SignerCertificates.Key.RSA.KeySize) +} + +func TestEffectiveSignerPKIConfig(t *testing.T) { + cases := []struct { + name string + ic *types.InstallConfig + expectNil bool + expectAlgo types.KeyAlgorithm + expectSize int32 + expectCurve types.ECDSACurve + }{ + { + name: "feature gate off, pki nil", + ic: &types.InstallConfig{ + FeatureSet: configv1.Default, + }, + expectNil: true, + }, + { + name: "feature gate on, pki nil - returns RSA-4096 from DefaultPKIProfile", + ic: &types.InstallConfig{ + FeatureSet: configv1.TechPreviewNoUpgrade, + }, + expectNil: false, + expectAlgo: types.KeyAlgorithmRSA, + expectSize: 4096, + }, + { + name: "feature gate on, pki specified - returns user config", + ic: &types.InstallConfig{ + FeatureSet: configv1.TechPreviewNoUpgrade, + PKI: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP384}, + }, + }, + }, + }, + expectNil: false, + }, + { + name: "feature gate off, pki specified - returns user config", + ic: &types.InstallConfig{ + FeatureSet: configv1.Default, + PKI: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 4096}, + }, + }, + }, + }, + expectNil: false, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + result := EffectiveSignerPKIConfig(tc.ic) + + if tc.expectNil { + assert.Nil(t, result) + return + } + + assert.NotNil(t, result) + + if tc.ic.PKI != nil { + // Should return user's config unchanged + assert.Equal(t, tc.ic.PKI, result) + } else { + // Should return synthesized config from DefaultPKIProfile + assert.Equal(t, tc.expectAlgo, result.SignerCertificates.Key.Algorithm) + if tc.expectAlgo == types.KeyAlgorithmECDSA { + assert.Equal(t, tc.expectCurve, result.SignerCertificates.Key.ECDSA.Curve) + } else { + assert.Equal(t, tc.expectSize, result.SignerCertificates.Key.RSA.KeySize) + } + } + }) + } +} diff --git a/pkg/types/pki/validation.go b/pkg/types/pki/validation.go new file mode 100644 index 00000000000..c27acd5365e --- /dev/null +++ b/pkg/types/pki/validation.go @@ -0,0 +1,116 @@ +package pki + +import ( + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/openshift/installer/pkg/types" +) + +// ValidatePKIConfig validates the PKI configuration. +// When pkiConfig is non-nil, signerCertificates must be fully specified. +// NOTE: All fields are value types (not pointers). Use zero-value checks. +func ValidatePKIConfig(pkiConfig *types.PKIConfig, fldPath *field.Path, fips bool) field.ErrorList { + allErrs := field.ErrorList{} + + if pkiConfig == nil { + return allErrs + } + + // signerCertificates.key must be fully specified when pki is present + if pkiConfig.SignerCertificates.Key.Algorithm == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("signerCertificates", "key"), + "signerCertificates.key is required when pki is specified")) + return allErrs + } + + allErrs = append(allErrs, ValidateKeyConfig(pkiConfig.SignerCertificates.Key, + fldPath.Child("signerCertificates", "key"), fips)...) + + return allErrs +} + +// ValidateKeyConfig validates the KeyConfig structure. +// KeyConfig fields are value types: RSA is RSAKeyConfig, ECDSA is ECDSAKeyConfig. +// Use zero-value checks (KeySize == 0, Curve == "") instead of nil checks. +func ValidateKeyConfig(config types.KeyConfig, fldPath *field.Path, fips bool) field.ErrorList { + allErrs := field.ErrorList{} + + if config.Algorithm == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("algorithm"), + "algorithm must be specified")) + return allErrs + } + + if config.Algorithm != types.KeyAlgorithmRSA && config.Algorithm != types.KeyAlgorithmECDSA { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("algorithm"), + config.Algorithm, []string{string(types.KeyAlgorithmRSA), string(types.KeyAlgorithmECDSA)})) + return allErrs + } + + if config.Algorithm == types.KeyAlgorithmRSA { + if config.RSA.KeySize == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("rsa", "keySize"), + "keySize must be specified when algorithm is RSA")) + } else { + allErrs = append(allErrs, validateRSAKeyConfig(config.RSA, fldPath.Child("rsa"), fips)...) + } + + if config.ECDSA.Curve != "" { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("ecdsa"), + "ecdsa must not be set when algorithm is RSA")) + } + } + + if config.Algorithm == types.KeyAlgorithmECDSA { + if config.ECDSA.Curve == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("ecdsa", "curve"), + "curve must be specified when algorithm is ECDSA")) + } else { + allErrs = append(allErrs, validateECDSAKeyConfig(config.ECDSA, fldPath.Child("ecdsa"), fips)...) + } + + if config.RSA.KeySize != 0 { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("rsa"), + "rsa must not be set when algorithm is ECDSA")) + } + } + + return allErrs +} + +func validateRSAKeyConfig(config types.RSAKeyConfig, fldPath *field.Path, fips bool) field.ErrorList { + allErrs := field.ErrorList{} + + // Validate key size — aligned with API kubebuilder validation: + // multiples of 1024 from 2048 to 8192 + if config.KeySize < 2048 || config.KeySize > 8192 || config.KeySize%1024 != 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("keySize"), config.KeySize, + "must be a multiple of 1024 from 2048 to 8192")) + } + + return allErrs +} + +func validateECDSAKeyConfig(config types.ECDSAKeyConfig, fldPath *field.Path, fips bool) field.ErrorList { + allErrs := field.ErrorList{} + + validCurves := []types.ECDSACurve{ + types.ECDSACurveP256, + types.ECDSACurveP384, + types.ECDSACurveP521, + } + valid := false + for _, curve := range validCurves { + if config.Curve == curve { + valid = true + break + } + } + + if !valid { + allErrs = append(allErrs, field.Invalid(fldPath.Child("curve"), config.Curve, + "must be P256, P384, or P521")) + } + + return allErrs +} diff --git a/pkg/types/pki/validation_test.go b/pkg/types/pki/validation_test.go new file mode 100644 index 00000000000..f687259a570 --- /dev/null +++ b/pkg/types/pki/validation_test.go @@ -0,0 +1,263 @@ +package pki + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/openshift/installer/pkg/types" +) + +func TestValidatePKIConfig(t *testing.T) { + fldPath := field.NewPath("pki") + + cases := []struct { + name string + pkiConfig *types.PKIConfig + fips bool + expectError bool + errorCount int + }{ + { + name: "nil config is valid", + pkiConfig: nil, + expectError: false, + }, + { + name: "valid RSA signer config", + pkiConfig: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 4096}, + }, + }, + }, + expectError: false, + }, + { + name: "valid ECDSA signer config", + pkiConfig: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP384}, + }, + }, + }, + expectError: false, + }, + { + name: "empty PKI config - signerCertificates required", + pkiConfig: &types.PKIConfig{}, + expectError: true, + errorCount: 1, + }, + { + name: "invalid RSA key size", + pkiConfig: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 1024}, + }, + }, + }, + expectError: true, + errorCount: 1, + }, + { + name: "invalid ECDSA curve", + pkiConfig: &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: "P224"}, + }, + }, + }, + expectError: true, + errorCount: 1, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + errs := ValidatePKIConfig(tc.pkiConfig, fldPath, tc.fips) + if tc.expectError { + assert.Len(t, errs, tc.errorCount) + } else { + assert.Empty(t, errs) + } + }) + } +} + +func TestValidateKeyConfig(t *testing.T) { + fldPath := field.NewPath("pki", "signerCertificates", "key") + + cases := []struct { + name string + config types.KeyConfig + fips bool + expectError bool + errorCount int + }{ + // Valid RSA configs + { + name: "valid RSA 2048", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 2048}, + }, + }, + { + name: "valid RSA 3072", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 3072}, + }, + }, + { + name: "valid RSA 4096", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 4096}, + }, + }, + { + name: "valid RSA 8192", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 8192}, + }, + }, + // Valid ECDSA configs + { + name: "valid ECDSA P256", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP256}, + }, + }, + { + name: "valid ECDSA P384", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP384}, + }, + }, + { + name: "valid ECDSA P521", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP521}, + }, + }, + // Invalid: missing algorithm + { + name: "missing algorithm", + config: types.KeyConfig{ + RSA: types.RSAKeyConfig{KeySize: 4096}, + }, + expectError: true, + errorCount: 1, + }, + // Invalid: unsupported algorithm + { + name: "unsupported algorithm", + config: types.KeyConfig{ + Algorithm: "Ed25519", + }, + expectError: true, + errorCount: 1, + }, + // Invalid RSA key sizes + { + name: "RSA key size too small", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 1024}, + }, + expectError: true, + errorCount: 1, + }, + { + name: "RSA key size too large", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 9216}, + }, + expectError: true, + errorCount: 1, + }, + { + name: "RSA key size not multiple of 1024", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 5000}, + }, + expectError: true, + errorCount: 1, + }, + { + name: "RSA missing key size", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + }, + expectError: true, + errorCount: 1, + }, + // Invalid ECDSA curves + { + name: "ECDSA invalid curve", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: "P224"}, + }, + expectError: true, + errorCount: 1, + }, + { + name: "ECDSA missing curve", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + }, + expectError: true, + errorCount: 1, + }, + // Mismatched algorithm/params + { + name: "RSA with ECDSA config", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 4096}, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP256}, + }, + expectError: true, + errorCount: 1, + }, + { + name: "ECDSA with RSA config", + config: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP384}, + RSA: types.RSAKeyConfig{KeySize: 4096}, + }, + expectError: true, + errorCount: 1, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + errs := ValidateKeyConfig(tc.config, fldPath, tc.fips) + if tc.expectError { + assert.Len(t, errs, tc.errorCount, "expected %d errors, got: %v", tc.errorCount, errs) + } else { + assert.Empty(t, errs, "expected no errors, got: %v", errs) + } + }) + } +} diff --git a/pkg/types/validation/featuregate_test.go b/pkg/types/validation/featuregate_test.go index efffd9a3e4d..46d574dbc02 100644 --- a/pkg/types/validation/featuregate_test.go +++ b/pkg/types/validation/featuregate_test.go @@ -225,6 +225,41 @@ func TestFeatureGates(t *testing.T) { return c }(), }, + { + name: "PKI config present without feature gate - error", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.PKI = &types.PKIConfig{} + return c + }(), + expected: `^pki: Forbidden: this field is protected by the ConfigurablePKI feature gate which must be enabled through either the TechPreviewNoUpgrade or CustomNoUpgrade feature set$`, + }, + { + name: "PKI config present with TechPreviewNoUpgrade - passes", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.FeatureSet = v1.TechPreviewNoUpgrade + c.PKI = &types.PKIConfig{} + return c + }(), + }, + { + name: "PKI config present with CustomNoUpgrade and ConfigurablePKI - passes", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.FeatureSet = v1.CustomNoUpgrade + c.FeatureGates = []string{"ConfigurablePKI=true"} + c.PKI = &types.PKIConfig{} + return c + }(), + }, + { + name: "PKI nil without feature gate - no error", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + return c + }(), + }, { name: "OKD featureset requires SCOS-compiled installer", installConfig: func() *types.InstallConfig { diff --git a/pkg/types/validation/featuregates.go b/pkg/types/validation/featuregates.go index a46b5bbff50..9ff53a1ff64 100644 --- a/pkg/types/validation/featuregates.go +++ b/pkg/types/validation/featuregates.go @@ -51,5 +51,10 @@ func validateMachinePoolFeatureGates(c *types.InstallConfig) []featuregates.Gate Condition: len(c.Compute) > 0 && c.Compute[0].Management == types.ClusterAPI, Field: field.NewPath("compute", "management"), }, + { + FeatureGateName: features.FeatureGateConfigurablePKI, + Condition: c.PKI != nil, + Field: field.NewPath("pki"), + }, } } diff --git a/pkg/types/validation/installconfig.go b/pkg/types/validation/installconfig.go index d6c088ee596..eb2dc6195f2 100644 --- a/pkg/types/validation/installconfig.go +++ b/pkg/types/validation/installconfig.go @@ -22,6 +22,7 @@ import ( utilsnet "k8s.io/utils/net" configv1 "github.com/openshift/api/config/v1" + features "github.com/openshift/api/features" operv1 "github.com/openshift/api/operator/v1" "github.com/openshift/installer/pkg/hostcrypt" "github.com/openshift/installer/pkg/ipnet" @@ -48,6 +49,7 @@ import ( openstackvalidation "github.com/openshift/installer/pkg/types/openstack/validation" "github.com/openshift/installer/pkg/types/ovirt" ovirtvalidation "github.com/openshift/installer/pkg/types/ovirt/validation" + pkivalidation "github.com/openshift/installer/pkg/types/pki" "github.com/openshift/installer/pkg/types/powervc" powervcvalidation "github.com/openshift/installer/pkg/types/powervc/validation" "github.com/openshift/installer/pkg/types/powervs" @@ -284,6 +286,10 @@ func ValidateInstallConfig(c *types.InstallConfig, usingAgentMethod bool) field. } } + if c.PKI != nil && c.Enabled(features.FeatureGateConfigurablePKI) { + allErrs = append(allErrs, pkivalidation.ValidatePKIConfig(c.PKI, field.NewPath("pki"), c.FIPS)...) + } + allErrs = append(allErrs, ValidateFeatureSet(c)...) allErrs = append(allErrs, validateOSImageStream(c)...) diff --git a/pkg/types/validation/installconfig_test.go b/pkg/types/validation/installconfig_test.go index ef7e2372c13..a085288d201 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -3101,6 +3101,55 @@ func TestValidateInstallConfig(t *testing.T) { }(), expectedError: `^bootstrapInPlace: Invalid value: "": Single Node OpenShift is not supported on the baremetal platform$`, }, + { + name: "valid PKI with signer certificates", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.FeatureSet = configv1.TechPreviewNoUpgrade + c.PKI = &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmECDSA, + ECDSA: types.ECDSAKeyConfig{Curve: types.ECDSACurveP384}, + }, + }, + } + return c + }(), + }, + { + name: "invalid PKI signer with unsupported algorithm", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.FeatureSet = configv1.TechPreviewNoUpgrade + c.PKI = &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: "EdDSA", + }, + }, + } + return c + }(), + expectedError: `pki\.signerCertificates\.key\.algorithm: Unsupported value: "EdDSA"`, + }, + { + name: "invalid PKI signer with bad RSA key size", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.FeatureSet = configv1.TechPreviewNoUpgrade + c.PKI = &types.PKIConfig{ + SignerCertificates: types.CertificateConfig{ + Key: types.KeyConfig{ + Algorithm: types.KeyAlgorithmRSA, + RSA: types.RSAKeyConfig{KeySize: 1024}, + }, + }, + } + return c + }(), + expectedError: `pki\.signerCertificates\.key\.rsa\.keySize: Invalid value: 1024`, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/types/zz_generated.deepcopy.go b/pkg/types/zz_generated.deepcopy.go index b32e9eded99..199ac92b9e6 100644 --- a/pkg/types/zz_generated.deepcopy.go +++ b/pkg/types/zz_generated.deepcopy.go @@ -60,6 +60,23 @@ func (in *Capabilities) DeepCopy() *Capabilities { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CertificateConfig) DeepCopyInto(out *CertificateConfig) { + *out = *in + out.Key = in.Key + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertificateConfig. +func (in *CertificateConfig) DeepCopy() *CertificateConfig { + if in == nil { + return nil + } + out := new(CertificateConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterMetadata) DeepCopyInto(out *ClusterMetadata) { *out = *in @@ -292,6 +309,22 @@ func (in *DiskUserDefined) DeepCopy() *DiskUserDefined { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ECDSAKeyConfig) DeepCopyInto(out *ECDSAKeyConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ECDSAKeyConfig. +func (in *ECDSAKeyConfig) DeepCopy() *ECDSAKeyConfig { + if in == nil { + return nil + } + out := new(ECDSAKeyConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Fencing) DeepCopyInto(out *Fencing) { *out = *in @@ -448,6 +481,11 @@ func (in *InstallConfig) DeepCopyInto(out *InstallConfig) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.PKI != nil { + in, out := &in.PKI, &out.PKI + *out = new(PKIConfig) + **out = **in + } return } @@ -461,6 +499,24 @@ func (in *InstallConfig) DeepCopy() *InstallConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyConfig) DeepCopyInto(out *KeyConfig) { + *out = *in + out.RSA = in.RSA + out.ECDSA = in.ECDSA + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyConfig. +func (in *KeyConfig) DeepCopy() *KeyConfig { + if in == nil { + return nil + } + out := new(KeyConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineNetworkEntry) DeepCopyInto(out *MachineNetworkEntry) { *out = *in @@ -720,6 +776,23 @@ func (in *OperatorPublishingStrategy) DeepCopy() *OperatorPublishingStrategy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PKIConfig) DeepCopyInto(out *PKIConfig) { + *out = *in + out.SignerCertificates = in.SignerCertificates + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PKIConfig. +func (in *PKIConfig) DeepCopy() *PKIConfig { + if in == nil { + return nil + } + out := new(PKIConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Platform) DeepCopyInto(out *Platform) { *out = *in @@ -816,3 +889,19 @@ func (in *Proxy) DeepCopy() *Proxy { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RSAKeyConfig) DeepCopyInto(out *RSAKeyConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RSAKeyConfig. +func (in *RSAKeyConfig) DeepCopy() *RSAKeyConfig { + if in == nil { + return nil + } + out := new(RSAKeyConfig) + in.DeepCopyInto(out) + return out +}