Implement core pkg/runtime/secrets migration
Updates pkg/runtime from v0.60.0 to v0.63.0 and migrates core secret handling components to use the consolidated pkg/runtime/secrets package. This includes basic auth, TLS config, and OCI registry authentication for Bucket and OCIRepository controllers. Removes internal wrapper functions in favor of direct pkg/runtime/secrets usage. Signed-off-by: cappyzawa <cappyzawa@gmail.com>
This commit is contained in:
parent
8f77ed4981
commit
3cd259eaf2
2
go.mod
2
go.mod
|
@ -37,7 +37,7 @@ require (
|
|||
github.com/fluxcd/pkg/lockedfile v0.6.0
|
||||
github.com/fluxcd/pkg/masktoken v0.7.0
|
||||
github.com/fluxcd/pkg/oci v0.49.0
|
||||
github.com/fluxcd/pkg/runtime v0.60.0
|
||||
github.com/fluxcd/pkg/runtime v0.63.0
|
||||
github.com/fluxcd/pkg/sourceignore v0.12.0
|
||||
github.com/fluxcd/pkg/ssh v0.19.0
|
||||
github.com/fluxcd/pkg/tar v0.12.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -391,8 +391,8 @@ github.com/fluxcd/pkg/masktoken v0.7.0 h1:pitmyOg2pUVdW+nn2Lk/xqm2TaA08uxvOC0ns3
|
|||
github.com/fluxcd/pkg/masktoken v0.7.0/go.mod h1:Lc1uoDjO1GY6+YdkK+ZqqBIBWquyV58nlSJ5S1N1IYU=
|
||||
github.com/fluxcd/pkg/oci v0.49.0 h1:L8/dmNSIzqu6X8vzIkPLrW8NAF7Et/SnOuI8WJkXeq8=
|
||||
github.com/fluxcd/pkg/oci v0.49.0/go.mod h1:iZkF4bQTpc6YOU5IJWMBp0Q8voGm7bkMYiAarJ9407U=
|
||||
github.com/fluxcd/pkg/runtime v0.60.0 h1:d++EkV3FlycB+bzakB5NumwY4J8xts8i7lbvD6jBLeU=
|
||||
github.com/fluxcd/pkg/runtime v0.60.0/go.mod h1:UeU0/eZLErYC/1bTmgzBfNXhiHy9fuQzjfLK0HxRgxY=
|
||||
github.com/fluxcd/pkg/runtime v0.63.0 h1:55J7ascGmXyTXWGwhD21N9fU7jC1l5rhdzjgNXs6aZg=
|
||||
github.com/fluxcd/pkg/runtime v0.63.0/go.mod h1:7pxGvaU0Yy1cDIUhiHAHhCx2yCLnkcVsplbYZG6j4JY=
|
||||
github.com/fluxcd/pkg/sourceignore v0.12.0 h1:jCIe6d50rQ3wdXPF0+PhhqN0XrTRIq3upMomPelI8Mw=
|
||||
github.com/fluxcd/pkg/sourceignore v0.12.0/go.mod h1:dc0zvkuXM5OgL/b3IkrVuwvPjj1zJn4NBUMH45uJ4Y0=
|
||||
github.com/fluxcd/pkg/ssh v0.19.0 h1:njSwNJQZ+3TGhBXshU/2TbqvooMbf6lQzFn7w6vuaKI=
|
||||
|
|
|
@ -50,6 +50,7 @@ import (
|
|||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
rreconcile "github.com/fluxcd/pkg/runtime/reconcile"
|
||||
"github.com/fluxcd/pkg/runtime/secrets"
|
||||
"github.com/fluxcd/pkg/sourceignore"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||
|
@ -58,7 +59,6 @@ import (
|
|||
"github.com/fluxcd/source-controller/internal/index"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||
"github.com/fluxcd/source-controller/internal/tls"
|
||||
"github.com/fluxcd/source-controller/pkg/azure"
|
||||
"github.com/fluxcd/source-controller/pkg/gcp"
|
||||
"github.com/fluxcd/source-controller/pkg/minio"
|
||||
|
@ -480,11 +480,14 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
|
|||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
tlsConfig, err := r.getTLSConfig(ctx, obj.Spec.CertSecretRef, obj.GetNamespace(), obj.Spec.Endpoint)
|
||||
if err != nil {
|
||||
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||
return sreconcile.ResultEmpty, e
|
||||
var tlsConfig *stdtls.Config
|
||||
if obj.Spec.CertSecretRef != nil {
|
||||
tlsConfig, err = r.getTLSConfig(ctx, obj.Spec.CertSecretRef, obj.GetNamespace(), obj.Spec.Endpoint)
|
||||
if err != nil {
|
||||
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
}
|
||||
stsSecret, err := r.getSTSSecret(ctx, obj)
|
||||
if err != nil {
|
||||
|
@ -757,17 +760,19 @@ func (r *BucketReconciler) getSecret(ctx context.Context, secretRef *meta.LocalO
|
|||
// Secret reference, namespace and endpoint.
|
||||
func (r *BucketReconciler) getTLSConfig(ctx context.Context,
|
||||
secretRef *meta.LocalObjectReference, namespace, endpoint string) (*stdtls.Config, error) {
|
||||
certSecret, err := r.getSecret(ctx, secretRef, namespace)
|
||||
if err != nil || certSecret == nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig, _, err := tls.KubeTLSClientConfigFromSecret(*certSecret, endpoint)
|
||||
tlsConfig, err := secrets.TLSConfigFromSecret(ctx, r.Client, secretRef.Name, namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create TLS config: %w", err)
|
||||
}
|
||||
if tlsConfig == nil {
|
||||
return nil, fmt.Errorf("certificate secret does not contain any TLS configuration")
|
||||
}
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse endpoint URL: %w", err)
|
||||
}
|
||||
tlsConfig.ServerName = u.Hostname()
|
||||
tlsConfig.MinVersion = stdtls.VersionTLS12
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
|
@ -812,6 +817,9 @@ func (r *BucketReconciler) getSTSTLSConfig(ctx context.Context, obj *sourcev1.Bu
|
|||
if obj.Spec.STS == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if obj.Spec.STS.CertSecretRef == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return r.getTLSConfig(ctx, obj.Spec.STS.CertSecretRef, obj.GetNamespace(), obj.Spec.STS.Endpoint)
|
||||
}
|
||||
|
||||
|
|
|
@ -481,7 +481,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret 'default/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -491,7 +491,8 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -522,7 +523,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to create TLS config: secret 'default/dummy' not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -532,7 +533,8 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -547,7 +549,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "certificate secret does not contain any TLS configuration"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to create TLS config: secret 'default/dummy' must contain either 'ca.crt' or both 'tls.crt' and 'tls.key'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -563,7 +565,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret 'default/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -573,7 +575,8 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -588,7 +591,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid proxy secret '/dummy': key 'address' is missing"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid proxy secret 'default/dummy': key 'address' is missing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -604,7 +607,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret 'default/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -614,7 +617,8 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -648,7 +652,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to create TLS config: secret 'default/dummy' not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -658,7 +662,8 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -676,7 +681,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get STS TLS config: certificate secret does not contain any TLS configuration"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get STS TLS config: failed to create TLS config: secret 'default/dummy' must contain either 'ca.crt' or both 'tls.crt' and 'tls.key'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -921,6 +926,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-bucket-",
|
||||
Generation: 1,
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: sourcev1.BucketSpec{
|
||||
Timeout: &metav1.Duration{Duration: timeout},
|
||||
|
@ -1030,7 +1036,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret 'default/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -1040,7 +1046,8 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -1073,7 +1080,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret 'default/dummy': secrets \"dummy\" not found"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
@ -1083,7 +1090,8 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
|
|||
bucketName: "dummy",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummy",
|
||||
Name: "dummy",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
beforeFunc: func(obj *sourcev1.Bucket) {
|
||||
|
@ -1097,7 +1105,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
|
|||
wantErr: true,
|
||||
assertIndex: index.NewDigester(),
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid proxy secret '/dummy': key 'address' is missing"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "invalid proxy secret 'default/dummy': key 'address' is missing"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
|
||||
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
|
||||
},
|
||||
|
|
|
@ -48,11 +48,11 @@ import (
|
|||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
conditionscheck "github.com/fluxcd/pkg/runtime/conditions/check"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
"github.com/fluxcd/pkg/runtime/secrets"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||
"github.com/fluxcd/source-controller/internal/cache"
|
||||
intdigest "github.com/fluxcd/source-controller/internal/digest"
|
||||
"github.com/fluxcd/source-controller/internal/helm/getter"
|
||||
"github.com/fluxcd/source-controller/internal/helm/repository"
|
||||
intpredicates "github.com/fluxcd/source-controller/internal/predicates"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
|
@ -881,16 +881,14 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
|
|||
var tlsConf *tls.Config
|
||||
validSecret := true
|
||||
if secret != nil {
|
||||
// Extract the client options from secret, ignoring any invalid
|
||||
// value. validSecret is used to determine if the index digest
|
||||
// should be calculated below.
|
||||
var gOpts []helmgetter.Option
|
||||
var serr error
|
||||
gOpts, serr = getter.GetterOptionsFromSecret(*secret)
|
||||
// Extract the client option from secret. validSecret is used to
|
||||
// determine if the index digest should be calculated below.
|
||||
username, password, serr := secrets.BasicAuthFromSecret(ctx, k8sClient, secret.Name, secret.Namespace)
|
||||
if serr != nil {
|
||||
validSecret = false
|
||||
} else {
|
||||
getterOpts = append(getterOpts, helmgetter.WithBasicAuth(username, password))
|
||||
}
|
||||
getterOpts = append(getterOpts, gOpts...)
|
||||
repoURL := server.URL()
|
||||
if tt.url != "" {
|
||||
repoURL = tt.url
|
||||
|
|
|
@ -60,6 +60,7 @@ import (
|
|||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
rreconcile "github.com/fluxcd/pkg/runtime/reconcile"
|
||||
"github.com/fluxcd/pkg/runtime/secrets"
|
||||
"github.com/fluxcd/pkg/sourceignore"
|
||||
"github.com/fluxcd/pkg/tar"
|
||||
"github.com/fluxcd/pkg/version"
|
||||
|
@ -77,7 +78,6 @@ import (
|
|||
"github.com/fluxcd/source-controller/internal/oci/notation"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||
"github.com/fluxcd/source-controller/internal/tls"
|
||||
"github.com/fluxcd/source-controller/internal/util"
|
||||
)
|
||||
|
||||
|
@ -995,30 +995,11 @@ func (r *OCIRepositoryReconciler) getTLSConfig(ctx context.Context, obj *sourcev
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
certSecretName := types.NamespacedName{
|
||||
Namespace: obj.Namespace,
|
||||
Name: obj.Spec.CertSecretRef.Name,
|
||||
}
|
||||
var certSecret corev1.Secret
|
||||
if err := r.Get(ctx, certSecretName, &certSecret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig, _, err := tls.KubeTLSClientConfigFromSecret(certSecret, "")
|
||||
tlsConfig, err := secrets.TLSConfigFromSecret(ctx, r.Client, obj.Spec.CertSecretRef.Name, obj.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tlsConfig == nil {
|
||||
tlsConfig, _, err = tls.TLSClientConfigFromSecret(certSecret, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tlsConfig != nil {
|
||||
ctrl.LoggerFrom(ctx).
|
||||
Info("warning: specifying TLS auth data via `certFile`/`keyFile`/`caFile` is deprecated, please use `tls.crt`/`tls.key`/`ca.crt` instead")
|
||||
}
|
||||
}
|
||||
|
||||
tlsConfig.MinVersion = cryptotls.VersionTLS12
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/secrets"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
helmgetter "helm.sh/helm/v3/pkg/getter"
|
||||
helmreg "helm.sh/helm/v3/pkg/registry"
|
||||
|
@ -109,11 +110,11 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepos
|
|||
}
|
||||
|
||||
// Construct actual Helm client options.
|
||||
opts, err := GetterOptionsFromSecret(*authSecret)
|
||||
username, password, err := secrets.BasicAuthFromSecret(ctx, c, obj.Spec.SecretRef.Name, obj.GetNamespace())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to configure Helm client: %w", err)
|
||||
}
|
||||
hrOpts.GetterOpts = append(hrOpts.GetterOpts, opts...)
|
||||
hrOpts.GetterOpts = append(hrOpts.GetterOpts, helmgetter.WithBasicAuth(username, password))
|
||||
|
||||
// If the TLS config is nil, i.e. one couldn't be constructed using
|
||||
// `.spec.certSecretRef`, then try to use `.spec.secretRef`.
|
||||
|
@ -129,7 +130,7 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepos
|
|||
}
|
||||
|
||||
if ociRepo {
|
||||
hrOpts.Keychain, err = registry.LoginOptionFromSecret(url, *authSecret)
|
||||
hrOpts.Keychain, err = registry.LoginOptionFromSecretRef(ctx, c, url, obj.Spec.SecretRef.Name, obj.GetNamespace())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to configure login options: %w", err)
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package getter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// GetterOptionsFromSecret constructs a getter.Option slice for the given secret.
|
||||
// It returns the slice, or an error.
|
||||
func GetterOptionsFromSecret(secret corev1.Secret) ([]getter.Option, error) {
|
||||
var opts []getter.Option
|
||||
basicAuth, err := basicAuthFromSecret(secret)
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
if basicAuth != nil {
|
||||
opts = append(opts, basicAuth)
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// basicAuthFromSecret attempts to construct a basic auth getter.Option for the
|
||||
// given v1.Secret and returns the result.
|
||||
//
|
||||
// Secrets with no username AND password are ignored, if only one is defined it
|
||||
// returns an error.
|
||||
func basicAuthFromSecret(secret corev1.Secret) (getter.Option, error) {
|
||||
username, password := string(secret.Data["username"]), string(secret.Data["password"])
|
||||
switch {
|
||||
case username == "" && password == "":
|
||||
return nil, nil
|
||||
case username == "" || password == "":
|
||||
return nil, fmt.Errorf("invalid '%s' secret data: required fields 'username' and 'password'", secret.Name)
|
||||
}
|
||||
return getter.WithBasicAuth(username, password), nil
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package getter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
basicAuthSecretFixture = corev1.Secret{
|
||||
Data: map[string][]byte{
|
||||
"username": []byte("user"),
|
||||
"password": []byte("password"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetterOptionsFromSecret(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
secrets []corev1.Secret
|
||||
}{
|
||||
{"basic auth", []corev1.Secret{basicAuthSecretFixture}},
|
||||
{"empty", []corev1.Secret{}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
secret := corev1.Secret{Data: map[string][]byte{}}
|
||||
for _, s := range tt.secrets {
|
||||
for k, v := range s.Data {
|
||||
secret.Data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
got, err := GetterOptionsFromSecret(secret)
|
||||
if err != nil {
|
||||
t.Errorf("ClientOptionsFromSecret() error = %v", err)
|
||||
return
|
||||
}
|
||||
if len(got) != len(tt.secrets) {
|
||||
t.Errorf("ClientOptionsFromSecret() options = %v, expected = %v", got, len(tt.secrets))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_basicAuthFromSecret(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
secret corev1.Secret
|
||||
modify func(secret *corev1.Secret)
|
||||
wantErr bool
|
||||
wantNil bool
|
||||
}{
|
||||
{"username and password", basicAuthSecretFixture, nil, false, false},
|
||||
{"without username", basicAuthSecretFixture, func(s *corev1.Secret) { delete(s.Data, "username") }, true, true},
|
||||
{"without password", basicAuthSecretFixture, func(s *corev1.Secret) { delete(s.Data, "password") }, true, true},
|
||||
{"empty", corev1.Secret{}, nil, false, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
secret := tt.secret.DeepCopy()
|
||||
if tt.modify != nil {
|
||||
tt.modify(secret)
|
||||
}
|
||||
got, err := basicAuthFromSecret(*secret)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("BasicAuthFromSecret() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if tt.wantNil && got != nil {
|
||||
t.Error("BasicAuthFromSecret() != nil")
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -18,16 +18,20 @@ package registry
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
"github.com/fluxcd/pkg/runtime/secrets"
|
||||
"github.com/fluxcd/source-controller/internal/helm/common"
|
||||
"github.com/fluxcd/source-controller/internal/oci"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// helper is a subset of the Docker credential helper credentials.Helper interface used by NewKeychainFromHelper.
|
||||
|
@ -86,6 +90,35 @@ func LoginOptionFromSecret(registryURL string, secret corev1.Secret) (authn.Keyc
|
|||
return authn.NewKeychainFromHelper(helper{registry: parsedURL.Host, username: username, password: password}), nil
|
||||
}
|
||||
|
||||
// LoginOptionFromSecretRef derives authentication data using runtime/secrets.
|
||||
func LoginOptionFromSecretRef(ctx context.Context, c client.Client, registryURL, secretName, namespace string) (authn.Keychain, error) {
|
||||
parsedURL, err := url.Parse(registryURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse registry URL '%s' while reconciling Secret '%s/%s': %w",
|
||||
registryURL, namespace, secretName, err)
|
||||
}
|
||||
|
||||
secretKey := client.ObjectKey{Name: secretName, Namespace: namespace}
|
||||
var secret corev1.Secret
|
||||
if err := c.Get(ctx, secretKey, &secret); err != nil {
|
||||
return nil, fmt.Errorf("failed to get secret '%s/%s': %w", namespace, secretName, err)
|
||||
}
|
||||
|
||||
if secret.Type == corev1.SecretTypeDockerConfigJson {
|
||||
return LoginOptionFromSecret(registryURL, secret)
|
||||
}
|
||||
|
||||
username, password, err := secrets.BasicAuthFromSecret(ctx, c, secretName, namespace)
|
||||
if err != nil {
|
||||
if errors.Is(err, secrets.ErrKeyNotFound) {
|
||||
return oci.Anonymous{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return authn.NewKeychainFromHelper(helper{registry: parsedURL.Host, username: username, password: password}), nil
|
||||
}
|
||||
|
||||
// KeyChainAdaptHelper returns an ORAS credentials callback configured with the authorization data
|
||||
// from the given authn keychain. This allows for example to make use of credential helpers from
|
||||
// cloud providers.
|
||||
|
|
Loading…
Reference in New Issue