Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 83
✨ Certificate support for image registry#956
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -43,6 +43,25 @@ const ( | ||
| UpgradeConstraintPolicyIgnore UpgradeConstraintPolicy = "Ignore" | ||
| ) | ||
| // Similar to NamespacedName, but with json | ||
| type ClusterExtensionSecretRef struct { | ||
| // Name of the secret | ||
| Name string `json:"name"` | ||
| // Namespace of the secret | ||
| Namespace string `json:"namespace,omitempty"` | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we allow for specification of the namespace here? I'm wondering if using the install namespace as a point where all other resources referenced should exist is more reasonable?
| ||
| } | ||
| type ClusterExtensionTLS struct { | ||
| //+optional | ||
| // InsecureSkipTLSVerify allows the HTTPS client to ignore the server certificate | ||
| InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"` | ||
| // +optional | ||
| // CertificateSecretRef references a Secret that contains the tls.crt certificate that | ||
| // can verify the server certificate. | ||
| // This fits the definition of NamespacedName (but that doesn't have json tags) | ||
| CertificateSecretRef *ClusterExtensionSecretRef `json:"certificateSecretRef,omitempty"` | ||
| } | ||
| // ClusterExtensionSpec defines the desired state of ClusterExtension | ||
| type ClusterExtensionSpec struct { | ||
| //+kubebuilder:validation:MaxLength:=48 | ||
| @@ -78,6 +97,10 @@ type ClusterExtensionSpec struct { | ||
| // the bundle may contain resources that are cluster-scoped or that are | ||
| // installed in a different namespace. This namespace is expected to exist. | ||
| InstallNamespace string `json:"installNamespace"` | ||
| //+optional | ||
| // RegistryTLS defines the connection parameters to retrieve an image from a registry | ||
| RegistryTLS *ClusterExtensionTLS `json:"registryTLS,omitempty"` | ||
Comment on lines
+100
to
+103
| ||
| } | ||
| const ( | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -150,7 +150,6 @@ func main() { | ||
| setupLog.Error(err, "unable to start manager") | ||
| os.Exit(1) | ||
| } | ||
| httpClient, err := httputil.BuildHTTPClient(caCert) | ||
| if err != nil { | ||
| setupLog.Error(err, "unable to create catalogd http client") | ||
| @@ -210,6 +209,7 @@ func main() { | ||
| if err = (&controllers.ClusterExtensionReconciler{ | ||
| Client: cl, | ||
| Reader: mgr.GetAPIReader(), | ||
tmshort marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| BundleProvider: catalogClient, | ||
| ActionClientGetter: acg, | ||
| Unpacker: unpacker, | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -36,6 +36,7 @@ import ( | ||
| "helm.sh/helm/v3/pkg/postrender" | ||
| "helm.sh/helm/v3/pkg/release" | ||
| "helm.sh/helm/v3/pkg/storage/driver" | ||
| corev1 "k8s.io/api/core/v1" | ||
| "k8s.io/apimachinery/pkg/api/equality" | ||
| apimeta "k8s.io/apimachinery/pkg/api/meta" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| @@ -79,6 +80,7 @@ import ( | ||
| // ClusterExtensionReconciler reconciles a ClusterExtension object | ||
| type ClusterExtensionReconciler struct { | ||
| client.Client | ||
| client.Reader | ||
| BundleProvider BundleProvider | ||
| Unpacker rukpaksource.Unpacker | ||
| ActionClientGetter helmclient.ActionClientGetter | ||
| @@ -96,10 +98,6 @@ 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 := r.getCertificateData(ctx, ce) | ||
| 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, | ||
| @@ -550,21 +552,54 @@ func (r *ClusterExtensionReconciler) generateBundleDeploymentForUnpack(bundlePat | ||
| Image: &rukpakv1alpha2.ImageSource{ | ||
| Ref: bundlePath, | ||
| InsecureSkipTLSVerify: isInsecureSkipTLSVerifySet(ce), | ||
| CertificateData: certData, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
| func isInsecureSkipTLSVerifySet(ce *ocv1alpha1.ClusterExtension) bool { | ||
| if ce == nil { | ||
| if ce == nil || ce.Spec.RegistryTLS == nil { | ||
| return false | ||
| } | ||
| value, ok := ce.Annotations[bundleConnectionAnnotation] | ||
| if !ok { | ||
| return false | ||
| return ce.Spec.RegistryTLS.InsecureSkipTLSVerify | ||
| } | ||
| func (r *ClusterExtensionReconciler) getCertificateData(ctx context.Context, ce *ocv1alpha1.ClusterExtension) (string, error) { | ||
| if ce == nil || ce.Spec.RegistryTLS == nil || ce.Spec.RegistryTLS.CertificateSecretRef == nil { | ||
| return "", nil | ||
tmshort marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| secretName := getNamespacedName(*ce.Spec.RegistryTLS.CertificateSecretRef) | ||
| var secret = &corev1.Secret{} | ||
| if err := r.Reader.Get(ctx, secretName, secret); err != nil { | ||
| return "", fmt.Errorf("unable to get secret %v: %w", secretName, err) | ||
| } | ||
tmshort marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if secret.Type != corev1.SecretTypeTLS { | ||
| return "", fmt.Errorf("invalid type in secret %v: %v", secretName, secret.Type) | ||
| } | ||
| var certs []string | ||
| // Get any 'ca.crt' | ||
| data, ok := secret.Data[corev1.ServiceAccountRootCAKey] | ||
| if ok && len(data) > 0 { | ||
| certs = append(certs, string(data)) | ||
| } | ||
| // Get any 'tls.crt' | ||
| data, ok = secret.Data[corev1.TLSCertKey] | ||
| if ok && len(data) > 0 { | ||
| certs = append(certs, string(data)) | ||
| } | ||
| if len(certs) == 0 { | ||
| return "", fmt.Errorf("no data found in secret: %v", secretName) | ||
| } | ||
| return strings.Join(certs, "\n"), nil | ||
| } | ||
| func getNamespacedName(name ocv1alpha1.ClusterExtensionSecretRef) types.NamespacedName { | ||
| if name.Namespace == "" { | ||
| return types.NamespacedName{Namespace: "default", Name: name.Name} | ||
| } | ||
| return value == "true" | ||
| return types.NamespacedName{Namespace: name.Namespace, Name: name.Name} | ||
| } | ||
| // SetupWithManager sets up the controller with the Manager. | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we force this to be the install namespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually disagree, the registry and it's secret are likely in a separate namespace from the installation namespace.