Skip to content

Commit

Permalink
Certificate support for image registry
Browse files Browse the repository at this point in the history
Remove the InsecureSkipTLSVerify annotations.

* Create a ClusterIssuer CA (via openssl) that is used by OLMv1 e2e
* Update the operator controller to specify a cert directory, rather than a
  single file.
* Use this directory for catalogd and image-registries
* Update the deployment to reference CAs appropriately

Signed-off-by: Todd Short <tshort@redhat.com>
  • Loading branch information
tmshort committed Jun 20, 2024
1 parent 58b4363 commit b128ce3
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 56 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ test-e2e: GO_BUILD_FLAGS := -cover
test-e2e: run image-registry build-push-e2e-catalog registry-load-bundles e2e e2e-coverage kind-clean #HELP Run e2e test suite on local kind cluster

.PHONY: extension-developer-e2e
extension-developer-e2e: KUSTOMIZE_BUILD_DIR := config/overlays/certs
extension-developer-e2e: KIND_CLUSTER_NAME := operator-controller-ext-dev-e2e #EXHELP Run extension-developer e2e on local kind cluster
extension-developer-e2e: run image-registry test-ext-dev-e2e kind-clean

Expand Down
7 changes: 4 additions & 3 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ func main() {
cachePath string
operatorControllerVersion bool
systemNamespace string
caCert string
caCertDir string
)
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.StringVar(&caCert, "ca-cert", "", "The TLS certificate to use for verifying HTTPS connections to the Catalogd web server.")
flag.StringVar(&caCertDir, "ca-cert", "", "The directory of TLS certificate to use for verifying HTTPS connections to the Catalogd and docker-registry web servers.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
Expand Down Expand Up @@ -151,7 +151,7 @@ func main() {
os.Exit(1)
}

httpClient, err := httputil.BuildHTTPClient(caCert)
httpClient, err := httputil.BuildHTTPClient(caCertDir)
if err != nil {
setupLog.Error(err, "unable to create catalogd http client")
}
Expand Down Expand Up @@ -217,6 +217,7 @@ func main() {
InstalledBundleGetter: &controllers.DefaultInstalledBundleGetter{ActionClientGetter: acg},
Handler: registryv1handler.HandlerFunc(registry.HandleBundleDeployment),
Finalizers: clusterExtensionFinalizers,
CaCertDir: caCertDir,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension")
os.Exit(1)
Expand Down
8 changes: 8 additions & 0 deletions config/overlays/certs/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace: olmv1-system

resources:
- ../../overlays/tls
- manager_cert.yaml

patches:
- path: manager_cert_patch.yaml
16 changes: 16 additions & 0 deletions config/overlays/certs/manager_cert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: olmv1-cert
spec:
secretName: olmv1-cert
dnsNames:
- operator-controller.olmv1-system.svc
- operator-controller.olmv1-system.svc.cluster.local
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: olmv1-ca
kind: ClusterIssuer
group: cert-manager.io
23 changes: 23 additions & 0 deletions config/overlays/certs/manager_cert_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
- name: manager
volumeMounts:
- name: e2e-cert
mountPath: /var/certs/olm-ca.crt
subPath: olm-ca.crt
readOnly: true
volumes:
- name: e2e-cert
secret:
secretName: olmv1-cert
items:
- key: ca.crt
path: olm-ca.crt
2 changes: 1 addition & 1 deletion config/overlays/e2e/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace: olmv1-system

resources:
- ../../overlays/tls
- ../../overlays/certs
- manager_e2e_coverage_pvc.yaml
- manager_e2e_coverage_copy_pod.yaml

Expand Down
6 changes: 3 additions & 3 deletions config/overlays/tls/patches/manager_deployment_cert.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
- op: add
path: /spec/template/spec/volumes/-
value: {"name":"ca-certificate", "secret":{"secretName":"catalogd-catalogserver-cert", "optional": false, "items": [{"key": "tls.crt", "path": "tls.crt"}]}}
value: {"name":"catalogd-certificate", "secret":{"secretName":"catalogd-catalogserver-cert", "optional": false, "items": [{"key": "ca.crt", "path": "catalogd.crt"}]}}
- op: add
path: /spec/template/spec/containers/0/volumeMounts/-
value: {"name":"ca-certificate", "readOnly": true, "mountPath":"/var/certs"}
value: {"name":"catalogd-certificate", "readOnly": true, "mountPath":"/var/certs/catalogd.crt", "subPath":"catalogd.crt"}
- op: add
path: /spec/template/spec/containers/0/args/-
value: "--ca-cert=/var/certs/tls.crt"
value: "--ca-cert=/var/certs"
28 changes: 10 additions & 18 deletions internal/controllers/clusterextension_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import (
catalogfilter "github.com/operator-framework/operator-controller/internal/catalogmetadata/filter"
catalogsort "github.com/operator-framework/operator-controller/internal/catalogmetadata/sort"
"github.com/operator-framework/operator-controller/internal/conditionsets"
"github.com/operator-framework/operator-controller/internal/httputil"
"github.com/operator-framework/operator-controller/internal/labels"
)

Expand All @@ -90,16 +91,13 @@ type ClusterExtensionReconciler struct {
cache cache.Cache
InstalledBundleGetter InstalledBundleGetter
Finalizers crfinalizer.Finalizers
CaCertDir string
}

type InstalledBundleGetter interface {
GetInstalledBundle(ctx context.Context, ext *ocv1alpha1.ClusterExtension) (*ocv1alpha1.BundleMetadata, error)
}

const (
bundleConnectionAnnotation string = "bundle.connection.config/insecureSkipTLSVerify"
)

//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions,verbs=get;list;watch
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions/status,verbs=update;patch
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensions/finalizers,verbs=update
Expand Down Expand Up @@ -249,7 +247,7 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
// Generate a BundleDeployment from the ClusterExtension to Unpack.
// Note: The BundleDeployment here is not a k8s API, its a simple Go struct which
// necessary embedded values.
bd := r.generateBundleDeploymentForUnpack(bundle.Image, ext)
bd := r.generateBundleDeploymentForUnpack(ctx, bundle.Image, ext)
unpackResult, err := r.Unpacker.Unpack(ctx, bd)
if err != nil {
setStatusUnpackFailed(ext, err.Error())
Expand Down Expand Up @@ -533,7 +531,11 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundle *catalogmetad
}
}

func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(bundlePath string, ce *ocv1alpha1.ClusterExtension) *rukpakv1alpha2.BundleDeployment {
func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(ctx context.Context, bundlePath string, ce *ocv1alpha1.ClusterExtension) *rukpakv1alpha2.BundleDeployment {
certData, err := httputil.LoadCerts(r.CaCertDir)
if err != nil {
log.FromContext(ctx).WithName("operator-controller").WithValues("cluster-extension", ce.GetName()).Error(err, "unable to get TLS certificate")
}
return &rukpakv1alpha2.BundleDeployment{
TypeMeta: metav1.TypeMeta{
Kind: ce.Kind,
Expand All @@ -549,24 +551,14 @@ func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(bundlePat
Type: rukpakv1alpha2.SourceTypeImage,
Image: &rukpakv1alpha2.ImageSource{
Ref: bundlePath,
InsecureSkipTLSVerify: isInsecureSkipTLSVerifySet(ce),
InsecureSkipTLSVerify: false,
CertificateData: certData,
},
},
},
}
}

func isInsecureSkipTLSVerifySet(ce *ocv1alpha1.ClusterExtension) bool {
if ce == nil {
return false
}
value, ok := ce.Annotations[bundleConnectionAnnotation]
if !ok {
return false
}
return value == "true"
}

// SetupWithManager sets up the controller with the Manager.
func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error {
controller, err := ctrl.NewControllerManagedBy(mgr).
Expand Down
60 changes: 45 additions & 15 deletions internal/httputil/httputil.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,60 @@ import (
"crypto/x509"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)

func BuildHTTPClient(caCert string) (*http.Client, error) {
httpClient := &http.Client{Timeout: 10 * time.Second}

if caCert != "" {
// tlsFileWatcher, err := certwatcher.New(caCert, "")
func LoadCerts(caDir string) (string, error) {
if caDir == "" {
return "", nil
}

cert, err := os.ReadFile(caCert)
var certs []string
err := filepath.Walk(caDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil, err
return err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(cert)
tlsConfig := &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
if info.IsDir() {
return nil
}
tlsTransport := &http.Transport{
TLSClientConfig: tlsConfig,
data, err := os.ReadFile(path)
if err != nil {
return err
}
httpClient.Transport = tlsTransport
certs = append(certs, string(data))
return nil
})
if err != nil {
return "", err
}
return strings.Join(certs, "\n"), nil
}

func BuildHTTPClient(caDir string) (*http.Client, error) {
httpClient := &http.Client{Timeout: 10 * time.Second}

// use the SystemCertPool as a default
caCertPool, err := x509.SystemCertPool()
if err != nil {
return nil, err
}

certs, err := LoadCerts(caDir)
if err != nil {
return nil, err
}

caCertPool.AppendCertsFromPEM([]byte(certs))
tlsConfig := &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
tlsTransport := &http.Transport{
TLSClientConfig: tlsConfig,
}
httpClient.Transport = tlsTransport

return httpClient, nil
}
36 changes: 36 additions & 0 deletions scripts/install.tpl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,42 @@ function kubectl_wait() {
kubectl apply -f "https://github.com/cert-manager/cert-manager/releases/download/${cert_mgr_version}/cert-manager.yaml"
kubectl_wait "cert-manager" "deployment/cert-manager-webhook" "60s"

# Create the self-signed certificate for the ClusterIssuer and the ClusterIssuer
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: self-sign-issuer
namespace: cert-manager
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: olmv1-ca
namespace: cert-manager
spec:
isCA: true
commonName: olmv1-ca
secretName: olmv1-ca
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: self-sign-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: olmv1-ca
spec:
ca:
secretName: olmv1-ca
EOF

kubectl apply -f "https://github.com/operator-framework/catalogd/releases/download/${catalogd_version}/catalogd.yaml"
kubectl_wait "olmv1-system" "deployment/catalogd-controller-manager" "60s"

Expand Down
3 changes: 0 additions & 3 deletions test/e2e/cluster_extension_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, *catalogd.ClusterCata
clusterExtension := &ocv1alpha1.ClusterExtension{
ObjectMeta: metav1.ObjectMeta{
Name: clusterExtensionName,
Annotations: map[string]string{
"bundle.connection.config/insecureSkipTLSVerify": "true",
},
},
}
return clusterExtension, extensionCatalog
Expand Down
3 changes: 0 additions & 3 deletions test/extension-developer-e2e/extension_developer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ func TestExtensionDeveloper(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{
Name: "registryv1",
Annotations: map[string]string{
"bundle.connection.config/insecureSkipTLSVerify": "true",
},
},
Spec: ocv1alpha1.ClusterExtensionSpec{
PackageName: os.Getenv("REG_PKG_NAME"),
Expand Down
12 changes: 2 additions & 10 deletions test/tools/image-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,6 @@ metadata:
name: ${namespace}
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: ${namespace}
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ${namespace}-registry
Expand All @@ -52,8 +44,8 @@ spec:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: Issuer
name: olmv1-ca
kind: ClusterIssuer
group: cert-manager.io
---
apiVersion: apps/v1
Expand Down

0 comments on commit b128ce3

Please sign in to comment.