diff --git a/Tiltfile b/Tiltfile index ef12a3042..a2c9b47c0 100644 --- a/Tiltfile +++ b/Tiltfile @@ -1,7 +1,7 @@ if not os.path.exists('../tilt-support'): fail('Please clone https://github.com/operator-framework/tilt-support to ../tilt-support') -load('../tilt-support/Tiltfile', 'deploy_repo') +load('../tilt-support/Tiltfile', 'deploy_repo', 'process_yaml') config.define_string_list('repos', args=True) cfg = config.parse() @@ -16,6 +16,8 @@ repo = { 'starting_debug_port': 30000, } +process_yaml("testdata/certs/issuers.yaml") + for r in repos: if r == 'operator-controller': deploy_repo('operator-controller', repo) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index a91a51b86..c6e7d3353 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -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-certs-dir", "", "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.") @@ -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") } @@ -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) diff --git a/config/overlays/tls/kustomization.yaml b/config/overlays/tls/kustomization.yaml index 9d8517a68..e7c746a95 100644 --- a/config/overlays/tls/kustomization.yaml +++ b/config/overlays/tls/kustomization.yaml @@ -12,9 +12,11 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../../base +- resources/manager_cert.yaml patches: - target: kind: Deployment name: controller-manager - path: patches/manager_deployment_cert.yaml \ No newline at end of file + path: patches/manager_deployment_cert.yaml +- path: patches/manager_cert_patch.yaml diff --git a/config/overlays/tls/patches/manager_cert_patch.yaml b/config/overlays/tls/patches/manager_cert_patch.yaml new file mode 100644 index 000000000..959d53a9a --- /dev/null +++ b/config/overlays/tls/patches/manager_cert_patch.yaml @@ -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 diff --git a/config/overlays/tls/patches/manager_deployment_cert.yaml b/config/overlays/tls/patches/manager_deployment_cert.yaml index 72615bcd5..94df488c6 100644 --- a/config/overlays/tls/patches/manager_deployment_cert.yaml +++ b/config/overlays/tls/patches/manager_deployment_cert.yaml @@ -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" \ No newline at end of file + value: "--ca-certs-dir=/var/certs" diff --git a/config/overlays/tls/resources/manager_cert.yaml b/config/overlays/tls/resources/manager_cert.yaml new file mode 100644 index 000000000..a7a19f4dd --- /dev/null +++ b/config/overlays/tls/resources/manager_cert.yaml @@ -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 diff --git a/internal/controllers/clusterextension_controller.go b/internal/controllers/clusterextension_controller.go index b7572fe89..4051dde71 100644 --- a/internal/controllers/clusterextension_controller.go +++ b/internal/controllers/clusterextension_controller.go @@ -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" ) @@ -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 @@ -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()) @@ -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, @@ -548,25 +550,14 @@ func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(bundlePat Source: rukpakv1alpha2.BundleSource{ Type: rukpakv1alpha2.SourceTypeImage, Image: &rukpakv1alpha2.ImageSource{ - Ref: bundlePath, - InsecureSkipTLSVerify: isInsecureSkipTLSVerifySet(ce), + Ref: bundlePath, + 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). diff --git a/internal/httputil/httputil.go b/internal/httputil/httputil.go index dde765f0a..8ee0cb852 100644 --- a/internal/httputil/httputil.go +++ b/internal/httputil/httputil.go @@ -5,30 +5,57 @@ 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) - if err != nil { - return nil, err - } - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(cert) - tlsConfig := &tls.Config{ - RootCAs: caCertPool, - MinVersion: tls.VersionTLS12, + certs := []string{} + dirEntries, err := os.ReadDir(caDir) + if err != nil { + return "", err + } + for _, e := range dirEntries { + if e.IsDir() { + continue } - tlsTransport := &http.Transport{ - TLSClientConfig: tlsConfig, + data, err := os.ReadFile(filepath.Join(caDir, e.Name())) + if err != nil { + return "", err } - httpClient.Transport = tlsTransport + certs = append(certs, string(data)) + } + 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 } diff --git a/scripts/install.tpl.sh b/scripts/install.tpl.sh index 1b44ac630..9af36c32c 100644 --- a/scripts/install.tpl.sh +++ b/scripts/install.tpl.sh @@ -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 a self-signed ClusterIssuer +kubectl apply -f - <