Skip to content

Commit 2e8db08

Browse files
committed
refactor TLSconfig funcs
Signed-off-by: Soule BA <[email protected]>
1 parent 6293fe1 commit 2e8db08

23 files changed

+476
-332
lines changed

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/spec/v1beta2/helmrepositories.md

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -459,16 +459,8 @@ a deprecation warning will be logged.
459459

460460
### Cert secret reference
461461

462-
<<<<<<< HEAD
463-
**Note:** TLS authentication is not yet supported by OCI Helm repositories.
464-
465462
`.spec.certSecretRef.name` is an optional field to specify a secret containing TLS
466463
certificate data. The secret can contain the following keys:
467-
=======
468-
To provide TLS credentials to use while connecting with the Helm repository,
469-
the referenced Secret is expected to contain `.data.certFile` and
470-
`.data.keyFile`, and/or `.data.caFile` values.
471-
>>>>>>> 3df4c49 (refactoring and fix tests)
472464

473465
* `certFile` and `keyFile`, to specify the client certificate and private key used for
474466
TLS client authentication. These must be used in conjunction, i.e. specifying one without
@@ -515,28 +507,6 @@ data:
515507
caFile: <BASE64>
516508
```
517509

518-
#### Provide TLS credentials in a secret of type kubernetes.io/dockerconfigjson
519-
520-
For OCI Helm repositories, Kubernetes secrets of type [kubernetes.io/dockerconfigjson](https://kubernetes.io/docs/concepts/configuration/secret/#secret-types)
521-
are also supported. It is possible to append TLS credentials to the secret data.
522-
523-
For example:
524-
525-
```yaml
526-
apiVersion: v1
527-
kind: Secret
528-
metadata:
529-
name: example-tls
530-
namespace: default
531-
type: kubernetes.io/dockerconfigjson
532-
data:
533-
.dockerconfigjson: <BASE64>
534-
certFile: <BASE64>
535-
keyFile: <BASE64>
536-
# NOTE: Can be supplied without the above values
537-
caFile: <BASE64>
538-
```
539-
540510
### Pass credentials
541511

542512
`.spec.passCredentials` is an optional field to allow the credentials from the

go.mod

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,8 @@ require (
4040
github.com/fluxcd/pkg/tar v0.2.0
4141
github.com/fluxcd/pkg/testserver v0.4.0
4242
github.com/fluxcd/pkg/version v0.2.2
43-
<<<<<<< HEAD
4443
github.com/fluxcd/source-controller/api v1.0.0
45-
=======
46-
github.com/fluxcd/source-controller/api v1.0.0-rc.5
4744
github.com/foxcpp/go-mockdns v1.0.0
48-
>>>>>>> 4e0d792 (Adapting setupRegistryServer to be able to use https with the docker)
4945
github.com/go-git/go-billy/v5 v5.4.1
5046
github.com/go-git/go-git/v5 v5.8.1
5147
github.com/go-logr/logr v1.2.4

internal/controller/helmchart_controller.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
510510
if err != nil {
511511
return chartRepoConfigErrorReturn(err, obj)
512512
}
513-
clientOpts, err := getter.GetClientOpts(ctxTimeout, r.Client, repo, normalizedURL)
513+
514+
clientOpts, certsTmpDir, err := getter.GetClientOpts(ctxTimeout, r.Client, repo, normalizedURL)
514515
if err != nil && !errors.Is(err, getter.ErrDeprecatedTLSConfig) {
515516
e := &serror.Event{
516517
Err: err,
@@ -519,6 +520,10 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
519520
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
520521
return sreconcile.ResultEmpty, e
521522
}
523+
if certsTmpDir != "" {
524+
// this happens when the tls certs are stored in a temporary directory
525+
defer os.RemoveAll(certsTmpDir)
526+
}
522527
getterOpts := clientOpts.GetterOpts
523528

524529
// Initialize the chart repository
@@ -534,7 +539,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
534539
// this is needed because otherwise the credentials are stored in ~/.docker/config.json.
535540
// TODO@souleb: remove this once the registry move to Oras v2
536541
// or rework to enable reusing credentials to avoid the unneccessary handshake operations
537-
registryClient, credentialsFile, err := r.RegistryClientGenerator(clientOpts.RegLoginOpt != nil)
542+
registryClient, credentialsFile, err := r.RegistryClientGenerator(clientOpts.TlsConfig, clientOpts.RegLoginOpts[0] != nil)
538543
if err != nil {
539544
e := &serror.Event{
540545
Err: fmt.Errorf("failed to construct Helm client: %w", err),
@@ -583,8 +588,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
583588

584589
// If login options are configured, use them to login to the registry
585590
// The OCIGetter will later retrieve the stored credentials to pull the chart
586-
if clientOpts.RegLoginOpt != nil {
587-
err = ociChartRepo.Login(clientOpts.RegLoginOpt)
591+
if clientOpts.RegLoginOpts != nil {
592+
err = ociChartRepo.Login(clientOpts.RegLoginOpts...)
588593
if err != nil {
589594
e := &serror.Event{
590595
Err: fmt.Errorf("failed to login to OCI registry: %w", err),
@@ -981,15 +986,19 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
981986
ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
982987
defer cancel()
983988

984-
clientOpts, err := getter.GetClientOpts(ctxTimeout, r.Client, obj, normalizedURL)
989+
clientOpts, certsTmpDir, err := getter.GetClientOpts(ctxTimeout, r.Client, obj, normalizedURL)
985990
if err != nil && !errors.Is(err, getter.ErrDeprecatedTLSConfig) {
986991
return nil, err
987992
}
993+
if certsTmpDir != "" {
994+
// this happens when the tls certs are stored in a temporary directory
995+
defer os.RemoveAll(certsTmpDir)
996+
}
988997
getterOpts := clientOpts.GetterOpts
989998

990999
var chartRepo repository.Downloader
9911000
if helmreg.IsOCI(normalizedURL) {
992-
registryClient, credentialsFile, err := r.RegistryClientGenerator(clientOpts.RegLoginOpt != nil)
1001+
registryClient, credentialsFile, err := r.RegistryClientGenerator(clientOpts.TlsConfig, clientOpts.RegLoginOpts[0] != nil)
9931002
if err != nil {
9941003
return nil, fmt.Errorf("failed to create registry client: %w", err)
9951004
}
@@ -1014,8 +1023,8 @@ func (r *HelmChartReconciler) namespacedChartRepositoryCallback(ctx context.Cont
10141023

10151024
// If login options are configured, use them to login to the registry
10161025
// The OCIGetter will later retrieve the stored credentials to pull the chart
1017-
if clientOpts.RegLoginOpt != nil {
1018-
err = ociChartRepo.Login(clientOpts.RegLoginOpt)
1026+
if clientOpts.RegLoginOpts != nil {
1027+
err = ociChartRepo.Login(clientOpts.RegLoginOpts...)
10191028
if err != nil {
10201029
errs = append(errs, fmt.Errorf("failed to login to OCI chart repository: %w", err))
10211030
// clean up the credentialsFile

internal/controller/helmchart_controller_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,7 +2205,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
22052205
registryOpts registryOptions
22062206
secretOpts secretOptions
22072207
secret *corev1.Secret
2208-
withTLS bool
2208+
insecure bool
22092209
provider string
22102210
providerImg string
22112211
want sreconcile.Result
@@ -2302,7 +2302,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
23022302
registryOpts: registryOptions{
23032303
withTLS: true,
23042304
},
2305-
withTLS: true,
2305+
insecure: true,
23062306
secretOpts: secretOptions{
23072307
username: testRegistryUsername,
23082308
password: testRegistryPassword,
@@ -2326,7 +2326,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
23262326
registryOpts: registryOptions{
23272327
withTLS: true,
23282328
},
2329-
withTLS: true,
2329+
insecure: true,
23302330
secretOpts: secretOptions{
23312331
username: testRegistryUsername,
23322332
password: testRegistryPassword,
@@ -2361,9 +2361,9 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
23612361

23622362
server, err := setupRegistryServer(ctx, workspaceDir, tt.registryOpts)
23632363
g.Expect(err).NotTo(HaveOccurred())
2364-
if tt.withTLS {
2365-
defer server.stopSrv()
2366-
}
2364+
t.Cleanup(func() {
2365+
server.Close()
2366+
})
23672367

23682368
// Load a test chart
23692369
chartData, err := os.ReadFile(chartPath)

internal/controller/helmrepository_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc
398398
return sreconcile.ResultEmpty, e
399399
}
400400

401-
clientOpts, err := getter.GetClientOpts(ctx, r.Client, obj, normalizedURL)
401+
clientOpts, _, err := getter.GetClientOpts(ctx, r.Client, obj, normalizedURL)
402402
if err != nil {
403403
if errors.Is(err, getter.ErrDeprecatedTLSConfig) {
404404
ctrl.LoggerFrom(ctx).

internal/controller/helmrepository_controller_oci.go

Lines changed: 51 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -307,50 +307,55 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, sp *patch.S
307307
conditions.Delete(obj, meta.StalledCondition)
308308

309309
var (
310-
authenticator authn.Authenticator
311-
keychain authn.Keychain
312-
tlsConfig *tls.Config
313-
tmpCertsDir string
314-
tlsLoginOpt helmreg.LoginOption
315-
err error
310+
authenticator authn.Authenticator
311+
keychain authn.Keychain
312+
tlsConfig *tls.Config
313+
tlsBytes *getter.TLSBytes
314+
certFile, keyFile, caFile string
315+
err error
316316
)
317317
// Configure any authentication related options.
318318
if obj.Spec.SecretRef != nil {
319-
// Attempt to retrieve secret.
320-
name := types.NamespacedName{
321-
Namespace: obj.GetNamespace(),
322-
Name: obj.Spec.SecretRef.Name,
323-
}
324-
var secret corev1.Secret
325-
if err := r.Client.Get(ctx, name, &secret); err != nil {
326-
conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error())
327-
result, retErr = ctrl.Result{}, err
328-
return
329-
}
330-
keychain, err = authFromSecret(ctx, r.Client, obj.Spec.URL, secret)
319+
secret, err := r.getSecret(ctx, obj)
331320
if err != nil {
332321
conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error())
333322
result, retErr = ctrl.Result{}, err
334323
return
335324
}
336-
tlsConfig, err = getter.TLSClientConfigFromSecret(secret, obj.Spec.URL)
325+
keychain, err = authFromSecret(ctx, r.Client, obj.Spec.URL, *secret)
337326
if err != nil {
338327
conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error())
339328
result, retErr = ctrl.Result{}, err
340329
return
341330
}
342-
tlsLoginOpt, tmpCertsDir, err = makeTLSLoginOption(&secret)
331+
tlsConfig, tlsBytes, err = getter.TLSClientConfigFromSecret(*secret, obj.Spec.URL)
343332
if err != nil {
344333
conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error())
345334
result, retErr = ctrl.Result{}, err
346335
return
347336
}
348-
defer func() {
349-
if err := os.RemoveAll(tmpCertsDir); err != nil {
350-
r.eventLogf(ctx, obj, corev1.EventTypeWarning, meta.FailedReason,
351-
"failed to delete temporary certificates directory: %s", err)
337+
// Now that we have checked for TLS config, persist the certs files to the path if needed.
338+
if tlsBytes != nil {
339+
certsTmpDir, err := os.MkdirTemp("", "helm-repo-oci-certs")
340+
if err != nil {
341+
conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error())
342+
result, retErr = ctrl.Result{}, err
343+
return
352344
}
353-
}()
345+
defer func() {
346+
if err := os.RemoveAll(certsTmpDir); err != nil {
347+
r.eventLogf(ctx, obj, corev1.EventTypeWarning, meta.FailedReason,
348+
"failed to delete temporary certificates directory: %s", err)
349+
}
350+
}()
351+
352+
certFile, keyFile, caFile, err = getter.StoreCertsFiles(tlsBytes, certsTmpDir)
353+
if err != nil {
354+
conditions.MarkFalse(obj, meta.ReadyCondition, sourcev1.AuthenticationFailedReason, err.Error())
355+
result, retErr = ctrl.Result{}, err
356+
return
357+
}
358+
}
354359
} else if obj.Spec.Provider != helmv1.GenericOCIProvider && obj.Spec.Type == helmv1.HelmRepositoryTypeOCI {
355360
auth, authErr := soci.OIDCAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider)
356361
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
@@ -401,8 +406,11 @@ func (r *HelmRepositoryOCIReconciler) reconcile(ctx context.Context, sp *patch.S
401406
// Attempt to login to the registry if credentials are provided.
402407
if loginOpt != nil {
403408
opts := []helmreg.LoginOption{loginOpt}
404-
if tlsLoginOpt != nil {
405-
opts = append(opts, tlsLoginOpt)
409+
if tlsBytes != nil {
410+
tlsLoginOpt := registry.TLSLoginOption(certFile, keyFile, caFile)
411+
if tlsLoginOpt != nil {
412+
opts = append(opts, tlsLoginOpt)
413+
}
406414
}
407415
err = chartRepo.Login(opts...)
408416
if err != nil {
@@ -430,6 +438,22 @@ func (r *HelmRepositoryOCIReconciler) reconcileDelete(ctx context.Context, obj *
430438
return ctrl.Result{}, nil
431439
}
432440

441+
func (r *HelmRepositoryOCIReconciler) getSecret(ctx context.Context, obj *helmv1.HelmRepository) (*corev1.Secret, error) {
442+
if obj.Spec.SecretRef == nil {
443+
return nil, nil
444+
}
445+
name := types.NamespacedName{
446+
Namespace: obj.GetNamespace(),
447+
Name: obj.Spec.SecretRef.Name,
448+
}
449+
var secret corev1.Secret
450+
err := r.Client.Get(ctx, name, &secret)
451+
if err != nil {
452+
return nil, err
453+
}
454+
return &secret, nil
455+
}
456+
433457
// eventLogf records events, and logs at the same time.
434458
//
435459
// This log is different from the debug log in the EventRecorder, in the sense
@@ -471,80 +495,6 @@ func makeLoginOption(auth authn.Authenticator, keychain authn.Keychain, registry
471495
return nil, nil
472496
}
473497

474-
func makeTLSLoginOption(secret *corev1.Secret) (helmreg.LoginOption, string, error) {
475-
var errs []error
476-
certFile, keyFile, caFile, tmpDir, err := certsFilesFromSecret(secret)
477-
if err != nil {
478-
errs = append(errs, err)
479-
if tmpDir != "" {
480-
if err := os.RemoveAll(tmpDir); err != nil {
481-
errs = append(errs, err)
482-
}
483-
}
484-
return nil, "", kerrors.NewAggregate(errs)
485-
}
486-
487-
if (certFile != "" && keyFile != "") || caFile != "" {
488-
return helmreg.LoginOptTLSClientConfig(certFile, keyFile, caFile), tmpDir, nil
489-
}
490-
491-
return nil, "", nil
492-
}
493-
494-
func certsFilesFromSecret(secret *corev1.Secret) (string, string, string, string, error) {
495-
certBytes, keyBytes, caBytes := secret.Data["certFile"], secret.Data["keyFile"], secret.Data["caFile"]
496-
switch {
497-
case len(certBytes)+len(keyBytes)+len(caBytes) == 0:
498-
return "", "", "", "", nil
499-
case (len(certBytes) > 0 && len(keyBytes) == 0) || (len(keyBytes) > 0 && len(certBytes) == 0):
500-
return "", "", "", "", fmt.Errorf("invalid '%s' secret data: fields 'certFile' and 'keyFile' require each other's presence",
501-
secret.Name)
502-
}
503-
504-
var (
505-
certFile string
506-
keyFile string
507-
caFile string
508-
err error
509-
)
510-
511-
// create temporary folder to store the certs
512-
tmpDir, err := os.MkdirTemp("", "helm-repo-oci-certs")
513-
if err != nil {
514-
return "", "", "", "", err
515-
}
516-
517-
if len(certBytes) > 0 && len(keyBytes) > 0 {
518-
certFile, err = writeTofile(certBytes, "cert.pem", tmpDir)
519-
if err != nil {
520-
return "", "", "", "", err
521-
}
522-
keyFile, err = writeTofile(keyBytes, "key.pem", tmpDir)
523-
if err != nil {
524-
return "", "", "", "", err
525-
}
526-
}
527-
if len(caBytes) > 0 {
528-
caFile, err = writeTofile(caBytes, "ca.pem", tmpDir)
529-
if err != nil {
530-
return "", "", "", "", err
531-
}
532-
}
533-
return certFile, keyFile, caFile, tmpDir, nil
534-
}
535-
536-
func writeTofile(data []byte, filename, tmpDir string) (string, error) {
537-
file, err := os.CreateTemp(tmpDir, filename)
538-
if err != nil {
539-
return "", err
540-
}
541-
defer file.Close()
542-
if _, err := file.Write(data); err != nil {
543-
return "", err
544-
}
545-
return file.Name(), nil
546-
}
547-
548498
func conditionsDiff(a, b []string) []string {
549499
bMap := make(map[string]struct{}, len(b))
550500
for _, j := range b {

0 commit comments

Comments
 (0)