Skip to content

Commit 2d22893

Browse files
committed
feat: migrate HelmRepository to runtime/secrets v0.66.0 with strict BasicAuth validation
Implement switch-case pattern for handling secret reference combinations and add strict BasicAuth validation to prevent security issues. Replace hardcoded keys with runtime/secrets constraints and refactor GetClientOpts into smaller focused functions. Eliminate duplicate Secret fetches and update tests to match runtime/secrets v0.66.0 error message format. BREAKING CHANGE: - SecretRef with partial BasicAuth data (username-only or password-only) now returns an error instead of silently falling back to other authentication methods - Invalid CA certificates now cause authentication failures even for public repositories These changes improve security by preventing unintended authentication bypass. Signed-off-by: cappyzawa <[email protected]>
1 parent 1392911 commit 2d22893

File tree

4 files changed

+206
-122
lines changed

4 files changed

+206
-122
lines changed

internal/controller/helmchart_controller_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,12 +1035,12 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) {
10351035
}
10361036
},
10371037
want: sreconcile.ResultEmpty,
1038-
wantErr: &serror.Generic{Err: errors.New("failed to get authentication secret '/invalid'")},
1038+
wantErr: &serror.Generic{Err: errors.New("failed to get authentication secret: secrets \"invalid\" not found")},
10391039
assertFunc: func(g *WithT, obj *sourcev1.HelmChart, build chart.Build) {
10401040
g.Expect(build.Complete()).To(BeFalse())
10411041

10421042
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{
1043-
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get authentication secret '/invalid'"),
1043+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get authentication secret: secrets \"invalid\" not found"),
10441044
}))
10451045
},
10461046
},
@@ -1304,12 +1304,12 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) {
13041304
}
13051305
},
13061306
want: sreconcile.ResultEmpty,
1307-
wantErr: &serror.Generic{Err: errors.New("failed to get authentication secret '/invalid'")},
1307+
wantErr: &serror.Generic{Err: errors.New("failed to get authentication secret: secrets \"invalid\" not found")},
13081308
assertFunc: func(g *WithT, obj *sourcev1.HelmChart, build chart.Build) {
13091309
g.Expect(build.Complete()).To(BeFalse())
13101310

13111311
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{
1312-
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get authentication secret '/invalid'"),
1312+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get authentication secret: secrets \"invalid\" not found"),
13131313
}))
13141314
},
13151315
},
@@ -2515,7 +2515,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) {
25152515
},
25162516
},
25172517
assertConditions: []metav1.Condition{
2518-
*conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: failed to construct Helm client's TLS config: cannot append certificate into certificate pool: invalid CA certificate"),
2518+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: failed to construct Helm client's TLS config: failed to parse CA certificate"),
25192519
},
25202520
},
25212521
{

internal/controller/helmrepository_controller_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,11 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
426426
assertConditions []metav1.Condition
427427
}{
428428
{
429-
name: "HTTPS with certSecretRef pointing to non-matching CA cert but public repo URL succeeds",
429+
name: "HTTPS with certSecretRef pointing to non-matching CA cert but public repo URL fails",
430430
protocol: "http",
431431
url: "https://stefanprodan.github.io/podinfo",
432-
want: sreconcile.ResultSuccess,
432+
want: sreconcile.ResultEmpty,
433+
wantErr: true,
433434
secret: &corev1.Secret{
434435
ObjectMeta: metav1.ObjectMeta{
435436
Name: "ca-file",
@@ -441,10 +442,18 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
441442
},
442443
beforeFunc: func(t *WithT, obj *sourcev1.HelmRepository) {
443444
obj.Spec.CertSecretRef = &meta.LocalObjectReference{Name: "ca-file"}
445+
conditions.MarkReconciling(obj, meta.ProgressingReason, "foo")
446+
conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar")
444447
},
445448
assertConditions: []metav1.Condition{
446-
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "building artifact: new index revision"),
447-
*conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new index revision"),
449+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, meta.FailedReason, "tls: failed to verify certificate: x509: certificate signed by unknown authority"),
450+
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
451+
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
452+
},
453+
afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
454+
t.Expect(chartRepo.Path).To(BeEmpty())
455+
t.Expect(chartRepo.Index).To(BeNil())
456+
t.Expect(artifact.Revision).To(BeEmpty())
448457
},
449458
},
450459
{
@@ -769,12 +778,11 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
769778
},
770779
wantErr: true,
771780
assertConditions: []metav1.Condition{
772-
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "cannot append certificate into certificate pool: invalid CA certificate"),
781+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to construct Helm client's TLS config: failed to parse CA certificate"),
773782
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
774783
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
775784
},
776785
afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
777-
// No repo index due to fetch fail.
778786
t.Expect(chartRepo.Path).To(BeEmpty())
779787
t.Expect(chartRepo.Index).To(BeNil())
780788
t.Expect(artifact.Revision).To(BeEmpty())
@@ -796,7 +804,6 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
796804
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
797805
},
798806
afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
799-
// No repo index due to fetch fail.
800807
t.Expect(chartRepo.Path).To(BeEmpty())
801808
t.Expect(chartRepo.Index).To(BeNil())
802809
t.Expect(artifact.Revision).To(BeEmpty())
@@ -818,7 +825,6 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
818825
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
819826
},
820827
afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
821-
// No repo index due to fetch fail.
822828
t.Expect(chartRepo.Path).To(BeEmpty())
823829
t.Expect(chartRepo.Index).To(BeNil())
824830
t.Expect(artifact.Revision).To(BeEmpty())
@@ -839,14 +845,13 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
839845
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
840846
},
841847
afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
842-
// No repo index due to fetch fail.
843848
t.Expect(chartRepo.Path).To(BeEmpty())
844849
t.Expect(chartRepo.Index).To(BeNil())
845850
t.Expect(artifact.Revision).To(BeEmpty())
846851
},
847852
},
848853
{
849-
name: "Malformed secret returns FetchFailed=True and returns error",
854+
name: "Malformed Basic Auth secret returns FetchFailed=True and returns error",
850855
protocol: "http",
851856
secret: &corev1.Secret{
852857
ObjectMeta: metav1.ObjectMeta{
@@ -864,12 +869,11 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
864869
},
865870
wantErr: true,
866871
assertConditions: []metav1.Condition{
867-
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "required fields 'username' and 'password"),
872+
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to configure Basic Auth: secret 'default/malformed-basic-auth': key 'password' not found"),
868873
*conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"),
869874
*conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"),
870875
},
871876
afterFunc: func(t *WithT, obj *sourcev1.HelmRepository, artifact sourcev1.Artifact, chartRepo *repository.ChartRepository) {
872-
// No repo index due to fetch fail.
873877
t.Expect(chartRepo.Path).To(BeEmpty())
874878
t.Expect(chartRepo.Index).To(BeNil())
875879
t.Expect(artifact.Revision).To(BeEmpty())

0 commit comments

Comments
 (0)