Skip to content

Commit ed2ed81

Browse files
committed
tls: refactor TLS cert generation to support configurable key algorithms
Refactor pkg/asset/tls/ to support generating signer certificates with configurable key algorithms (RSA or ECDSA). PrivateKeyToPem now returns ([]byte, error) instead of calling logrus.Fatalf, and GenerateSelfSignedCertificate accepts PrivateKeyParams to control key generation. KeyUsage flags are set based on the algorithm since ECDSA keys cannot perform key encipherment. All signer certs pass nil for pkiConfig in this commit, preserving the existing RSA-2048 behavior. Wiring signers to read PKI config is deferred to a follow-up to avoid breaking codepaths that generate signer certs without an install-config on disk (e.g. agent create certificates, node-joiner add-nodes). Assisted-by: Claude Code (Opus 4.6)
1 parent f1faa3f commit ed2ed81

16 files changed

Lines changed: 729 additions & 119 deletions

pkg/asset/imagebased/configimage/ingressoperatorsigner.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ func (a *IngressOperatorSignerCertKey) Generate(ctx context.Context, dependencie
5959
return err
6060
}
6161

62-
a.KeyRaw = tls.PrivateKeyToPem(key)
62+
a.KeyRaw, err = tls.PrivateKeyToPem(key)
63+
if err != nil {
64+
return fmt.Errorf("failed to encode private key to PEM: %w", err)
65+
}
6366
a.CertRaw = tls.CertToPem(crt)
6467

6568
return nil

pkg/asset/tls/adminkubeconfig.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,27 @@ type AdminKubeConfigSignerCertKey struct {
1515

1616
var _ asset.WritableAsset = (*AdminKubeConfigSignerCertKey)(nil)
1717

18-
// Dependencies returns the dependency of the root-ca, which is empty.
18+
// Dependencies returns no dependencies. Configurable PKI requires
19+
// reading the PKI config from InstallConfig, but adding InstallConfig
20+
// as a dependency here would break codepaths that generate signer certs
21+
// without an install-config on disk (e.g. agent create certificates,
22+
// node-joiner). A follow-up commit introduces an intermediate asset
23+
// that reads the config directly from disk without triggering
24+
// InstallConfig validation.
1925
func (c *AdminKubeConfigSignerCertKey) Dependencies() []asset.Asset {
2026
return []asset.Asset{}
2127
}
2228

2329
// Generate generates the root-ca key and cert pair.
2430
func (c *AdminKubeConfigSignerCertKey) Generate(ctx context.Context, parents asset.Parents) error {
2531
cfg := &CertCfg{
26-
Subject: pkix.Name{CommonName: "admin-kubeconfig-signer", OrganizationalUnit: []string{"openshift"}},
27-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
28-
Validity: ValidityTenYears(),
29-
IsCA: true,
32+
Subject: pkix.Name{CommonName: "admin-kubeconfig-signer", OrganizationalUnit: []string{"openshift"}},
33+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
34+
Validity: ValidityTenYears(),
35+
IsCA: true,
3036
}
3137

32-
return c.SelfSignedCertKey.Generate(ctx, cfg, "admin-kubeconfig-signer")
38+
return c.SelfSignedCertKey.Generate(ctx, cfg, "admin-kubeconfig-signer", nil)
3339
}
3440

3541
// Load reads the asset files from disk.

pkg/asset/tls/aggregator.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ func (a *AggregatorCA) Generate(ctx context.Context, dependencies asset.Parents)
3232
dependencies.Get(installConfig)
3333

3434
cfg := &CertCfg{
35-
Subject: pkix.Name{CommonName: "aggregator", OrganizationalUnit: []string{"bootkube"}},
36-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
37-
Validity: ValidityOneDay(installConfig),
38-
IsCA: true,
35+
Subject: pkix.Name{CommonName: "aggregator", OrganizationalUnit: []string{"bootkube"}},
36+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
37+
Validity: ValidityOneDay(installConfig),
38+
IsCA: true,
3939
}
4040

41-
return a.SelfSignedCertKey.Generate(ctx, cfg, "aggregator-ca")
41+
return a.SelfSignedCertKey.Generate(ctx, cfg, "aggregator-ca", nil)
4242
}
4343

