Skip to content

Commit 87d97f2

Browse files
authored
Use x509.CertPools instead of PEM strings (#1052)
Signed-off-by: Todd Short <[email protected]>
1 parent bfc65ee commit 87d97f2

File tree

5 files changed

+67
-44
lines changed

5 files changed

+67
-44
lines changed

cmd/manager/main.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,13 @@ func main() {
152152
os.Exit(1)
153153
}
154154

155-
httpClient, err := httputil.BuildHTTPClient(caCertDir)
155+
certPool, err := httputil.NewCertPool(caCertDir)
156+
if err != nil {
157+
setupLog.Error(err, "unable to create CA certificate pool")
158+
os.Exit(1)
159+
}
160+
161+
httpClient, err := httputil.BuildHTTPClient(certPool)
156162
if err != nil {
157163
setupLog.Error(err, "unable to create catalogd http client")
158164
}
@@ -191,6 +197,7 @@ func main() {
191197
BaseCachePath: filepath.Join(cachePath, "unpack"),
192198
// TODO: This needs to be derived per extension via ext.Spec.InstallNamespace
193199
AuthNamespace: systemNamespace,
200+
CaCertPool: certPool,
194201
}
195202

196203
domain := ocv1alpha1.GroupVersion.Group
@@ -220,7 +227,7 @@ func main() {
220227
Unpacker: unpacker,
221228
InstalledBundleGetter: &controllers.DefaultInstalledBundleGetter{ActionClientGetter: acg},
222229
Finalizers: clusterExtensionFinalizers,
223-
CaCertDir: caCertDir,
230+
CaCertPool: certPool,
224231
Preflights: preflights,
225232
}).SetupWithManager(mgr); err != nil {
226233
setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension")

internal/controllers/clusterextension_controller.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controllers
1919
import (
2020
"bytes"
2121
"context"
22+
"crypto/x509"
2223
"errors"
2324
"fmt"
2425
"io"
@@ -69,7 +70,6 @@ import (
6970
catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter"
7071
catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort"
7172
"github.com/operator-framework/operator-controller/internal/conditionsets"
72-
"github.com/operator-framework/operator-controller/internal/httputil"
7373
"github.com/operator-framework/operator-controller/internal/labels"
7474
"github.com/operator-framework/operator-controller/internal/rukpak/bundledeployment"
7575
"github.com/operator-framework/operator-controller/internal/rukpak/convert"
@@ -94,7 +94,7 @@ type ClusterExtensionReconciler struct {
9494
cache cache.Cache
9595
InstalledBundleGetter InstalledBundleGetter
9696
Finalizers crfinalizer.Finalizers
97-
CaCertDir string
97+
CaCertPool *x509.CertPool
9898
Preflights []Preflight
9999
}
100100

@@ -273,7 +273,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
273273
// Generate a BundleDeployment from the ClusterExtension to Unpack.
274274
// Note: The BundleDeployment here is not a k8s API, its a simple Go struct which
275275
// necessary embedded values.
276-
bd := r.generateBundleDeploymentForUnpack(ctx, bundle.Image, ext)
276+
bd := r.generateBundleDeploymentForUnpack(bundle.Image, ext)
277277
l.V(1).Info("unpacking resolved bundle")
278278
unpackResult, err := r.Unpacker.Unpack(ctx, bd)
279279
if err != nil {
@@ -577,20 +577,15 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundle *catalogmetad
577577
}
578578
}
579579

580-
func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(ctx context.Context, bundlePath string, ce *ocv1alpha1.ClusterExtension) *bundledeployment.BundleDeployment {
581-
certData, err := httputil.LoadCerts(r.CaCertDir)
582-
if err != nil {
583-
log.FromContext(ctx).WithName("operator-controller").WithValues("cluster-extension", ce.GetName()).Error(err, "unable to get TLS certificate")
584-
}
580+
func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(bundlePath string, ce *ocv1alpha1.ClusterExtension) *bundledeployment.BundleDeployment {
585581
return &bundledeployment.BundleDeployment{
586582
Name: ce.Name,
587583
Spec: bundledeployment.BundleDeploymentSpec{
588584
InstallNamespace: ce.Spec.InstallNamespace,
589585
Source: bundledeployment.BundleSource{
590586
Type: bundledeployment.SourceTypeImage,
591587
Image: &bundledeployment.ImageSource{
592-
Ref: bundlePath,
593-
CertificateData: certData,
588+
Ref: bundlePath,
594589
},
595590
},
596591
},

internal/httputil/httputil.go

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,79 @@ package httputil
33
import (
44
"crypto/tls"
55
"crypto/x509"
6+
"encoding/pem"
7+
"fmt"
68
"net/http"
79
"os"
810
"path/filepath"
9-
"strings"
1011
"time"
1112
)
1213

13-
func LoadCerts(caDir string) (string, error) {
14+
// This version of (*x509.CertPool).AppendCertsFromPEM() will error out if parsing fails
15+
func appendCertsFromPEM(s *x509.CertPool, pemCerts []byte) error {
16+
n := 1
17+
for len(pemCerts) > 0 {
18+
var block *pem.Block
19+
block, pemCerts = pem.Decode(pemCerts)
20+
if block == nil {
21+
return fmt.Errorf("unable to PEM decode cert %d", n)
22+
}
23+
// ignore non-certificates (e.g. keys)
24+
if block.Type != "CERTIFICATE" {
25+
continue
26+
}
27+
if len(block.Headers) != 0 {
28+
// This is a cert, but we're ignoring it, so bump the counter
29+
n++
30+
continue
31+
}
32+
33+
cert, err := x509.ParseCertificate(block.Bytes)
34+
if err != nil {
35+
return fmt.Errorf("unable to parse cert %d: %w", n, err)
36+
}
37+
// no return values - panics or always succeeds
38+
s.AddCert(cert)
39+
n++
40+
}
41+
42+
return nil
43+
}
44+
45+
func NewCertPool(caDir string) (*x509.CertPool, error) {
46+
caCertPool, err := x509.SystemCertPool()
47+
if err != nil {
48+
return nil, err
49+
}
1450
if caDir == "" {
15-
return "", nil
51+
return caCertPool, nil
1652
}
1753

18-
certs := []string{}
1954
dirEntries, err := os.ReadDir(caDir)
2055
if err != nil {
21-
return "", err
56+
return nil, err
2257
}
2358
for _, e := range dirEntries {
2459
if e.IsDir() {
2560
continue
2661
}
27-
data, err := os.ReadFile(filepath.Join(caDir, e.Name()))
62+
file := filepath.Join(caDir, e.Name())
63+
data, err := os.ReadFile(file)
64+
if err != nil {
65+
return nil, fmt.Errorf("error reading cert file %q: %w", file, err)
66+
}
67+
err = appendCertsFromPEM(caCertPool, data)
2868
if err != nil {
29-
return "", err
69+
return nil, fmt.Errorf("error adding cert file %q: %w", file, err)
3070
}
31-
certs = append(certs, string(data))
3271
}
33-
return strings.Join(certs, "\n"), nil
72+
73+
return caCertPool, nil
3474
}
3575

36-
func BuildHTTPClient(caDir string) (*http.Client, error) {
76+
func BuildHTTPClient(caCertPool *x509.CertPool) (*http.Client, error) {
3777
httpClient := &http.Client{Timeout: 10 * time.Second}
3878

39-
// use the SystemCertPool as a default
40-
caCertPool, err := x509.SystemCertPool()
41-
if err != nil {
42-
return nil, err
43-
}
44-
45-
certs, err := LoadCerts(caDir)
46-
if err != nil {
47-
return nil, err
48-
}
49-
50-
caCertPool.AppendCertsFromPEM([]byte(certs))
5179
tlsConfig := &tls.Config{
5280
RootCAs: caCertPool,
5381
MinVersion: tls.VersionTLS12,

internal/rukpak/bundledeployment/bundledeployment.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,4 @@ type ImageSource struct {
8080
// fetch the specified image reference.
8181
// This should not be used in a production environment.
8282
InsecureSkipTLSVerify bool
83-
// CertificateData contains the PEM data of the certificate that is to be used for the TLS connection
84-
CertificateData string
8583
}

internal/rukpak/source/image_registry.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func NewUnrecoverable(err error) *Unrecoverable {
4040
type ImageRegistry struct {
4141
BaseCachePath string
4242
AuthNamespace string
43+
CaCertPool *x509.CertPool
4344
}
4445

4546
func (i *ImageRegistry) Unpack(ctx context.Context, bundle *bd.BundleDeployment) (*Result, error) {
@@ -85,13 +86,8 @@ func (i *ImageRegistry) Unpack(ctx context.Context, bundle *bd.BundleDeployment)
8586
if bundle.Spec.Source.Image.InsecureSkipTLSVerify {
8687
transport.TLSClientConfig.InsecureSkipVerify = true // nolint:gosec
8788
}
88-
if bundle.Spec.Source.Image.CertificateData != "" {
89-
pool, err := x509.SystemCertPool()
90-
if err != nil || pool == nil {
91-
pool = x509.NewCertPool()
92-
}
93-
transport.TLSClientConfig.RootCAs = pool
94-
transport.TLSClientConfig.RootCAs.AppendCertsFromPEM([]byte(bundle.Spec.Source.Image.CertificateData))
89+
if i.CaCertPool != nil {
90+
transport.TLSClientConfig.RootCAs = i.CaCertPool
9591
}
9692
remoteOpts = append(remoteOpts, remote.WithTransport(transport))
9793

@@ -163,7 +159,6 @@ func unpackedResult(fsys fs.FS, bundle *bd.BundleDeployment, ref string) *Result
163159
Ref: ref,
164160
ImagePullSecretName: bundle.Spec.Source.Image.ImagePullSecretName,
165161
InsecureSkipTLSVerify: bundle.Spec.Source.Image.InsecureSkipTLSVerify,
166-
CertificateData: bundle.Spec.Source.Image.CertificateData,
167162
},
168163
},
169164
State: StateUnpacked,

0 commit comments

Comments
 (0)