diff --git a/cmd/install/install.go b/cmd/install/install.go index 36d89c9177f..7523db3c505 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -37,14 +37,18 @@ import ( crclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + configapi "github.com/openshift/api/config/v1" imageapi "github.com/openshift/api/image/v1" hyperapi "github.com/openshift/hypershift/api" hyperv1 "github.com/openshift/hypershift/api/v1beta1" "github.com/openshift/hypershift/cmd/install/assets" + "github.com/openshift/hypershift/cmd/log" "github.com/openshift/hypershift/cmd/util" "github.com/openshift/hypershift/cmd/version" + "github.com/openshift/hypershift/support/capabilities" "github.com/openshift/hypershift/support/metrics" "github.com/openshift/hypershift/support/rhobsmonitoring" + "github.com/openshift/hypershift/vendor/k8s.io/client-go/discovery" ) const ( @@ -368,6 +372,11 @@ func fetchImageRefs(file string) (map[string]string, error) { } func hyperShiftOperatorManifests(opts Options) ([]crclient.Object, error) { + mgmtClusterCaps, err := getMgmtClusterCapabilities() + if err != nil { + return nil, fmt.Errorf("unable to detect management cluster capabilities: %w", err) + } + var objects []crclient.Object var images map[string]string @@ -480,6 +489,42 @@ func hyperShiftOperatorManifests(opts Options) ([]crclient.Object, error) { objects = append(objects, userCABundleCM) } + if mgmtClusterCaps.Has(capabilities.CapabilityImage) && mgmtClusterCaps.Has(capabilities.CapabilityProxy) { + if imageRegistryCABundle := getImageRegistryCABundle(); imageRegistryCABundle != "" { + // Create a new configmap containing all the image registry CA certificates as a bundle + // Proxy expects the configmap to: + // 1. contain only "ca-bundle.crt" as the key + // 2. exist in the openshift-config namespace + imageRegistryCABundle := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "user-image-registry-ca-bundle", + Namespace: "openshift-config", + }, + Data: map[string]string{ + "ca-bundle.crt": imageRegistryCABundle, + }, + } + objects = append(objects, imageRegistryCABundle) + + // Adding the configmap containing the bundle to the proxy object + // will allow the openshift-network-operator to automatically + // inject this bundle into the openshift-config-managed-trusted-ca-bundle configmap (created below) + // Reference: https://docs.openshift.com/container-platform/4.12/security/certificates/updating-ca-bundle.html + proxy := &configapi.Proxy{ + ObjectMeta: metav1.ObjectMeta{Name: "cluster"}, + Spec: configapi.ProxySpec{ + TrustedCA: configapi.ConfigMapNameReference{ + Name: imageRegistryCABundle.Name, + }, + }, + } + objects = append(objects, proxy) + } + } else { + log.Log.Info(`Skipping adding image registry CA bundle to Proxy. + Either image.config.openshift.io or proxy.config.openshift.io is not defined in the management cluster`) + } + trustedCABundle := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: "hypershift", @@ -684,3 +729,39 @@ func hyperShiftOperatorManifests(opts Options) ([]crclient.Object, error) { return objects, nil } + +// getMgmtClusterCapabilities initializes the ManagementClusterCabilities object +// to determine if the management cluster has the listed capabilities +func getMgmtClusterCapabilities() (*capabilities.ManagementClusterCapabilities, error) { + config, err := util.GetConfig() + if err != nil { + return nil, fmt.Errorf("unable to get kubernetes config: %w", err) + } + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + return nil, fmt.Errorf("unable to get discovery client: %w", err) + } + return capabilities.DetectManagementClusterCapabilities(discoveryClient) +} + +// getImageRegistryCABundle retrieves the image registry CAs listed under image.config.openshift.io +// and merges them into one CA bundle +func getImageRegistryCABundle() string { + if client, err := util.GetClient(); err == nil { + img := &configapi.Image{} + if err := client.Get(context.Background(), types.NamespacedName{Name: "cluster"}, img); err == nil && img != nil && img.Spec.AdditionalTrustedCA.Name != "" { + configmap := &corev1.ConfigMap{} + if err := client.Get(context.Background(), types.NamespacedName{Name: img.Spec.AdditionalTrustedCA.Name, Namespace: "openshift-config"}, configmap); err == nil && configmap != nil { + // Merge all registry CA certificates together into one bundle + var buf bytes.Buffer + for _, crt := range configmap.Data { + buf.WriteString(crt) + } + if buf.Len() > 0 { + return buf.String() + } + } + } + } + return "" +} diff --git a/support/capabilities/management_cluster_capabilities.go b/support/capabilities/management_cluster_capabilities.go index d281100e4f4..21b75454141 100644 --- a/support/capabilities/management_cluster_capabilities.go +++ b/support/capabilities/management_cluster_capabilities.go @@ -27,6 +27,10 @@ const ( // supports security context constraints CapabilitySecurityContextConstraint + // CapabilityImage indicates if the cluster supports the + // image.config.openshift.io api + CapabilityImage + // CapabilityInfrastructure indicates if the cluster supports the // infrastructures.config.openshift.io api CapabilityInfrastructure