Skip to content
Open
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/gorm v1.24.5 // indirect
k8s.io/apiserver v0.36.0 // indirect
k8s.io/apiserver v0.36.0
k8s.io/cli-runtime v0.36.0 // indirect
k8s.io/cluster-bootstrap v0.35.5 // indirect
k8s.io/component-base v0.36.0 // indirect
Expand Down
59 changes: 43 additions & 16 deletions pkg/asset/imagebased/configimage/ingressoperatorsigner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/tls"
libcrypto "github.com/openshift/library-go/pkg/crypto"
libpki "github.com/openshift/library-go/pkg/pki"
)

// Name returns the human-friendly name of the asset.
Expand All @@ -37,35 +39,49 @@ var _ asset.Asset = (*IngressOperatorSignerCertKey)(nil)

// Dependencies returns the dependency of the the cert/key pair.
func (a *IngressOperatorSignerCertKey) Dependencies() []asset.Asset {
return []asset.Asset{&installconfig.InstallConfig{}}
return []asset.Asset{
&installconfig.InstallConfig{},
&tls.SignerKeyParams{},
}
}

// Generate generates the cert/key pair based on its dependencies.
func (a *IngressOperatorSignerCertKey) Generate(ctx context.Context, dependencies asset.Parents) error {
signerName := fmt.Sprintf("%s@%d", "ingress-operator", time.Now().Unix())

installConfig := &installconfig.InstallConfig{}
dependencies.Get(installConfig)

cfg := &tls.CertCfg{
Subject: pkix.Name{CommonName: signerName},
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
Validity: tls.ValidityOneYear(installConfig) * 2,
IsCA: true,
pkiCfg := &tls.SignerKeyParams{}
dependencies.Get(installConfig, pkiCfg)

if !pkiCfg.ConfigurablePKIEnabled {
cfg := &tls.CertCfg{
Subject: pkix.Name{CommonName: signerName},
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
Validity: tls.ValidityOneYear(installConfig) * 2,
IsCA: true,
}
key, crt, err := generateSelfSignedCertificate(cfg)
if err != nil {
return err
}
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
}

key, crt, err := generateSelfSignedCertificate(cfg)
keyGen, err := resolveSignerKeyGen(pkiCfg, "installer.ingress-operator-signer")
if err != nil {
return err
}

a.KeyRaw, err = tls.PrivateKeyToPem(key)
if err != nil {
return fmt.Errorf("failed to encode private key to PEM: %w", err)
cfg := &tls.CertCfg{
Subject: pkix.Name{CommonName: signerName},
Validity: tls.ValidityOneYear(installConfig) * 2,
IsCA: true,
}
a.CertRaw = tls.CertToPem(crt)

return nil
return a.SelfSignedCertKey.Generate(ctx, cfg, "ingress-operator-signer", keyGen)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

// IngressOperatorCABundle is the asset the generates the ingress-operator-signer-ca-bundle,
Expand Down Expand Up @@ -175,3 +191,14 @@ func generateSubjectKeyID(pub crypto.PublicKey) ([]byte, error) {
hash := sha1.Sum(publicKeyBytes) //nolint: gosec
return hash[:], nil
}

// resolveSignerKeyGen resolves the KeyPairGenerator for a signer certificate
// from the SignerKeyParams's profile.
func resolveSignerKeyGen(pkiCfg *tls.SignerKeyParams, certName string) (libcrypto.KeyPairGenerator, error) {
provider := libpki.NewStaticPKIProfileProvider(&pkiCfg.Profile)
resolved, err := libpki.ResolveCertificateConfig(provider, libpki.CertificateTypeSigner, certName)
if err != nil {
return nil, fmt.Errorf("failed to resolve PKI config for signer certificate %q: %w", certName, err)
}
return resolved.Key, nil
}
41 changes: 11 additions & 30 deletions pkg/asset/manifests/pki.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ import (
"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"
"github.com/openshift/installer/pkg/types"
pkidefaults "github.com/openshift/installer/pkg/types/pki"
"github.com/openshift/installer/pkg/asset/tls"
libpki "github.com/openshift/library-go/pkg/pki"
)

var pkiCfgFilename = path.Join(manifestDir, "cluster-pki-02-config.yaml")
Expand All @@ -34,32 +32,32 @@ func (*PKIConfiguration) Name() string {
// the asset.
func (*PKIConfiguration) Dependencies() []asset.Asset {
return []asset.Asset{
&installconfig.InstallConfig{},
&tls.SignerKeyParams{},
}
}

// 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)
signerKeyParams := &tls.SignerKeyParams{}
dependencies.Get(signerKeyParams)

if !installConfig.Config.Enabled(features.FeatureGateConfigurablePKI) {
if !signerKeyParams.ConfigurablePKIEnabled {
return nil
}

certMgmt := configv1alpha1.PKICertificateManagement{
Mode: configv1alpha1.PKICertificateManagementModeDefault,
}

if installConfig.Config.PKI != nil {
profile := pkidefaults.DefaultPKIProfile()
profile.SignerCertificates = convertToAPICertConfig(installConfig.Config.PKI.SignerCertificates)

// When ConfigurablePKIEnabled is true but the profile equals the default,
// it means no user customization was provided.
defaultProfile := libpki.DefaultPKIProfile()
if signerKeyParams.Profile != defaultProfile {
certMgmt = configv1alpha1.PKICertificateManagement{
Mode: configv1alpha1.PKICertificateManagementModeCustom,
Custom: configv1alpha1.CustomPKIPolicy{
PKIProfile: profile,
PKIProfile: signerKeyParams.Profile,
},
}
}
Expand Down Expand Up @@ -101,20 +99,3 @@ func (p *PKIConfiguration) Files() []*asset.File {
func (p *PKIConfiguration) Load(f asset.FileFetcher) (bool, error) {
return false, nil
}

// convertToAPICertConfig converts the installer CertificateConfig
// to the openshift/api configv1alpha1.CertificateConfig for use in the PKI CR manifest.
func convertToAPICertConfig(certConf types.CertificateConfig) configv1alpha1.CertificateConfig {
out := configv1alpha1.CertificateConfig{
Key: configv1alpha1.KeyConfig{
Algorithm: configv1alpha1.KeyAlgorithm(certConf.Key.Algorithm),
},
}
if certConf.Key.RSA != nil {
out.Key.RSA = configv1alpha1.RSAKeyConfig{KeySize: certConf.Key.RSA.KeySize}
}
if certConf.Key.ECDSA != nil {
out.Key.ECDSA = configv1alpha1.ECDSAKeyConfig{Curve: configv1alpha1.ECDSACurve(certConf.Key.ECDSA.Curve)}
}
return out
}
82 changes: 50 additions & 32 deletions pkg/asset/manifests/pki_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ import (
"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"
"github.com/openshift/installer/pkg/asset/tls"
libpki "github.com/openshift/library-go/pkg/pki"
)

func TestPKIConfigurationGenerate(t *testing.T) {
cases := []struct {
testCases := []struct {
name string
installConfig *types.InstallConfig
signerKeyParams *tls.SignerKeyParams
expectEmpty bool
expectMode configv1alpha1.PKICertificateManagementMode
expectSignerAlgo configv1alpha1.KeyAlgorithm
Expand All @@ -29,31 +28,38 @@ func TestPKIConfigurationGenerate(t *testing.T) {
}{
{
name: "feature gate disabled - no manifest generated",
installConfig: &types.InstallConfig{
FeatureSet: configv1.Default,
signerKeyParams: &tls.SignerKeyParams{
ConfigurablePKIEnabled: false,
},
expectEmpty: true,
},
{
name: "feature gate enabled, pki nil - mode Default",
installConfig: &types.InstallConfig{
FeatureSet: configv1.TechPreviewNoUpgrade,
signerKeyParams: &tls.SignerKeyParams{
Profile: libpki.DefaultPKIProfile(),
ConfigurablePKIEnabled: true,
},
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},
signerKeyParams: &tls.SignerKeyParams{
Profile: 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},
},
},
},
ConfigurablePKIEnabled: true,
},
expectEmpty: false,
expectMode: configv1alpha1.PKICertificateManagementModeCustom,
Expand All @@ -64,16 +70,22 @@ func TestPKIConfigurationGenerate(t *testing.T) {
},
{
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},
signerKeyParams: &tls.SignerKeyParams{
Profile: configv1alpha1.PKIProfile{
Defaults: configv1alpha1.DefaultCertificateConfig{
Key: configv1alpha1.KeyConfig{
Algorithm: configv1alpha1.KeyAlgorithmRSA,
RSA: configv1alpha1.RSAKeyConfig{KeySize: 4096},
},
},
SignerCertificates: configv1alpha1.CertificateConfig{
Key: configv1alpha1.KeyConfig{
Algorithm: configv1alpha1.KeyAlgorithmECDSA,
ECDSA: configv1alpha1.ECDSAKeyConfig{Curve: configv1alpha1.ECDSACurveP384},
},
},
},
ConfigurablePKIEnabled: true,
},
expectEmpty: false,
expectMode: configv1alpha1.PKICertificateManagementModeCustom,
Expand All @@ -84,16 +96,22 @@ func TestPKIConfigurationGenerate(t *testing.T) {
},
{
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},
signerKeyParams: &tls.SignerKeyParams{
Profile: 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: 2048},
},
},
},
ConfigurablePKIEnabled: true,
},
expectEmpty: false,
expectMode: configv1alpha1.PKICertificateManagementModeCustom,
Expand All @@ -104,10 +122,10 @@ func TestPKIConfigurationGenerate(t *testing.T) {
},
}

for _, tc := range cases {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
parents := asset.Parents{}
parents.Add(installconfig.MakeAsset(tc.installConfig))
parents.Add(tc.signerKeyParams)

pkiAsset := &PKIConfiguration{}
err := pkiAsset.Generate(context.Background(), parents)
Expand Down
Loading