diff --git a/api/v1beta1/spec.go b/api/v1beta1/spec.go index fd319c88..de0074e0 100644 --- a/api/v1beta1/spec.go +++ b/api/v1beta1/spec.go @@ -718,6 +718,18 @@ type RemoteKustomizeURL struct { // "caFile" — PEM-encoded CA certificate for TLS verification // +optional SecretRef *corev1.SecretReference `json:"secretRef,omitempty"` + + // InsecureSkipTLSVerify controls server certificate verification. + // Ignored if the referenced SecretRef provides a "caFile". + // +kubebuilder:default:=false + // +optional + InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"` + + // PlainHTTP indicates to use insecure HTTP connections when URL uses the + // "oci://" scheme. Ignored for "http://"/"https://" URLs. + // +kubebuilder:default:=false + // +optional + PlainHTTP bool `json:"plainHTTP,omitempty"` } // StopMatchingBehavior indicates what will happen when Cluster stops matching @@ -893,6 +905,18 @@ type RemoteURL struct { // on a ConfigMap or Secret. // +optional Template bool `json:"template,omitempty"` + + // InsecureSkipTLSVerify controls server certificate verification. + // Ignored if the referenced SecretRef provides a "caFile". + // +kubebuilder:default:=false + // +optional + InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"` + + // PlainHTTP indicates to use insecure HTTP connections when URL uses the + // "oci://" scheme. Ignored for "http://"/"https://" URLs. + // +kubebuilder:default:=false + // +optional + PlainHTTP bool `json:"plainHTTP,omitempty"` } type Clusters struct { diff --git a/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml b/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml index ee597a98..de8fe042 100644 --- a/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml +++ b/config/crd/bases/config.projectsveltos.io_clusterprofiles.yaml @@ -829,11 +829,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1184,11 +1196,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml b/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml index 7a8836c7..743cf533 100644 --- a/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml +++ b/config/crd/bases/config.projectsveltos.io_clusterpromotions.yaml @@ -731,11 +731,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1086,11 +1098,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -2536,11 +2560,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -2959,11 +2995,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml index 175e9265..63b7e16c 100644 --- a/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml +++ b/config/crd/bases/config.projectsveltos.io_clustersummaries.yaml @@ -868,11 +868,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1223,11 +1235,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/config/crd/bases/config.projectsveltos.io_profiles.yaml b/config/crd/bases/config.projectsveltos.io_profiles.yaml index 34f26eb4..0fe91b4d 100644 --- a/config/crd/bases/config.projectsveltos.io_profiles.yaml +++ b/config/crd/bases/config.projectsveltos.io_profiles.yaml @@ -829,11 +829,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1184,11 +1196,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/controllers/handlers_kustomize.go b/controllers/handlers_kustomize.go index 145bdf71..9e5e823a 100644 --- a/controllers/handlers_kustomize.go +++ b/controllers/handlers_kustomize.go @@ -488,7 +488,12 @@ func getHashFromKustomizationRef(ctx context.Context, c client.Client, clusterSu func getHashFromRemoteKustomizeURL(ctx context.Context, remoteURL *configv1beta1.RemoteKustomizeURL, clusterSummary *configv1beta1.ClusterSummary, logger logr.Logger) ([]byte, error) { - body, err := fetchContentForHash(ctx, remoteURL.URL, remoteURL.SecretRef, + opts := remoteFetchOptions{ + secretRef: remoteURL.SecretRef, + insecureSkipTLSVerify: remoteURL.InsecureSkipTLSVerify, + plainHTTP: remoteURL.PlainHTTP, + } + body, err := fetchContentForHash(ctx, remoteURL.URL, opts, clusterSummary.Spec.ClusterNamespace, clusterSummary.Spec.ClusterName, clusterSummary.Spec.ClusterType, logger) if err != nil { @@ -860,7 +865,12 @@ func prepareFileSystemWithRemoteURL(ctx context.Context, kustomizationRef *confi return "", fmt.Errorf("tmp dir error: %w", err) } - err = fetchContentToDir(ctx, kustomizationRef.RemoteURL.URL, kustomizationRef.RemoteURL.SecretRef, + opts := remoteFetchOptions{ + secretRef: kustomizationRef.RemoteURL.SecretRef, + insecureSkipTLSVerify: kustomizationRef.RemoteURL.InsecureSkipTLSVerify, + plainHTTP: kustomizationRef.RemoteURL.PlainHTTP, + } + err = fetchContentToDir(ctx, kustomizationRef.RemoteURL.URL, opts, clusterSummary.Spec.ClusterNamespace, clusterSummary.Spec.ClusterName, clusterSummary.Spec.ClusterType, tmpDir, logger) if err != nil { diff --git a/controllers/handlers_resources.go b/controllers/handlers_resources.go index 8679af44..4eff46cd 100644 --- a/controllers/handlers_resources.go +++ b/controllers/handlers_resources.go @@ -630,7 +630,12 @@ func urlPolicyRefsHash(ctx context.Context, clusterSummary *configv1beta1.Cluste if ref.RemoteURL == nil { continue } - body, err := fetchContent(ctx, ref.RemoteURL.URL, ref.RemoteURL.SecretRef, + opts := remoteFetchOptions{ + secretRef: ref.RemoteURL.SecretRef, + insecureSkipTLSVerify: ref.RemoteURL.InsecureSkipTLSVerify, + plainHTTP: ref.RemoteURL.PlainHTTP, + } + body, err := fetchContent(ctx, ref.RemoteURL.URL, opts, clusterSummary.Spec.ClusterNamespace, clusterSummary.Spec.ClusterName, clusterSummary.Spec.ClusterType, logger) if err != nil { diff --git a/controllers/handlers_utils.go b/controllers/handlers_utils.go index f793bc7b..e75e1c2d 100644 --- a/controllers/handlers_utils.go +++ b/controllers/handlers_utils.go @@ -78,9 +78,11 @@ type referencedObject struct { Optional bool Path string // URL and related fields are set only for URL-based PolicyRefs (Kind == urlSourceKind). - URL string - IsTemplate bool - SecretRef *corev1.SecretReference + URL string + IsTemplate bool + SecretRef *corev1.SecretReference + InsecureSkipTLSVerify bool + PlainHTTP bool } func getClusterSummaryAnnotationValue(clusterSummary *configv1beta1.ClusterSummary) string { @@ -787,6 +789,8 @@ func collectReferencedObjects(references []configv1beta1.PolicyRef) (local, remo object.URL = reference.RemoteURL.URL object.IsTemplate = reference.RemoteURL.Template object.SecretRef = reference.RemoteURL.SecretRef + object.InsecureSkipTLSVerify = reference.RemoteURL.InsecureSkipTLSVerify + object.PlainHTTP = reference.RemoteURL.PlainHTTP setCommonReferencedObjectFields(&object, reference) if reference.DeploymentType == configv1beta1.DeploymentTypeLocal { diff --git a/controllers/suite_helpers_test.go b/controllers/suite_helpers_test.go index 9d2046f6..8c7852a6 100644 --- a/controllers/suite_helpers_test.go +++ b/controllers/suite_helpers_test.go @@ -45,26 +45,35 @@ const ( kubeconfigPostfix = "-kubeconfig" ) -// addOwnerReference adds owner as OwnerReference of obj +// addOwnerReference adds owner as OwnerReference of obj. +// This method can be invoked by different tests in parallel, all touching the same obj +// (e.g. a shared ClusterConfiguration), so the get-modify-update is wrapped in a retry: +// otherwise a conflicting write between the Get and the Update below would fail the +// calling spec outright instead of retrying against the latest version. func addOwnerReference(ctx context.Context, c client.Client, obj, owner client.Object) { Expect(addTypeInformationToObject(testEnv.Scheme(), owner)).To(Succeed()) objCopy := obj.DeepCopyObject().(client.Object) key := client.ObjectKeyFromObject(obj) - Expect(c.Get(ctx, key, objCopy)).To(Succeed()) - refs := objCopy.GetOwnerReferences() - if refs == nil { - refs = make([]metav1.OwnerReference, 0) - } - refs = append(refs, - metav1.OwnerReference{ - UID: owner.GetUID(), - Name: owner.GetName(), - Kind: owner.GetObjectKind().GroupVersionKind().Kind, - APIVersion: owner.GetObjectKind().GroupVersionKind().GroupVersion().String(), - }) - objCopy.SetOwnerReferences(refs) - Expect(c.Update(ctx, objCopy)).To(Succeed()) + err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + if err := c.Get(ctx, key, objCopy); err != nil { + return err + } + refs := objCopy.GetOwnerReferences() + if refs == nil { + refs = make([]metav1.OwnerReference, 0) + } + refs = append(refs, + metav1.OwnerReference{ + UID: owner.GetUID(), + Name: owner.GetName(), + Kind: owner.GetObjectKind().GroupVersionKind().Kind, + APIVersion: owner.GetObjectKind().GroupVersionKind().GroupVersion().String(), + }) + objCopy.SetOwnerReferences(refs) + return c.Update(ctx, objCopy) + }) + Expect(err).To(Succeed()) } // waitForObject waits for the cache to be updated helps in preventing test flakes due to the cache sync delays. diff --git a/controllers/url_source.go b/controllers/url_source.go index 92ebda3e..3fd80d40 100644 --- a/controllers/url_source.go +++ b/controllers/url_source.go @@ -54,12 +54,26 @@ const ( defaultURLInterval = 5 * time.Minute ) +// remoteFetchOptions groups the options shared by every function that fetches +// content from a RemoteURL/RemoteKustomizeURL source. +type remoteFetchOptions struct { + // secretRef references a Secret containing optional auth credentials + // (keys: "token", "username"+"password", "caFile"). + secretRef *corev1.SecretReference + // insecureSkipTLSVerify disables server certificate verification. Ignored + // if secretRef provides a "caFile". + insecureSkipTLSVerify bool + // plainHTTP uses an insecure HTTP connection instead of HTTPS when + // fetching from an OCI registry ("oci://" scheme). Ignored otherwise. + plainHTTP bool +} + // fetchURL retrieves the raw content from rawURL. -// If secretRef is non-nil, the referenced Secret is read for optional auth +// If opts.secretRef is non-nil, the referenced Secret is read for optional auth // credentials (keys: "token", "username"+"password", "caFile"). // The secretRef Name and Namespace support Go templating against the target cluster. // When Namespace is empty it defaults to clusterNamespace. -func fetchURL(ctx context.Context, rawURL string, secretRef *corev1.SecretReference, +func fetchURL(ctx context.Context, rawURL string, opts remoteFetchOptions, clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, logger logr.Logger) ([]byte, error) { @@ -69,7 +83,8 @@ func fetchURL(ctx context.Context, rawURL string, secretRef *corev1.SecretRefere return nil, fmt.Errorf("failed to build HTTP request for %s: %w", rawURL, err) } - if secretRef != nil { + var caPool *x509.CertPool + if secretRef := opts.secretRef; secretRef != nil { ns, err := libsveltostemplate.GetReferenceResourceNamespace(ctx, getManagementClusterClient(), clusterNamespace, clusterName, secretRef.Namespace, clusterType) if err != nil { @@ -96,14 +111,22 @@ func fetchURL(ctx context.Context, rawURL string, secretRef *corev1.SecretRefere } if caFile, ok := secret.Data["caFile"]; ok { - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(caFile) { + caPool = x509.NewCertPool() + if !caPool.AppendCertsFromPEM(caFile) { return nil, fmt.Errorf("failed to parse caFile from secret %s/%s", ns, name) } - transport = &http.Transport{ - TLSClientConfig: &tls.Config{RootCAs: pool}, - } + } + } + + switch { + case caPool != nil: + transport = &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: caPool}, + } + case opts.insecureSkipTLSVerify: + transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // explicit opt-in via RemoteURL.InsecureSkipTLSVerify } } @@ -141,15 +164,15 @@ func fetchURL(ctx context.Context, rawURL string, secretRef *corev1.SecretRefere // The secretRef Name and Namespace are treated as Go templates and instantiated // against the target cluster, following the same convention as PolicyRef ConfigMap/Secret // references. When Namespace is empty it defaults to clusterNamespace. -func fetchContent(ctx context.Context, rawURL string, secretRef *corev1.SecretReference, +func fetchContent(ctx context.Context, rawURL string, opts remoteFetchOptions, clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, logger logr.Logger) ([]byte, error) { if strings.HasPrefix(rawURL, "oci://") { - return fetchOCI(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + return fetchOCI(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) } - body, err := fetchURL(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + body, err := fetchURL(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) if err != nil { return nil, err } @@ -166,11 +189,11 @@ func fetchContent(ctx context.Context, rawURL string, secretRef *corev1.SecretRe // exchanged for a registry token), "caFile" (PEM CA for TLS verification). // The secretRef Name and Namespace support Go templating against the target cluster. // When Namespace is empty it defaults to clusterNamespace. -func fetchOCI(ctx context.Context, rawURL string, secretRef *corev1.SecretReference, +func fetchOCI(ctx context.Context, rawURL string, opts remoteFetchOptions, clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, logger logr.Logger) ([]byte, error) { - layers, err := pullOCIArtifactLayers(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + layers, err := pullOCIArtifactLayers(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) if err != nil { return nil, err } @@ -187,26 +210,22 @@ func fetchOCI(ctx context.Context, rawURL string, secretRef *corev1.SecretRefere return buf.Bytes(), nil } -// pullOCIArtifactLayers connects to the OCI registry referenced by rawURL (scheme "oci://") -// and returns the raw bytes of every layer in the artifact's manifest, in order. +// buildOCIAuthClient resolves opts.secretRef (if set) into an oras auth client carrying +// registry credentials and, if the Secret provides a "caFile", a custom CA pool. When no +// caFile is present, opts.insecureSkipTLSVerify is applied as a fallback so the caller can +// still connect to a registry with a self-signed certificate. // Supported Secret keys: "token" (pre-obtained bearer token), "username"+"password" (basic auth // exchanged for a registry token), "caFile" (PEM CA for TLS verification). // The secretRef Name and Namespace support Go templating against the target cluster. -// When Namespace is empty it defaults to clusterNamespace. -func pullOCIArtifactLayers(ctx context.Context, rawURL string, secretRef *corev1.SecretReference, - clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, - logger logr.Logger) ([][]byte, error) { - - ref := strings.TrimPrefix(rawURL, "oci://") - - repo, err := orasremote.NewRepository(ref) - if err != nil { - return nil, fmt.Errorf("failed to parse OCI reference %s: %w", rawURL, err) - } +// When Namespace is empty it defaults to clusterNamespace. registryHost scopes the resolved +// credential, matching the OCI reference's registry (e.g. repo.Reference.Registry). +func buildOCIAuthClient(ctx context.Context, rawURL, registryHost string, opts remoteFetchOptions, + clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType) (*orasauth.Client, error) { authClient := &orasauth.Client{} - if secretRef != nil { + var caPool *x509.CertPool + if secretRef := opts.secretRef; secretRef != nil { ns, err := libsveltostemplate.GetReferenceResourceNamespace(ctx, getManagementClusterClient(), clusterNamespace, clusterName, secretRef.Namespace, clusterType) if err != nil { @@ -235,22 +254,61 @@ func pullOCIArtifactLayers(ctx context.Context, rawURL string, secretRef *corev1 } } } - authClient.Credential = orasauth.StaticCredential(repo.Reference.Registry, cred) + authClient.Credential = orasauth.StaticCredential(registryHost, cred) if caFile, ok := secret.Data["caFile"]; ok { - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(caFile) { + caPool = x509.NewCertPool() + if !caPool.AppendCertsFromPEM(caFile) { return nil, fmt.Errorf("failed to parse caFile from secret %s/%s", ns, name) } - authClient.Client = &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{RootCAs: pool}, - }, - } } } - repo.Client = authClient + switch { + case caPool != nil: + authClient.Client = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: caPool}, + }, + } + case opts.insecureSkipTLSVerify: + authClient.Client = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // explicit opt-in via RemoteURL.InsecureSkipTLSVerify + }, + } + } + + return authClient, nil +} + +// pullOCIArtifactLayers connects to the OCI registry referenced by rawURL (scheme "oci://") +// and returns the raw bytes of every layer in the artifact's manifest, in order. +// Supported Secret keys: "token" (pre-obtained bearer token), "username"+"password" (basic auth +// exchanged for a registry token), "caFile" (PEM CA for TLS verification). +// The secretRef Name and Namespace support Go templating against the target cluster. +// When Namespace is empty it defaults to clusterNamespace. +// opts.plainHTTP connects to the registry over plain HTTP instead of HTTPS, and +// opts.insecureSkipTLSVerify disables server certificate verification (ignored if +// the referenced Secret provides a "caFile"); oras-go otherwise requires HTTPS with +// a verified certificate. +func pullOCIArtifactLayers(ctx context.Context, rawURL string, opts remoteFetchOptions, + clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, + logger logr.Logger) ([][]byte, error) { + + ref := strings.TrimPrefix(rawURL, "oci://") + + repo, err := orasremote.NewRepository(ref) + if err != nil { + return nil, fmt.Errorf("failed to parse OCI reference %s: %w", rawURL, err) + } + repo.PlainHTTP = opts.plainHTTP + + repo.Client, err = buildOCIAuthClient(ctx, rawURL, repo.Reference.Registry, opts, + clusterNamespace, clusterName, clusterType) + if err != nil { + return nil, err + } logger.V(logs.LogDebug).Info(fmt.Sprintf("pulling OCI artifact %s", rawURL)) @@ -403,7 +461,12 @@ func deployContentOfURL(ctx context.Context, deployingToMgmtCluster bool, destCo destClient client.Client, ref *referencedObject, dCtx *deploymentContext, logger logr.Logger) ([]libsveltosv1beta1.ResourceReport, error) { - body, err := fetchContent(ctx, ref.URL, ref.SecretRef, + opts := remoteFetchOptions{ + secretRef: ref.SecretRef, + insecureSkipTLSVerify: ref.InsecureSkipTLSVerify, + plainHTTP: ref.PlainHTTP, + } + body, err := fetchContent(ctx, ref.URL, opts, dCtx.clusterSummary.Spec.ClusterNamespace, dCtx.clusterSummary.Spec.ClusterName, dCtx.clusterSummary.Spec.ClusterType, logger) if err != nil { @@ -483,12 +546,12 @@ func minKustomizeURLInterval(refs []configv1beta1.KustomizationRef) time.Duratio // It dispatches on the URL scheme: "oci://" pulls and concatenates every OCI layer; // everything else is fetched via fetchURL as a single archive. Either a gzip-compressed // or an uncompressed tar is accepted (see extractTarGz). -func fetchContentToDir(ctx context.Context, rawURL string, secretRef *corev1.SecretReference, +func fetchContentToDir(ctx context.Context, rawURL string, opts remoteFetchOptions, clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, destDir string, logger logr.Logger) error { if strings.HasPrefix(rawURL, "oci://") { - layers, err := pullOCIArtifactLayers(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + layers, err := pullOCIArtifactLayers(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) if err != nil { return err } @@ -500,7 +563,7 @@ func fetchContentToDir(ctx context.Context, rawURL string, secretRef *corev1.Sec return nil } - body, err := fetchURL(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + body, err := fetchURL(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) if err != nil { return err } @@ -512,12 +575,12 @@ func fetchContentToDir(ctx context.Context, rawURL string, secretRef *corev1.Sec // response body — without extracting anything to disk. Hashing these bytes (rather than // re-deriving a separate representation of the content) keeps drift-detection hashes for // KustomizationRef.RemoteURL in sync with exactly what fetchContentToDir will deploy. -func fetchContentForHash(ctx context.Context, rawURL string, secretRef *corev1.SecretReference, +func fetchContentForHash(ctx context.Context, rawURL string, opts remoteFetchOptions, clusterNamespace, clusterName string, clusterType libsveltosv1beta1.ClusterType, logger logr.Logger) ([]byte, error) { if strings.HasPrefix(rawURL, "oci://") { - layers, err := pullOCIArtifactLayers(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + layers, err := pullOCIArtifactLayers(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) if err != nil { return nil, err } @@ -528,7 +591,7 @@ func fetchContentForHash(ctx context.Context, rawURL string, secretRef *corev1.S return buf.Bytes(), nil } - return fetchURL(ctx, rawURL, secretRef, clusterNamespace, clusterName, clusterType, logger) + return fetchURL(ctx, rawURL, opts, clusterNamespace, clusterName, clusterType, logger) } // extractTarGzBytes writes a gzipped tarball's raw bytes to a temporary file inside diff --git a/controllers/url_source_test.go b/controllers/url_source_test.go index 1fd80d2e..4e63a877 100644 --- a/controllers/url_source_test.go +++ b/controllers/url_source_test.go @@ -21,11 +21,21 @@ import ( "bytes" "compress/gzip" "context" + "crypto/tls" + "fmt" "net/http" "net/http/httptest" + "strings" + "github.com/google/go-containerregistry/pkg/registry" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "oras.land/oras-go/v2" + "oras.land/oras-go/v2/content" + "oras.land/oras-go/v2/content/memory" + orasremote "oras.land/oras-go/v2/registry/remote" + orasauth "oras.land/oras-go/v2/registry/remote/auth" "github.com/go-logr/logr" @@ -202,7 +212,7 @@ var _ = Describe("fetchContent (http/https)", func() { url, cleanup := serve(raw) defer cleanup() - got, err := fetchContent(context.TODO(), url, nil, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + got, err := fetchContent(context.TODO(), url, remoteFetchOptions{}, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) Expect(err).ToNot(HaveOccurred()) Expect(got).To(Equal(raw)) }) @@ -212,7 +222,7 @@ var _ = Describe("fetchContent (http/https)", func() { url, cleanup := serve(gzipBytes(raw)) defer cleanup() - got, err := fetchContent(context.TODO(), url, nil, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + got, err := fetchContent(context.TODO(), url, remoteFetchOptions{}, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) Expect(err).ToNot(HaveOccurred()) Expect(got).To(Equal(raw)) }) @@ -226,7 +236,7 @@ var _ = Describe("fetchContent (http/https)", func() { url, cleanup := serve(archive) defer cleanup() - got, err := fetchContent(context.TODO(), url, nil, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + got, err := fetchContent(context.TODO(), url, remoteFetchOptions{}, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) Expect(err).ToNot(HaveOccurred()) s := string(got) Expect(s).To(ContainSubstring(dep)) @@ -239,8 +249,132 @@ var _ = Describe("fetchContent (http/https)", func() { url, cleanup := serve(archive) defer cleanup() - got, err := fetchContent(context.TODO(), url, nil, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + got, err := fetchContent(context.TODO(), url, remoteFetchOptions{}, "", "", libsveltosv1beta1.ClusterTypeCapi, logger) Expect(err).ToNot(HaveOccurred()) Expect(string(got)).To(ContainSubstring(svc)) }) }) + +var _ = Describe("pullOCIArtifactLayers (plain HTTP)", func() { + var logger logr.Logger + + BeforeEach(func() { + logger = logr.Discard() + }) + + // servePlainOCIRegistry starts an in-process, plain-HTTP OCI registry + // (github.com/google/go-containerregistry/pkg/registry) seeded with a + // single-layer artifact tagged tag, and returns the "oci://" reference to + // pull it back alongside a cleanup func. + servePlainOCIRegistry := func(tag string, layerContent []byte) (string, func()) { + server := httptest.NewServer(registry.New()) + + store := memory.New() + layerDesc := content.NewDescriptorFromBytes("application/vnd.projectsveltos.yaml", layerContent) + Expect(store.Push(context.TODO(), layerDesc, bytes.NewReader(layerContent))).To(Succeed()) + + manifestDesc, err := oras.PackManifest(context.TODO(), store, oras.PackManifestVersion1_1, + "application/vnd.projectsveltos.manifest", oras.PackManifestOptions{Layers: []ocispec.Descriptor{layerDesc}}) + Expect(err).ToNot(HaveOccurred()) + Expect(store.Tag(context.TODO(), manifestDesc, tag)).To(Succeed()) + + host := strings.TrimPrefix(server.URL, "http://") + repoRef := fmt.Sprintf("%s/unit/plainhttp:%s", host, tag) + + remoteRepo, err := orasremote.NewRepository(repoRef) + Expect(err).ToNot(HaveOccurred()) + remoteRepo.PlainHTTP = true + + _, err = oras.Copy(context.TODO(), store, tag, remoteRepo, tag, oras.CopyOptions{}) + Expect(err).ToNot(HaveOccurred()) + + return "oci://" + repoRef, server.Close + } + + It("pulls layers when PlainHTTP is set", func() { + layerContent := []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: oci-plain-http\n") + ociURL, cleanup := servePlainOCIRegistry("v1", layerContent) + defer cleanup() + + got, err := fetchOCI(context.TODO(), ociURL, remoteFetchOptions{plainHTTP: true}, + "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(layerContent)) + }) + + It("fails without PlainHTTP against a plain-HTTP registry", func() { + layerContent := []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: oci-plain-http\n") + ociURL, cleanup := servePlainOCIRegistry("v1", layerContent) + defer cleanup() + + _, err := fetchOCI(context.TODO(), ociURL, remoteFetchOptions{}, + "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + Expect(err).To(HaveOccurred()) + }) +}) + +var _ = Describe("pullOCIArtifactLayers (self-signed TLS)", func() { + var logger logr.Logger + + BeforeEach(func() { + logger = logr.Discard() + }) + + // serveSelfSignedOCIRegistry starts an in-process OCI registry behind a + // self-signed TLS certificate (httptest.NewTLSServer) seeded with a + // single-layer artifact tagged tag, and returns the "oci://" reference to + // pull it back alongside a cleanup func. Seeding itself skips certificate + // verification, same as a caFile-less client with InsecureSkipTLSVerify set + // would need to, since the server's cert is not signed by a known CA. + serveSelfSignedOCIRegistry := func(tag string, layerContent []byte) (string, func()) { + server := httptest.NewTLSServer(registry.New()) + + store := memory.New() + layerDesc := content.NewDescriptorFromBytes("application/vnd.projectsveltos.yaml", layerContent) + Expect(store.Push(context.TODO(), layerDesc, bytes.NewReader(layerContent))).To(Succeed()) + + manifestDesc, err := oras.PackManifest(context.TODO(), store, oras.PackManifestVersion1_1, + "application/vnd.projectsveltos.manifest", oras.PackManifestOptions{Layers: []ocispec.Descriptor{layerDesc}}) + Expect(err).ToNot(HaveOccurred()) + Expect(store.Tag(context.TODO(), manifestDesc, tag)).To(Succeed()) + + host := strings.TrimPrefix(server.URL, "https://") + repoRef := fmt.Sprintf("%s/unit/selfsigned:%s", host, tag) + + remoteRepo, err := orasremote.NewRepository(repoRef) + Expect(err).ToNot(HaveOccurred()) + remoteRepo.Client = &orasauth.Client{ + Client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // test-only, seeding a self-signed test registry + }, + }, + } + + _, err = oras.Copy(context.TODO(), store, tag, remoteRepo, tag, oras.CopyOptions{}) + Expect(err).ToNot(HaveOccurred()) + + return "oci://" + repoRef, server.Close + } + + It("pulls layers when InsecureSkipTLSVerify is set and no caFile is provided", func() { + layerContent := []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: oci-self-signed\n") + ociURL, cleanup := serveSelfSignedOCIRegistry("v1", layerContent) + defer cleanup() + + got, err := fetchOCI(context.TODO(), ociURL, remoteFetchOptions{insecureSkipTLSVerify: true}, + "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + Expect(err).ToNot(HaveOccurred()) + Expect(got).To(Equal(layerContent)) + }) + + It("fails without InsecureSkipTLSVerify against a self-signed TLS registry", func() { + layerContent := []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: oci-self-signed\n") + ociURL, cleanup := serveSelfSignedOCIRegistry("v1", layerContent) + defer cleanup() + + _, err := fetchOCI(context.TODO(), ociURL, remoteFetchOptions{}, + "", "", libsveltosv1beta1.ClusterTypeCapi, logger) + Expect(err).To(HaveOccurred()) + }) +}) diff --git a/lib/crd/clusterprofiles.go b/lib/crd/clusterprofiles.go index 9e585baa..c09d5a66 100644 --- a/lib/crd/clusterprofiles.go +++ b/lib/crd/clusterprofiles.go @@ -848,11 +848,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1203,11 +1215,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/lib/crd/clusterpromotions.go b/lib/crd/clusterpromotions.go index dff1a41a..d873480a 100644 --- a/lib/crd/clusterpromotions.go +++ b/lib/crd/clusterpromotions.go @@ -750,11 +750,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1105,11 +1117,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -2555,11 +2579,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -2978,11 +3014,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/lib/crd/clustersummaries.go b/lib/crd/clustersummaries.go index e8ffb670..504cf506 100644 --- a/lib/crd/clustersummaries.go +++ b/lib/crd/clustersummaries.go @@ -887,11 +887,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1242,11 +1254,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/lib/crd/profiles.go b/lib/crd/profiles.go index e9226bfc..1960cb71 100644 --- a/lib/crd/profiles.go +++ b/lib/crd/profiles.go @@ -848,11 +848,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1203,11 +1215,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional diff --git a/manifest/manifest.yaml b/manifest/manifest.yaml index 55eaa04d..a1fa906c 100644 --- a/manifest/manifest.yaml +++ b/manifest/manifest.yaml @@ -1138,11 +1138,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -1493,11 +1505,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -3581,11 +3605,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -3936,11 +3972,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -5386,11 +5434,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -5809,11 +5869,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -7167,11 +7239,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -7522,11 +7606,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -9686,11 +9782,23 @@ spec: or a ConfigMap/Secret. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional @@ -10041,11 +10149,23 @@ spec: RemoteURL configures fetching content from an HTTP/HTTPS endpoint or an OCI registry. When set, Kind/Name/Namespace must be omitted. properties: + insecureSkipTLSVerify: + default: false + description: |- + InsecureSkipTLSVerify controls server certificate verification. + Ignored if the referenced SecretRef provides a "caFile". + type: boolean interval: description: |- Interval defines how often Sveltos re-fetches the source to detect changes. Defaults to 5 minutes. type: string + plainHTTP: + default: false + description: |- + PlainHTTP indicates to use insecure HTTP connections when URL uses the + "oci://" scheme. Ignored for "http://"/"https://" URLs. + type: boolean secretRef: description: |- SecretRef references a Secret in the management cluster containing optional