4444
// Name returns the human-friendly name of the asset.
@@ -102,13 +102,13 @@ func (c *AggregatorSignerCertKey) Generate(ctx context.Context, parents asset.Pa
102102
installConfig := &installconfig.InstallConfig{}
103103
parents.Get(installConfig)
104104
cfg := &CertCfg{
105-
Subject: pkix.Name{CommonName: "aggregator-signer", OrganizationalUnit: []string{"openshift"}},
106-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
107-
Validity: ValidityOneDay(installConfig),
108-
IsCA: true,
105+
Subject: pkix.Name{CommonName: "aggregator-signer", OrganizationalUnit: []string{"openshift"}},
106+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
107+
Validity: ValidityOneDay(installConfig),
108+
IsCA: true,
109109
}
110110

111-
return c.SelfSignedCertKey.Generate(ctx, cfg, "aggregator-signer")
111+
return c.SelfSignedCertKey.Generate(ctx, cfg, "aggregator-signer", nil)
112112
}
113113

114114
// Name returns the human-friendly name of the asset.

pkg/asset/tls/apiserver.go

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ func (c *KubeAPIServerToKubeletSignerCertKey) Generate(ctx context.Context, pare
2929
installConfig := &installconfig.InstallConfig{}
3030
parents.Get(installConfig)
3131
cfg := &CertCfg{
32-
Subject: pkix.Name{CommonName: "kube-apiserver-to-kubelet-signer", OrganizationalUnit: []string{"openshift"}},
33-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
34-
Validity: ValidityOneYear(installConfig),
35-
IsCA: true,
32+
Subject: pkix.Name{CommonName: "kube-apiserver-to-kubelet-signer", OrganizationalUnit: []string{"openshift"}},
33+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
34+
Validity: ValidityOneYear(installConfig),
35+
IsCA: true,
3636
}
3737

38-
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-to-kubelet-signer")
38+
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-to-kubelet-signer", nil)
3939
}
4040

