Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions data/data/install.openshift.io_installconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ func TestArbiterIgnitionCustomizationsGenerate(t *testing.T) {

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rootCAParents := asset.Parents{}
rootCAParents.Add(&tls.SignerKeyParams{})
rootCA := &tls.RootCA{}
err := rootCA.Generate(context.Background(), nil)
err := rootCA.Generate(context.Background(), rootCAParents)
assert.NoError(t, err, "unexpected error generating root CA")

parents := asset.Parents{}
Expand Down
4 changes: 3 additions & 1 deletion pkg/asset/ignition/machine/arbiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ func TestArbiterGenerate(t *testing.T) {
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
rootCAParents := asset.Parents{}
rootCAParents.Add(&tls.SignerKeyParams{})
rootCA := &tls.RootCA{}
err := rootCA.Generate(context.Background(), nil)
err := rootCA.Generate(context.Background(), rootCAParents)
assert.NoError(t, err, "unexpected error generating root CA")

parents := asset.Parents{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ func TestMasterIgnitionCustomizationsGenerate(t *testing.T) {
},
})

rootCAParents := asset.Parents{}
rootCAParents.Add(&tls.SignerKeyParams{})
rootCA := &tls.RootCA{}
err := rootCA.Generate(context.Background(), nil)
err := rootCA.Generate(context.Background(), rootCAParents)
assert.NoError(t, err, "unexpected error generating root CA")

parents := asset.Parents{}
Expand Down
4 changes: 3 additions & 1 deletion pkg/asset/ignition/machine/master_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ func TestMasterGenerate(t *testing.T) {
},
})

rootCAParents := asset.Parents{}
rootCAParents.Add(&tls.SignerKeyParams{})
rootCA := &tls.RootCA{}
err := rootCA.Generate(context.Background(), nil)
err := rootCA.Generate(context.Background(), rootCAParents)
assert.NoError(t, err, "unexpected error generating root CA")

parents := asset.Parents{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ func TestWorkerIgnitionCustomizationsGenerate(t *testing.T) {
},
})

rootCAParents := asset.Parents{}
rootCAParents.Add(&tls.SignerKeyParams{})
rootCA := &tls.RootCA{}
err := rootCA.Generate(context.Background(), nil)
err := rootCA.Generate(context.Background(), rootCAParents)
assert.NoError(t, err, "unexpected error generating root CA")

parents := asset.Parents{}
Expand Down
4 changes: 3 additions & 1 deletion pkg/asset/ignition/machine/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ func TestWorkerGenerate(t *testing.T) {
},
})

rootCAParents := asset.Parents{}
rootCAParents.Add(&tls.SignerKeyParams{})
rootCA := &tls.RootCA{}
err := rootCA.Generate(context.Background(), nil)
err := rootCA.Generate(context.Background(), rootCAParents)
assert.NoError(t, err, "unexpected error generating root CA")

parents := asset.Parents{}
Expand Down
5 changes: 4 additions & 1 deletion pkg/asset/imagebased/configimage/ingressoperatorsigner.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ func (a *IngressOperatorSignerCertKey) Generate(ctx context.Context, dependencie
return err
}

a.KeyRaw = tls.PrivateKeyToPem(key)
a.KeyRaw, err = tls.PrivateKeyToPem(key)
if err != nil {
return fmt.Errorf("failed to encode private key to PEM: %w", err)
}
a.CertRaw = tls.CertToPem(crt)

return nil
Expand Down
5 changes: 4 additions & 1 deletion pkg/asset/manifests/operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func (m *Manifests) Dependencies() []asset.Asset {
&bootkube.InternalReleaseImageTLSSecret{},
&bootkube.InternalReleaseImageRegistryAuthSecret{},
&BMCVerifyCAConfigMap{},
&PKIConfiguration{},
}
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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)

Expand Down
102 changes: 102 additions & 0 deletions pkg/asset/manifests/pki.go
Original file line number Diff line number Diff line change
@@ -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
}
Comment on lines +42 to +48

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset generated state before the feature-gate early return.

On Line 47, Generate exits when the gate is disabled but does not clear p.FileList. Reusing the same asset instance can retain stale PKI manifest output from a previous enabled run.

Suggested fix
 func (p *PKIConfiguration) Generate(_ context.Context, dependencies asset.Parents) error {
+	p.FileList = nil
+
 	installConfig := &installconfig.InstallConfig{}
 	dependencies.Get(installConfig)
 
 	if !installConfig.Config.Enabled(features.FeatureGateConfigurablePKI) {
 		return nil
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (p *PKIConfiguration) Generate(_ context.Context, dependencies asset.Parents) error {
installConfig := &installconfig.InstallConfig{}
dependencies.Get(installConfig)
if !installConfig.Config.Enabled(features.FeatureGateConfigurablePKI) {
return nil
}
func (p *PKIConfiguration) Generate(_ context.Context, dependencies asset.Parents) error {
p.FileList = nil
installConfig := &installconfig.InstallConfig{}
dependencies.Get(installConfig)
if !installConfig.Config.Enabled(features.FeatureGateConfigurablePKI) {
return nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/asset/manifests/pki.go` around lines 42 - 48, The Generate method on
PKIConfiguration exits early when the FeatureGateConfigurablePKI is disabled but
doesn't clear previously generated state; ensure you reset p.FileList (e.g., set
to nil or an empty slice) before the early return in PKIConfiguration.Generate
so stale PKI manifests aren't retained across runs when the feature gate is off.


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
}
Loading