diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 16de6c026..0b0fde694 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -153,11 +153,11 @@ func (in *GitRepositorySpec) DeepCopyInto(out *GitRepositorySpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(metav1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.Reference != nil { in, out := &in.Reference, &out.Reference diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 60dae6ad1..fd3252bf3 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -111,11 +111,11 @@ func (in *BucketSpec) DeepCopyInto(out *BucketSpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.Ignore != nil { in, out := &in.Ignore, &out.Ignore @@ -265,11 +265,11 @@ func (in *GitRepositorySpec) DeepCopyInto(out *GitRepositorySpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.Reference != nil { in, out := &in.Reference, &out.Reference @@ -426,7 +426,7 @@ func (in *HelmChartList) DeepCopyObject() runtime.Object { func (in *HelmChartSpec) DeepCopyInto(out *HelmChartSpec) { *out = *in out.SourceRef = in.SourceRef - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.ValuesFiles != nil { in, out := &in.ValuesFiles, &out.ValuesFiles *out = make([]string, len(*in)) @@ -544,11 +544,11 @@ func (in *HelmRepositorySpec) DeepCopyInto(out *HelmRepositorySpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.AccessFrom != nil { in, out := &in.AccessFrom, &out.AccessFrom diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index cde266706..3a0850fd9 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -124,11 +124,11 @@ func (in *BucketSpec) DeepCopyInto(out *BucketSpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.Ignore != nil { in, out := &in.Ignore, &out.Ignore @@ -283,11 +283,11 @@ func (in *GitRepositorySpec) DeepCopyInto(out *GitRepositorySpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.Reference != nil { in, out := &in.Reference, &out.Reference @@ -454,7 +454,7 @@ func (in *HelmChartList) DeepCopyObject() runtime.Object { func (in *HelmChartSpec) DeepCopyInto(out *HelmChartSpec) { *out = *in out.SourceRef = in.SourceRef - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.ValuesFiles != nil { in, out := &in.ValuesFiles, &out.ValuesFiles *out = make([]string, len(*in)) @@ -577,11 +577,11 @@ func (in *HelmRepositorySpec) DeepCopyInto(out *HelmRepositorySpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.AccessFrom != nil { in, out := &in.AccessFrom, &out.AccessFrom @@ -760,11 +760,11 @@ func (in *OCIRepositorySpec) DeepCopyInto(out *OCIRepositorySpec) { *out = new(meta.LocalObjectReference) **out = **in } - in.Interval.DeepCopyInto(&out.Interval) + out.Interval = in.Interval if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(v1.Duration) - (*in).DeepCopyInto(*out) + **out = **in } if in.Ignore != nil { in, out := &in.Ignore, &out.Ignore diff --git a/docs/spec/v1beta2/helmrepositories.md b/docs/spec/v1beta2/helmrepositories.md index 34ddfe468..630680fc2 100644 --- a/docs/spec/v1beta2/helmrepositories.md +++ b/docs/spec/v1beta2/helmrepositories.md @@ -454,8 +454,6 @@ flux create secret oci ghcr-auth \ #### TLS authentication -**Note:** TLS authentication is not yet supported by OCI Helm repositories. - To provide TLS credentials to use while connecting with the Helm repository, the referenced Secret is expected to contain `.data.certFile` and `.data.keyFile`, and/or `.data.caFile` values. @@ -487,6 +485,28 @@ data: caFile: ``` +#### Provide TLS credentials in a secret of type kubernetes.io/dockerconfigjson + +For OCI Helm repositories, Kubernetes secrets of type [kubernetes.io/dockerconfigjson](https://kubernetes.io/docs/concepts/configuration/secret/#secret-types) +are also supported. It is possible to append TLS credentials to the secret data. + +For example: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: example-tls + namespace: default +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: + certFile: + keyFile: + # NOTE: Can be supplied without the above values + caFile: +``` + ### Pass credentials `.spec.passCredentials` is an optional field to allow the credentials from the diff --git a/internal/controller/helmchart_controller.go b/internal/controller/helmchart_controller.go index 9bd933862..9215807bc 100644 --- a/internal/controller/helmchart_controller.go +++ b/internal/controller/helmchart_controller.go @@ -510,6 +510,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj * tlsConfig *tls.Config authenticator authn.Authenticator keychain authn.Keychain + tlsLoginOpt helmreg.LoginOption + tmpCertsDir string ) // Used to login with the repository declared provider ctxTimeout, cancel := context.WithTimeout(ctx, repo.Spec.Timeout.Duration) @@ -549,6 +551,22 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj * } clientOpts = append(clientOpts, opts...) tlsConfig = tlsCfg + tlsLoginOpt, tmpCertsDir, err = makeTLSLoginOption(secret) + if err != nil { + e := &serror.Event{ + Err: err, + Reason: sourcev1.AuthenticationFailedReason, + } + conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error()) + // Requeue as content of secret might change + return sreconcile.ResultEmpty, e + } + defer func() { + if err := os.RemoveAll(tmpCertsDir); err != nil { + r.eventLogf(ctx, obj, corev1.EventTypeWarning, meta.FailedReason, + "failed to delete temporary certificates directory: %s", err) + } + }() // Build registryClient options from secret keychain, err = registry.LoginOptionFromSecret(normalizedURL, *secret) @@ -650,7 +668,11 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj * // If login options are configured, use them to login to the registry // The OCIGetter will later retrieve the stored credentials to pull the chart if loginOpt != nil { - err = ociChartRepo.Login(loginOpt) + opts := []helmreg.LoginOption{loginOpt} + if tlsLoginOpt != nil { + opts = append(opts, tlsLoginOpt) + } + err = ociChartRepo.Login(opts...) if err != nil { e := &serror.Event{ Err: fmt.Errorf("failed to login to OCI registry: %w", err), @@ -1023,9 +1045,11 @@ func (r *HelmChartReconciler) garbageCollect(ctx context.Context, obj *helmv1.He // or a shim with defaults if no object could be found. // The callback returns an object with a state, so the caller has to do the necessary cleanup. func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Context, name, namespace string) chart.GetChartDownloaderCallback { - return func(url string) (repository.Downloader, error) { + return func(url string) (repo repository.Downloader, err error) { var ( tlsConfig *tls.Config + tlsLoginOpt helmreg.LoginOption + tmpCertsDir string authenticator authn.Authenticator keychain authn.Keychain ) @@ -1069,6 +1093,19 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont } clientOpts = append(clientOpts, opts...) tlsConfig = tlsCfg + tlsLoginOpt, tmpCertsDir, err = makeTLSLoginOption(secret) + if err != nil { + return nil, err + } + defer func() { + var errs []error + if errf := os.RemoveAll(tmpCertsDir); errf != nil { + errs = append(errs, errf) + } + errs = append(errs, err) + err = kerrors.NewAggregate(errs) + return + }() // Build registryClient options from secret keychain, err = registry.LoginOptionFromSecret(normalizedURL, *secret) @@ -1119,7 +1156,11 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont // If login options are configured, use them to login to the registry // The OCIGetter will later retrieve the stored credentials to pull the chart if loginOpt != nil { - err = ociChartRepo.Login(loginOpt) + opts := []helmreg.LoginOption{loginOpt} + if tlsLoginOpt != nil { + opts = append(opts, tlsLoginOpt) + } + err = ociChartRepo.Login(opts...) if err != nil { errs = append(errs, fmt.Errorf("failed to login to OCI chart repository for HelmRepository '%s': %w", obj.Name, err)) // clean up the credentialsFile diff --git a/internal/controller/helmchart_controller_test.go b/internal/controller/helmchart_controller_test.go index fc88e01c4..009dd723e 100644 --- a/internal/controller/helmchart_controller_test.go +++ b/internal/controller/helmchart_controller_test.go @@ -1069,7 +1069,7 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) { g.Expect(err).NotTo(HaveOccurred()) // Upload the test chart - metadata, err := loadTestChartToOCI(chartData, chartPath, testRegistryServer) + metadata, err := loadTestChartToOCI(chartData, testRegistryServer, "", "", "") g.Expect(err).NotTo(HaveOccurred()) storage, err := NewStorage(tmpDir, "example.com", retentionTTL, retentionRecords) @@ -2274,7 +2274,6 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { type secretOptions struct { username string password string - ca []byte } tests := []struct { @@ -2282,6 +2281,8 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { url string registryOpts registryOptions secretOpts secretOptions + secret *corev1.Secret + withTLS bool provider string providerImg string want sreconcile.Result @@ -2306,6 +2307,13 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { username: testRegistryUsername, password: testRegistryPassword, }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: pulled 'helmchart' chart with version '0.1.0'"), *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: pulled 'helmchart' chart with version '0.1.0'"), @@ -2322,6 +2330,13 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { username: "wrong-pass", password: "wrong-pass", }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: failed to login to OCI registry"), }, @@ -2345,6 +2360,13 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { username: testRegistryUsername, password: testRegistryPassword, }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + }, provider: "azure", assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: pulled 'helmchart' chart with version '0.1.0'"), @@ -2355,27 +2377,47 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { name: "HTTPS With invalid CA cert", wantErr: true, registryOpts: registryOptions{ - withBasicAuth: true, + withTLS: true, }, + withTLS: true, secretOpts: secretOptions{ username: testRegistryUsername, password: testRegistryPassword, - ca: []byte("invalid-ca"), + }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + "caFile": []byte("invalid caFile"), + }, }, assertConditions: []metav1.Condition{ - *conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: failed to login to OCI registry"), + *conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: failed to create TLS client config with secret data: cannot append certificate into certificate pool: invalid caFile"), }, }, { name: "HTTPS With CA cert", want: sreconcile.ResultSuccess, registryOpts: registryOptions{ - withBasicAuth: true, + withTLS: true, }, + withTLS: true, secretOpts: secretOptions{ username: testRegistryUsername, password: testRegistryPassword, - ca: []byte(tlsCA), + }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + "caFile": tlsCA, + "certFile": tlsPublicKey, + "keyFile": tlsPrivateKey, + }, }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: pulled 'helmchart' chart with version '0.1.0'"), @@ -2396,13 +2438,16 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts) g.Expect(err).NotTo(HaveOccurred()) + if tt.withTLS { + defer server.stopSrv() + } // Load a test chart chartData, err := os.ReadFile(chartPath) g.Expect(err).ToNot(HaveOccurred()) // Upload the test chart - metadata, err := loadTestChartToOCI(chartData, chartPath, server) + metadata, err := loadTestChartToOCI(chartData, server, "testdata/certs/server.pem", "testdata/certs/server-key.pem", "testdata/certs/ca.pem") g.Expect(err).ToNot(HaveOccurred()) repo := &helmv1.HelmRepository{ @@ -2428,40 +2473,16 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { repo.Spec.URL = tt.providerImg } - var secret *corev1.Secret if tt.secretOpts.username != "" && tt.secretOpts.password != "" { - secret = &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, - Type: corev1.SecretTypeDockerConfigJson, - Data: map[string][]byte{ - ".dockerconfigjson": []byte(fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, - server.registryHost, tt.secretOpts.username, tt.secretOpts.password)), - }, - } - } - - if tt.secretOpts.ca != nil { - if secret == nil { - secret = &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, - Data: map[string][]byte{ - "caFile": tt.secretOpts.ca, - }, - } - } else { - secret.Data["caFile"] = tt.secretOpts.ca - } + tt.secret.Data[".dockerconfigjson"] = []byte(fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, + server.registryHost, tt.secretOpts.username, tt.secretOpts.password)) } - if secret != nil { + if tt.secret != nil { repo.Spec.SecretRef = &meta.LocalObjectReference{ - Name: secret.Name, + Name: tt.secret.Name, } - clientBuilder.WithObjects(secret, repo) + clientBuilder.WithObjects(tt.secret, repo) } else { clientBuilder.WithObjects(repo) } @@ -2534,7 +2555,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignature(t *testing.T g.Expect(err).ToNot(HaveOccurred()) // Upload the test chart - metadata, err := loadTestChartToOCI(chartData, chartPath, server) + metadata, err := loadTestChartToOCI(chartData, server, "", "", "") g.Expect(err).NotTo(HaveOccurred()) storage, err := NewStorage(tmpDir, "example.com", retentionTTL, retentionRecords) @@ -2765,17 +2786,11 @@ func extractChartMeta(chartData []byte) (*hchart.Metadata, error) { return ch.Metadata, nil } -func loadTestChartToOCI(chartData []byte, chartPath string, server *registryClientTestServer) (*hchart.Metadata, error) { +func loadTestChartToOCI(chartData []byte, server *registryClientTestServer, certFile, keyFile, cafile string) (*hchart.Metadata, error) { // Login to the registry err := server.registryClient.Login(server.registryHost, helmreg.LoginOptBasicAuth(testRegistryUsername, testRegistryPassword), - helmreg.LoginOptInsecure(true)) - if err != nil { - return nil, err - } - - // Load a test chart - chartData, err = os.ReadFile(chartPath) + helmreg.LoginOptTLSClientConfig(certFile, keyFile, cafile)) if err != nil { return nil, err } diff --git a/internal/controller/helmrepository_controller_oci.go b/internal/controller/helmrepository_controller_oci.go index 70feb8410..1325a4116 100644 --- a/internal/controller/helmrepository_controller_oci.go +++ b/internal/controller/helmrepository_controller_oci.go @@ -309,6 +309,8 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, sp *patch.S authenticator authn.Authenticator keychain authn.Keychain tlsConfig *tls.Config + tmpCertsDir string + tlsLoginOpt helmreg.LoginOption err error ) // Configure any authentication related options. @@ -336,6 +338,18 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, sp *patch.S result, retErr = ctrl.Result{}, err return } + tlsLoginOpt, tmpCertsDir, err = makeTLSLoginOption(&secret) + if err != nil { + conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error()) + result, retErr = ctrl.Result{}, err + return + } + defer func() { + if err := os.RemoveAll(tmpCertsDir); err != nil { + r.eventLogf(ctx, obj, corev1.EventTypeWarning, meta.FailedReason, + "failed to delete temporary certificates directory: %s", err) + } + }() } else if obj.Spec.Provider != helmv1.GenericOCIProvider && obj.Spec.Type == helmv1.HelmRepositoryTypeOCI { auth, authErr := oidcAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider) if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) { @@ -385,7 +399,11 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, sp *patch.S // Attempt to login to the registry if credentials are provided. if loginOpt != nil { - err = chartRepo.Login(loginOpt) + opts := []helmreg.LoginOption{loginOpt} + if tlsLoginOpt != nil { + opts = append(opts, tlsLoginOpt) + } + err = chartRepo.Login(opts...) if err != nil { e := fmt.Errorf("failed to login to registry '%s': %w", obj.Spec.URL, err) conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, e.Error()) @@ -452,6 +470,80 @@ func makeLoginOption(auth authn.Authenticator, keychain authn.Keychain, registry return nil, nil } +func makeTLSLoginOption(secret *corev1.Secret) (helmreg.LoginOption, string, error) { + var errs []error + certFile, keyFile, caFile, tmpDir, err := certsFilesFromSecret(secret) + if err != nil { + errs = append(errs, err) + if tmpDir != "" { + if err := os.RemoveAll(tmpDir); err != nil { + errs = append(errs, err) + } + } + return nil, "", kerrors.NewAggregate(errs) + } + + if (certFile != "" && keyFile != "") || caFile != "" { + return helmreg.LoginOptTLSClientConfig(certFile, keyFile, caFile), tmpDir, nil + } + + return nil, "", nil +} + +func certsFilesFromSecret(secret *corev1.Secret) (string, string, string, string, error) { + certBytes, keyBytes, caBytes := secret.Data["certFile"], secret.Data["keyFile"], secret.Data["caFile"] + switch { + case len(certBytes)+len(keyBytes)+len(caBytes) == 0: + return "", "", "", "", nil + case (len(certBytes) > 0 && len(keyBytes) == 0) || (len(keyBytes) > 0 && len(certBytes) == 0): + return "", "", "", "", fmt.Errorf("invalid '%s' secret data: fields 'certFile' and 'keyFile' require each other's presence", + secret.Name) + } + + var ( + certFile string + keyFile string + caFile string + err error + ) + + // create temporary folder to store the certs + tmpDir, err := os.MkdirTemp("", "helm-repo-oci-certs") + if err != nil { + return "", "", "", "", err + } + + if len(certBytes) > 0 && len(keyBytes) > 0 { + certFile, err = writeTofile(certBytes, "cert.pem", tmpDir) + if err != nil { + return "", "", "", "", err + } + keyFile, err = writeTofile(keyBytes, "key.pem", tmpDir) + if err != nil { + return "", "", "", "", err + } + } + if len(caBytes) > 0 { + caFile, err = writeTofile(caBytes, "ca.pem", tmpDir) + if err != nil { + return "", "", "", "", err + } + } + return certFile, keyFile, caFile, tmpDir, nil +} + +func writeTofile(data []byte, filename, tmpDir string) (string, error) { + file, err := os.CreateTemp(tmpDir, filename) + if err != nil { + return "", err + } + defer file.Close() + if _, err := file.Write(data); err != nil { + return "", err + } + return file.Name(), nil +} + func conditionsDiff(a, b []string) []string { bMap := make(map[string]struct{}, len(b)) for _, j := range b { diff --git a/internal/controller/helmrepository_controller_oci_test.go b/internal/controller/helmrepository_controller_oci_test.go index f64eb9fc1..432ef8ee0 100644 --- a/internal/controller/helmrepository_controller_oci_test.go +++ b/internal/controller/helmrepository_controller_oci_test.go @@ -164,19 +164,20 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { type secretOptions struct { username string password string - ca []byte } tests := []struct { - name string - url string - registryOpts registryOptions - secretOpts secretOptions - provider string - providerImg string - want ctrl.Result - wantErr bool - assertConditions []metav1.Condition + name string + url string + registryOpts registryOptions + withTLS bool + secretOpts secretOptions + secret *corev1.Secret + provider string + providerImg string + want ctrl.Result + wantErr bool + assertConditions []metav1.Condition }{ { name: "HTTP without basic auth", @@ -195,6 +196,13 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { username: testRegistryUsername, password: testRegistryPassword, }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Helm repository is ready"), }, @@ -210,6 +218,13 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { username: "wrong-pass", password: "wrong-pass", }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingWithRetryReason, "processing object: new generation"), *conditions.FalseCondition(meta.ReadyCondition, sourcev1.AuthenticationFailedReason, "failed to login to registry"), @@ -235,6 +250,13 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { username: testRegistryUsername, password: testRegistryPassword, }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + }, provider: "azure", assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Helm repository is ready"), @@ -244,12 +266,42 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { name: "HTTPS With invalid CA cert", wantErr: true, registryOpts: registryOptions{ - withBasicAuth: true, + withTLS: true, }, secretOpts: secretOptions{ username: testRegistryUsername, password: testRegistryPassword, - ca: []byte("invalid-ca"), + }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + "caFile": []byte("invalid caFile"), + }, + }, + assertConditions: []metav1.Condition{ + *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingWithRetryReason, "processing object: new generation"), + *conditions.FalseCondition(meta.ReadyCondition, sourcev1.AuthenticationFailedReason, "cannot append certificate into certificate pool: invalid caFile"), + }, + }, + { + name: "HTTPS With missing CA cert", + wantErr: true, + registryOpts: registryOptions{ + withTLS: true, + }, + secretOpts: secretOptions{ + username: testRegistryUsername, + password: testRegistryPassword, + }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingWithRetryReason, "processing object: new generation"), @@ -260,12 +312,23 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { name: "HTTPS With CA cert", want: ctrl.Result{RequeueAfter: interval}, registryOpts: registryOptions{ - withBasicAuth: true, + withTLS: true, }, + withTLS: true, secretOpts: secretOptions{ username: testRegistryUsername, password: testRegistryPassword, - ca: []byte(tlsCA), + }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth-secretref", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + "caFile": tlsCA, + "certFile": tlsPublicKey, + "keyFile": tlsPrivateKey, + }, }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Helm repository is ready"), @@ -284,7 +347,9 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { workspaceDir := t.TempDir() server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts) g.Expect(err).NotTo(HaveOccurred()) - + if tt.withTLS { + defer server.stopSrv() + } obj := &helmv1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "auth-strategy-", @@ -309,39 +374,15 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { obj.Spec.URL = tt.providerImg } - var secret *corev1.Secret if tt.secretOpts.username != "" && tt.secretOpts.password != "" { - secret = &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, - Type: corev1.SecretTypeDockerConfigJson, - Data: map[string][]byte{ - ".dockerconfigjson": []byte(fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, - server.registryHost, tt.secretOpts.username, tt.secretOpts.password)), - }, - } + tt.secret.Data[".dockerconfigjson"] = []byte(fmt.Sprintf(`{"auths": {%q: {"username": %q, "password": %q}}}`, + server.registryHost, tt.secretOpts.username, tt.secretOpts.password)) } - if tt.secretOpts.ca != nil { - if secret == nil { - secret = &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, - Data: map[string][]byte{ - "caFile": tt.secretOpts.ca, - }, - } - } else { - secret.Data["caFile"] = tt.secretOpts.ca - } - } - - if secret != nil { - clientBuilder.WithObjects(secret) + if tt.secret != nil { + clientBuilder.WithObjects(tt.secret) obj.Spec.SecretRef = &meta.LocalObjectReference{ - Name: secret.Name, + Name: tt.secret.Name, } } @@ -358,7 +399,6 @@ func TestHelmRepositoryOCIReconciler_authStrategy(t *testing.T) { }() sp := patch.NewSerialPatcher(obj, r.Client) - got, err := r.reconcile(ctx, sp, obj) g.Expect(err != nil).To(Equal(tt.wantErr)) g.Expect(got).To(Equal(tt.want)) diff --git a/internal/controller/ocirepository_controller_test.go b/internal/controller/ocirepository_controller_test.go index c971767f8..f068659ad 100644 --- a/internal/controller/ocirepository_controller_test.go +++ b/internal/controller/ocirepository_controller_test.go @@ -366,6 +366,12 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { pool := x509.NewCertPool() pool.AppendCertsFromPEM(tlsCA) + cert, err := tls.LoadX509KeyPair("testdata/certs/server.pem", "testdata/certs/server-key.pem") + if err != nil { + t.Fatal(err) + } + certs := []tls.Certificate{cert} + tests := []struct { name string url string @@ -373,6 +379,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { craneOpts []crane.Option secretOpts secretOptions tlsCertSecret *corev1.Secret + withTLS bool provider string providerImg string want sreconcile.Result @@ -493,9 +500,11 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { registryOpts: registryOptions{ withTLS: true, }, + withTLS: true, craneOpts: []crane.Option{crane.WithTransport(&http.Transport{ TLSClientConfig: &tls.Config{ - RootCAs: pool, + RootCAs: pool, + Certificates: certs, }, }), }, @@ -504,7 +513,9 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { Name: "ca-file", }, Data: map[string][]byte{ - "caFile": tlsCA, + "caFile": tlsCA, + "certFile": tlsPublicKey, + "keyFile": tlsPrivateKey, }, }, assertConditions: []metav1.Condition{ @@ -519,9 +530,11 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { registryOpts: registryOptions{ withTLS: true, }, + withTLS: true, craneOpts: []crane.Option{crane.WithTransport(&http.Transport{ TLSClientConfig: &tls.Config{ - RootCAs: pool, + RootCAs: pool, + Certificates: certs, }, }), }, @@ -536,9 +549,11 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { registryOpts: registryOptions{ withTLS: true, }, + withTLS: true, craneOpts: []crane.Option{crane.WithTransport(&http.Transport{ TLSClientConfig: &tls.Config{ - RootCAs: pool, + RootCAs: pool, + Certificates: certs, }, }), }, @@ -607,6 +622,9 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { workspaceDir := t.TempDir() server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts) + if tt.withTLS { + defer server.stopSrv() + } g.Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 7fa55e0cb..2f73c44d2 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -19,10 +19,13 @@ package controller import ( "bytes" "context" + "crypto/tls" + "crypto/x509" "fmt" "io" "math/rand" "net" + "net/http" "os" "path/filepath" "testing" @@ -139,20 +142,52 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry server.out = &out // init test client - client, err := helmreg.NewClient( - helmreg.ClientOptDebug(true), - helmreg.ClientOptWriter(server.out), - ) - if err != nil { - return nil, fmt.Errorf("failed to create registry client: %s", err) + if opts.withTLS { + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(tlsCA) { + return nil, fmt.Errorf("failed to append CA certificate") + } + cert, err := tls.X509KeyPair(tlsPublicKey, tlsPrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to load TLS key pair: %s", err) + } + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: pool, + Certificates: []tls.Certificate{cert}, + }, + }, + } + + client, err := helmreg.NewClient( + helmreg.ClientOptDebug(true), + helmreg.ClientOptWriter(server.out), + helmreg.ClientOptHTTPClient(httpClient), + ) + if err != nil { + return nil, fmt.Errorf("failed to create registry client: %s", err) + } + server.registryClient = client + } else { + client, err := helmreg.NewClient( + helmreg.ClientOptDebug(true), + helmreg.ClientOptWriter(server.out), + ) + if err != nil { + return nil, fmt.Errorf("failed to create registry client: %s", err) + } + server.registryClient = client } - server.registryClient = client config := &configuration.Configuration{} port, err := freeport.GetFreePort() if err != nil { return nil, fmt.Errorf("failed to get free port: %s", err) } + config.HTTP.Addr = fmt.Sprintf("127.0.0.1:%d", port) + config.HTTP.DrainTimeout = time.Duration(10) * time.Second + config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} server.registryHost = fmt.Sprintf("localhost:%d", port) if opts.withTLS { // docker `MatchLocalhost` is a host match function which returns true for @@ -171,10 +206,12 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry return nil, fmt.Errorf("failed to create mock DNS server: %s", err) } server.srv.PatchNet(net.DefaultResolver) + + config.HTTP.TLS.Certificate = "testdata/certs/server.pem" + config.HTTP.TLS.Key = "testdata/certs/server-key.pem" + config.HTTP.TLS.ClientCAs = []string{"testdata/certs/ca.pem"} + } - config.HTTP.Addr = fmt.Sprintf("127.0.0.1:%d", port) - config.HTTP.DrainTimeout = time.Duration(10) * time.Second - config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} if opts.withBasicAuth { // create htpasswd file (w BCrypt, which is required) @@ -197,12 +234,6 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry } } - if opts.withTLS { - config.HTTP.TLS.Certificate = "testdata/certs/server.pem" - config.HTTP.TLS.Key = "testdata/certs/server-key.pem" - config.HTTP.TLS.ClientCAs = []string{"testdata/certs/ca.pem"} - } - // setup logger options config.Log.AccessLog.Disabled = true config.Log.Level = "error" @@ -264,7 +295,6 @@ func TestMain(m *testing.M) { if err != nil { panic(fmt.Sprintf("Failed to create a test registry server: %v", err)) } - defer testRegistryServer.stopSrv() if err := (&GitRepositoryReconciler{ Client: testEnv, diff --git a/internal/controller/testdata/certs/ca-csr.json b/internal/controller/testdata/certs/ca-csr.json index 941277bb1..7cffb0e30 100644 --- a/internal/controller/testdata/certs/ca-csr.json +++ b/internal/controller/testdata/certs/ca-csr.json @@ -4,6 +4,7 @@ "127.0.0.1", "localhost", "example.com", - "www.example.com" + "www.example.com", + "0x7f000001" ] } diff --git a/internal/controller/testdata/certs/ca-key.pem b/internal/controller/testdata/certs/ca-key.pem index b69de5ab5..16bca20ab 100644 --- a/internal/controller/testdata/certs/ca-key.pem +++ b/internal/controller/testdata/certs/ca-key.pem @@ -1,5 +1,5 @@ -----BEGIN EC PRIVATE KEY----- -MHcCAQEEIOH/u9dMcpVcZ0+X9Fc78dCTj8SHuXawhLjhu/ej64WToAoGCCqGSM49 -AwEHoUQDQgAEruH/kPxtX3cyYR2G7TYmxLq6AHyzo/NGXc9XjGzdJutE2SQzn37H -dvSJbH+Lvqo9ik0uiJVRVdCYD1j7gNszGA== +MHcCAQEEIOXssMUfnHMMuufQpshjlYrR3zAbgcrkIrMqGVFQK6BpoAoGCCqGSM49 +AwEHoUQDQgAE5FNAUf6HpeTiM58p6N3NAsstI0RCvptmQy9b0eBUuW1+2ZGfQf2D +80FbuFw6zR4wr7A4PMLeLrVJNY5EY5b/TA== -----END EC PRIVATE KEY----- diff --git a/internal/controller/testdata/certs/ca.csr b/internal/controller/testdata/certs/ca.csr index baa8aeb26..be5c862b9 100644 --- a/internal/controller/testdata/certs/ca.csr +++ b/internal/controller/testdata/certs/ca.csr @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE REQUEST----- -MIIBIDCBxgIBADAZMRcwFQYDVQQDEw5leGFtcGxlLmNvbSBDQTBZMBMGByqGSM49 -AgEGCCqGSM49AwEHA0IABK7h/5D8bV93MmEdhu02JsS6ugB8s6PzRl3PV4xs3Sbr -RNkkM59+x3b0iWx/i76qPYpNLoiVUVXQmA9Y+4DbMxigSzBJBgkqhkiG9w0BCQ4x -PDA6MDgGA1UdEQQxMC+CCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFt -cGxlLmNvbYcEfwAAATAKBggqhkjOPQQDAgNJADBGAiEAkw85nyLhJssyCYsaFvRU -EErhu66xHPJug/nG50uV5OoCIQCUorrflOSxfChPeCe4xfwcPv7FpcCYbKVYtGzz -b34Wow== +MIIBKzCB0gIBADAZMRcwFQYDVQQDEw5leGFtcGxlLmNvbSBDQTBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABORTQFH+h6Xk4jOfKejdzQLLLSNEQr6bZkMvW9HgVLlt +ftmRn0H9g/NBW7hcOs0eMK+wODzC3i61STWORGOW/0ygVzBVBgkqhkiG9w0BCQ4x +SDBGMEQGA1UdEQQ9MDuCCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFt +cGxlLmNvbYIKMHg3ZjAwMDAwMYcEfwAAATAKBggqhkjOPQQDAgNIADBFAiBQLSRn +qPbbMyBZSlLcCH23gBTcR4axsAw4fCazBjMJ2wIhAPQ671kzMSn63wInDjWxsdF1 +t0c4ZH8jHpkUEAQ74UVW -----END CERTIFICATE REQUEST----- diff --git a/internal/controller/testdata/certs/ca.pem b/internal/controller/testdata/certs/ca.pem index 080bd24e6..188443c3d 100644 --- a/internal/controller/testdata/certs/ca.pem +++ b/internal/controller/testdata/certs/ca.pem @@ -1,11 +1,11 @@ -----BEGIN CERTIFICATE----- -MIIBhzCCAS2gAwIBAgIUdsAtiX3gN0uk7ddxASWYE/tdv0wwCgYIKoZIzj0EAwIw -GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjAwNDE3MDgxODAwWhcNMjUw -NDE2MDgxODAwWjAZMRcwFQYDVQQDEw5leGFtcGxlLmNvbSBDQTBZMBMGByqGSM49 -AgEGCCqGSM49AwEHA0IABK7h/5D8bV93MmEdhu02JsS6ugB8s6PzRl3PV4xs3Sbr -RNkkM59+x3b0iWx/i76qPYpNLoiVUVXQmA9Y+4DbMxijUzBRMA4GA1UdDwEB/wQE -AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQGyUiU1QEZiMAqjsnIYTwZ -4yp5wzAPBgNVHREECDAGhwR/AAABMAoGCCqGSM49BAMCA0gAMEUCIQDzdtvKdE8O -1+WRTZ9MuSiFYcrEz7Zne7VXouDEKqKEigIgM4WlbDeuNCKbqhqj+xZV0pa3rweb -OD8EjjCMY69RMO0= +MIIBhjCCAS2gAwIBAgIUE/XiwPHY1izwrIwbQiXxqO/Q8KswCgYIKoZIzj0EAwIw +GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjMwNTI0MTQyMTAwWhcNMjgw +NTIyMTQyMTAwWjAZMRcwFQYDVQQDEw5leGFtcGxlLmNvbSBDQTBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABORTQFH+h6Xk4jOfKejdzQLLLSNEQr6bZkMvW9HgVLlt +ftmRn0H9g/NBW7hcOs0eMK+wODzC3i61STWORGOW/0yjUzBRMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRSGfT6hp/EUOgqOjRm4Vzs +Pt8QiTAPBgNVHREECDAGhwR/AAABMAoGCCqGSM49BAMCA0cAMEQCIGqg/rOrI9Oq +EBEmPyCtehmKWLcpKDBMTzZAponByVlwAiATsP/meJNiEsMvOK2Q7UFvi2+c3Rcj +NUQAcAm8iEtJQQ== -----END CERTIFICATE----- diff --git a/internal/controller/testdata/certs/server-csr.json b/internal/controller/testdata/certs/server-csr.json index 0baf11601..9fb0c7962 100644 --- a/internal/controller/testdata/certs/server-csr.json +++ b/internal/controller/testdata/certs/server-csr.json @@ -4,6 +4,7 @@ "127.0.0.1", "localhost", "example.com", - "www.example.com" + "www.example.com", + "0x7f000001" ] } diff --git a/internal/controller/testdata/certs/server-key.pem b/internal/controller/testdata/certs/server-key.pem index 5054ff39f..d147ad6a5 100644 --- a/internal/controller/testdata/certs/server-key.pem +++ b/internal/controller/testdata/certs/server-key.pem @@ -1,5 +1,5 @@ -----BEGIN EC PRIVATE KEY----- -MHcCAQEEIKQbEXV6nljOHMmPrWVWQ+JrAE5wsbE9iMhfY7wlJgXOoAoGCCqGSM49 -AwEHoUQDQgAE+53oBGlrvVUTelSGYji8GNHVhVg8jOs1PeeLuXCIZjQmctHLFEq3 -fE+mGxCL93MtpYzlwIWBf0m7pEGQre6bzg== +MHcCAQEEIJ68GBu37yyAfzMnKMgo950R8OYoGrt6DUwVg1Wy5c+ZoAoGCCqGSM49 +AwEHoUQDQgAE/2T6ANTLUGqz/QWJi35H6ijcaDabFwU2IxqNu9iwwQz7e+CkCqcu +6wLAn1Q8URpG3CGTS2td0a9v2zPqSi9eAQ== -----END EC PRIVATE KEY----- diff --git a/internal/controller/testdata/certs/server.csr b/internal/controller/testdata/certs/server.csr index 5caf7b39c..941dd4f92 100644 --- a/internal/controller/testdata/certs/server.csr +++ b/internal/controller/testdata/certs/server.csr @@ -1,8 +1,9 @@ -----BEGIN CERTIFICATE REQUEST----- -MIIBHDCBwwIBADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG -CCqGSM49AwEHA0IABPud6ARpa71VE3pUhmI4vBjR1YVYPIzrNT3ni7lwiGY0JnLR -yxRKt3xPphsQi/dzLaWM5cCFgX9Ju6RBkK3um86gSzBJBgkqhkiG9w0BCQ4xPDA6 -MDgGA1UdEQQxMC+CCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxl -LmNvbYcEfwAAATAKBggqhkjOPQQDAgNIADBFAiB5A6wvQ5x6g/zhiyn+wLzXsOaB -Gb/F25p/zTHHQqZbkwIhAPUgWzy/2bs6eZEi97bSlaRdmrqHwqT842t5sEwGyXNV +MIIBJzCBzwIBADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABP9k+gDUy1Bqs/0FiYt+R+oo3Gg2mxcFNiMajbvYsMEM+3vg +pAqnLusCwJ9UPFEaRtwhk0trXdGvb9sz6kovXgGgVzBVBgkqhkiG9w0BCQ4xSDBG +MEQGA1UdEQQ9MDuCCWxvY2FsaG9zdIILZXhhbXBsZS5jb22CD3d3dy5leGFtcGxl +LmNvbYIKMHg3ZjAwMDAwMYcEfwAAATAKBggqhkjOPQQDAgNHADBEAiAyNdC5xrMi +E3MopSm30C0D3WI0dV2ygCC6NsB/VyKkyAIgYGG4A1F8EDn4e9T7237GIg21a0tc +oD7WyWEgeLOzb9c= -----END CERTIFICATE REQUEST----- diff --git a/internal/controller/testdata/certs/server.pem b/internal/controller/testdata/certs/server.pem index 11c655a0b..232a57eb4 100644 --- a/internal/controller/testdata/certs/server.pem +++ b/internal/controller/testdata/certs/server.pem @@ -1,13 +1,13 @@ -----BEGIN CERTIFICATE----- -MIIB7TCCAZKgAwIBAgIUB+17B8PU05wVTzRHLeG+S+ybZK4wCgYIKoZIzj0EAwIw -GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjAwNDE3MDgxODAwWhcNMzAw -NDE1MDgxODAwWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG -CCqGSM49AwEHA0IABPud6ARpa71VE3pUhmI4vBjR1YVYPIzrNT3ni7lwiGY0JnLR -yxRKt3xPphsQi/dzLaWM5cCFgX9Ju6RBkK3um86jgbowgbcwDgYDVR0PAQH/BAQD +MIIB+TCCAZ6gAwIBAgIUIGxnexcJoQ+H1wPEkwe5c7gwxbAwCgYIKoZIzj0EAwIw +GTEXMBUGA1UEAxMOZXhhbXBsZS5jb20gQ0EwHhcNMjMwNTI0MTQyMTAwWhcNMzMw +NTIxMTQyMTAwWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABP9k+gDUy1Bqs/0FiYt+R+oo3Gg2mxcFNiMajbvYsMEM+3vg +pAqnLusCwJ9UPFEaRtwhk0trXdGvb9sz6kovXgGjgcYwgcMwDgYDVR0PAQH/BAQD AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA -MB0GA1UdDgQWBBTM8HS5EIlVMBYv/300jN8PEArUgDAfBgNVHSMEGDAWgBQGyUiU -1QEZiMAqjsnIYTwZ4yp5wzA4BgNVHREEMTAvgglsb2NhbGhvc3SCC2V4YW1wbGUu -Y29tgg93d3cuZXhhbXBsZS5jb22HBH8AAAEwCgYIKoZIzj0EAwIDSQAwRgIhAOgB -5W82FEgiTTOmsNRekkK5jUPbj4D4eHtb2/BI7ph4AiEA2AxHASIFBdv5b7Qf5prb -bdNmUCzAvVuCAKuMjg2OPrE= +MB0GA1UdDgQWBBQ1Z+HHTrEjwJsOvNaXPhoiC3whBjAfBgNVHSMEGDAWgBRSGfT6 +hp/EUOgqOjRm4VzsPt8QiTBEBgNVHREEPTA7gglsb2NhbGhvc3SCC2V4YW1wbGUu +Y29tgg93d3cuZXhhbXBsZS5jb22CCjB4N2YwMDAwMDGHBH8AAAEwCgYIKoZIzj0E +AwIDSQAwRgIhAIlapRW3ve2Q0I+dkUkJZM3pSjEUj5Vnr5+XAGa1Hj7vAiEAtoXs +AGr5+WetlgkeRmxIqEAxtm8DED72dg5rTDueU04= -----END CERTIFICATE-----