4141
// Name returns the human-friendly name of the asset.
@@ -116,21 +116,27 @@ type KubeAPIServerLocalhostSignerCertKey struct {
116116

117117
var _ asset.WritableAsset = (*KubeAPIServerLocalhostSignerCertKey)(nil)
118118

119-
// Dependencies returns the dependency of the root-ca, which is empty.
119+
// Dependencies returns no dependencies. Configurable PKI requires
120+
// reading the PKI config from InstallConfig, but adding InstallConfig
121+
// as a dependency here would break codepaths that generate signer certs
122+
// without an install-config on disk (e.g. agent create certificates,
123+
// node-joiner). A follow-up commit introduces an intermediate asset
124+
// that reads the config directly from disk without triggering
125+
// InstallConfig validation.
120126
func (c *KubeAPIServerLocalhostSignerCertKey) Dependencies() []asset.Asset {
121127
return []asset.Asset{}
122128
}
123129

124130
// Generate generates the root-ca key and cert pair.
125131
func (c *KubeAPIServerLocalhostSignerCertKey) Generate(ctx context.Context, parents asset.Parents) error {
126132
cfg := &CertCfg{
127-
Subject: pkix.Name{CommonName: "kube-apiserver-localhost-signer", OrganizationalUnit: []string{"openshift"}},
128-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
129-
Validity: ValidityTenYears(),
130-
IsCA: true,
133+
Subject: pkix.Name{CommonName: "kube-apiserver-localhost-signer", OrganizationalUnit: []string{"openshift"}},
134+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
135+
Validity: ValidityTenYears(),
136+
IsCA: true,
131137
}
132138

133-
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-localhost-signer")
139+
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-localhost-signer", nil)
134140
}
135141

136142
// Load reads the asset files from disk.
@@ -220,21 +226,27 @@ type KubeAPIServerServiceNetworkSignerCertKey struct {
220226

221227
var _ asset.WritableAsset = (*KubeAPIServerServiceNetworkSignerCertKey)(nil)
222228

223-
// Dependencies returns the dependency of the root-ca, which is empty.
229+
// Dependencies returns no dependencies. Configurable PKI requires
230+
// reading the PKI config from InstallConfig, but adding InstallConfig
231+
// as a dependency here would break codepaths that generate signer certs
232+
// without an install-config on disk (e.g. agent create certificates,
233+
// node-joiner). A follow-up commit introduces an intermediate asset
234+
// that reads the config directly from disk without triggering
235+
// InstallConfig validation.
224236
func (c *KubeAPIServerServiceNetworkSignerCertKey) Dependencies() []asset.Asset {
225237
return []asset.Asset{}
226238
}
227239

228240
// Generate generates the root-ca key and cert pair.
229241
func (c *KubeAPIServerServiceNetworkSignerCertKey) Generate(ctx context.Context, parents asset.Parents) error {
230242
cfg := &CertCfg{
231-
Subject: pkix.Name{CommonName: "kube-apiserver-service-network-signer", OrganizationalUnit: []string{"openshift"}},
232-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
233-
Validity: ValidityTenYears(),
234-
IsCA: true,
243+
Subject: pkix.Name{CommonName: "kube-apiserver-service-network-signer", OrganizationalUnit: []string{"openshift"}},
244+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
245+
Validity: ValidityTenYears(),
246+
IsCA: true,
235247
}
236248

237-
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-service-network-signer")
249+
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-service-network-signer", nil)
238250
}
239251

240252
// Load reads the asset files from disk.
@@ -333,21 +345,27 @@ type KubeAPIServerLBSignerCertKey struct {
333345

334346
var _ asset.WritableAsset = (*KubeAPIServerLBSignerCertKey)(nil)
335347

336-
// Dependencies returns the dependency of the root-ca, which is empty.
348+
// Dependencies returns no dependencies. Configurable PKI requires
349+
// reading the PKI config from InstallConfig, but adding InstallConfig
350+
// as a dependency here would break codepaths that generate signer certs
351+
// without an install-config on disk (e.g. agent create certificates,
352+
// node-joiner). A follow-up commit introduces an intermediate asset
353+
// that reads the config directly from disk without triggering
354+
// InstallConfig validation.
337355
func (c *KubeAPIServerLBSignerCertKey) Dependencies() []asset.Asset {
338356
return []asset.Asset{}
339357
}
340358

341359
// Generate generates the root-ca key and cert pair.
342360
func (c *KubeAPIServerLBSignerCertKey) Generate(ctx context.Context, parents asset.Parents) error {
343361
cfg := &CertCfg{
344-
Subject: pkix.Name{CommonName: "kube-apiserver-lb-signer", OrganizationalUnit: []string{"openshift"}},
345-
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
346-
Validity: ValidityTenYears(),
347-
IsCA: true,
362+
Subject: pkix.Name{CommonName: "kube-apiserver-lb-signer", OrganizationalUnit: []string{"openshift"}},
363+
// KeyUsages is set by GenerateSelfSignedCertificate based on the key algorithm.
364+
Validity: ValidityTenYears(),
365+
IsCA: true,
348366
}
349367

350-
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-lb-signer")
368+
return c.SelfSignedCertKey.Generate(ctx, cfg, "kube-apiserver-lb-signer", nil)
351369
}
352370

353371
// Load reads the asset files from disk.

pkg/asset/tls/boundsasigningkey.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tls
22

33
import (
44
"context"
5+
"crypto/rsa"
56
"os"
67

78
"github.com/pkg/errors"
@@ -50,10 +51,14 @@ func (sk *BoundSASigningKey) Load(f asset.FileFetcher) (bool, error) {
5051
return false, err
5152
}
5253

53-
rsaKey, err := PemToPrivateKey(keyFile.Data)
54+
key, err := PemToPrivateKey(keyFile.Data)
5455
if err != nil {
55-
logrus.Debugf("Failed to load rsa.PrivateKey from file: %s", err)
56-
return false, errors.Wrap(err, "failed to load rsa.PrivateKey from the file")
56+
logrus.Debugf("Failed to load private key from file: %s", err)
57+
return false, errors.Wrap(err, "failed to load private key from the file")
58+
}
59+
rsaKey, ok := key.(*rsa.PrivateKey)
60+
if !ok {
61+
return false, errors.New("bound service account signing key must be RSA")
5762
}
5863
pubData, err := PublicKeyToPem(&rsaKey.PublicKey)
5964
if err != nil {

pkg/asset/tls/certkey.go

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ package tls
33
import (
44
"bytes"
55
"context"
6-
"crypto/rsa"
7-
"crypto/x509"
86
"os"
97

108
"github.com/pkg/errors"
119
"github.com/sirupsen/logrus"
1210

1311
"github.com/openshift/installer/pkg/asset"
12+
"github.com/openshift/installer/pkg/types"
1413
)
1514

1615
// CertInterface contains cert.
@@ -128,14 +127,10 @@ func (c *SignedCertKey) Generate(_ context.Context,
128127
filenameBase string,
129128
appendParent AppendParentChoice,
130129
) error {
131-
var key *rsa.PrivateKey
132-
var crt *x509.Certificate
133-
var err error
134-
135130
caKey, err := PemToPrivateKey(parentCA.Key())
136131
if err != nil {
137-
logrus.Debugf("Failed to parse RSA private key: %s", err)
138-
return errors.Wrap(err, "failed to parse rsa private key")
132+
logrus.Debugf("Failed to parse private key: %s", err)
133+
return errors.Wrap(err, "failed to parse private key")
139134
}
140135

141136
caCert, err := PemToCertificate(parentCA.Cert())
@@ -144,13 +139,16 @@ func (c *SignedCertKey) Generate(_ context.Context,
144139
return errors.Wrap(err, "failed to parse x509 certificate")
145140
}
146141

147-
key, crt, err = GenerateSignedCertificate(caKey, caCert, cfg)
142+
key, crt, err := GenerateSignedCertificate(caKey, caCert, cfg)
148143
if err != nil {
149144
logrus.Debugf("Failed to generate signed cert/key pair: %s", err)
150145
return errors.Wrap(err, "failed to generate signed cert/key pair")
151146
}
152147

153-
c.KeyRaw = PrivateKeyToPem(key)
148+
c.KeyRaw, err = PrivateKeyToPem(key)
149+
if err != nil {
150+
return errors.Wrap(err, "failed to encode private key to PEM")
151+
}
154152
c.CertRaw = CertToPem(crt)
155153

156154
if appendParent {
@@ -167,17 +165,23 @@ type SelfSignedCertKey struct {
167165
CertKey
168166
}
169167

170-
// Generate generates a cert/key pair signed by the specified parent CA.
168+
// Generate generates a self-signed cert/key pair using the specified PKI profile.
171169
func (c *SelfSignedCertKey) Generate(_ context.Context,
172170
cfg *CertCfg,
173171
filenameBase string,
172+
pkiConfig *types.PKIConfig,
174173
) error {
175-
key, crt, err := GenerateSelfSignedCertificate(cfg)
174+
params := PKIConfigToKeyParams(pkiConfig)
175+
176+
key, crt, err := GenerateSelfSignedCertificate(cfg, params)
176177
if err != nil {
177178
return errors.Wrap(err, "failed to generate self-signed cert/key pair")
178179
}
179180

180-
c.KeyRaw = PrivateKeyToPem(key)
181+
c.KeyRaw, err = PrivateKeyToPem(key)
182+
if err != nil {
183+
return errors.Wrap(err, "failed to encode private key to PEM")
184+
}
181185
c.CertRaw = CertToPem(crt)
182186

183187
c.generateFiles(filenameBase)
@@ -192,14 +196,10 @@ func RegenerateSignedCertKey(
192196
parentCA CertKeyInterface,
193197
appendParent AppendParentChoice,
194198
) ([]byte, []byte, error) {
195-
var key *rsa.PrivateKey
196-
var crt *x509.Certificate
197-
var err error
198-
199199
caKey, err := PemToPrivateKey(parentCA.Key())
200200
if err != nil {
201-
logrus.Debugf("Failed to parse RSA private key: %s", err)
202-
return nil, nil, errors.Wrap(err, "failed to parse rsa private key")
201+
logrus.Debugf("Failed to parse private key: %s", err)
202+
return nil, nil, errors.Wrap(err, "failed to parse private key")
203203
}
204204

205205
caCert, err := PemToCertificate(parentCA.Cert())
@@ -208,13 +208,16 @@ func RegenerateSignedCertKey(
208208
return nil, nil, errors.Wrap(err, "failed to parse x509 certificate")
209209
}
210210

211-
key, crt, err = GenerateSignedCertificate(caKey, caCert, cfg)
212-
if err != nil {
213-
logrus.Debugf("Failed to generate signed cert/key pair: %s", err)
214-
return nil, nil, errors.Wrap(err, "failed to generate signed cert/key pair")
211+
key, crt, generateErr := GenerateSignedCertificate(caKey, caCert, cfg)
212+
if generateErr != nil {
213+
logrus.Debugf("Failed to generate signed cert/key pair: %s", generateErr)
214+
return nil, nil, errors.Wrap(generateErr, "failed to generate signed cert/key pair")
215215
}
216216

217-
keyRaw := PrivateKeyToPem(key)
217+
keyRaw, err := PrivateKeyToPem(key)
218+
if err != nil {
219+
return nil, nil, errors.Wrap(err, "failed to encode private key to PEM")
220+
}
218221
certRaw := CertToPem(crt)
219222

220223
if appendParent {

0 commit comments

Comments
 (0)