@@ -95,6 +95,8 @@ var helmChartReadyCondition = summarize.Conditions{
95
95
},
96
96
}
97
97
98
+ const KeyringFileName = "pubring.gpg"
99
+
98
100
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts,verbs=get;list;watch;create;update;patch;delete
99
101
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts/status,verbs=get;update;patch
100
102
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts/finalizers,verbs=get;create;update;patch;delete
@@ -467,13 +469,20 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
467
469
opts .VersionMetadata = strconv .FormatInt (obj .Generation , 10 )
468
470
}
469
471
472
+ var keyring []byte
473
+ keyring , err = r .getProvenanceKeyring (ctx , obj )
474
+ if err != nil {
475
+ conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , sourcev1 .AuthenticationFailedReason , err .Error ())
476
+ return sreconcile .ResultEmpty , err
477
+ }
478
+
470
479
// Build the chart
471
480
ref := chart.RemoteReference {Name : obj .Spec .Chart , Version : obj .Spec .Version }
472
- build , err := cb .Build (ctx , ref , util .TempPathForObj ("" , ".tgz" , obj ), opts )
481
+ build , err := cb .Build (ctx , ref , util .TempPathForObj ("" , ".tgz" , obj ), opts , keyring )
482
+
473
483
if err != nil {
474
484
return sreconcile .ResultEmpty , err
475
485
}
476
-
477
486
* b = * build
478
487
return sreconcile .ResultSuccess , nil
479
488
}
@@ -590,13 +599,19 @@ func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj
590
599
}
591
600
opts .VersionMetadata += strconv .FormatInt (obj .Generation , 10 )
592
601
}
602
+ var keyring []byte
603
+ keyring , err = r .getProvenanceKeyring (ctx , obj )
604
+ if err != nil {
605
+ conditions .MarkTrue (obj , sourcev1 .FetchFailedCondition , sourcev1 .AuthenticationFailedReason , err .Error ())
606
+ return sreconcile .ResultEmpty , err
607
+ }
593
608
594
609
// Build chart
595
610
cb := chart .NewLocalBuilder (dm )
596
611
build , err := cb .Build (ctx , chart.LocalReference {
597
612
WorkDir : sourceDir ,
598
613
Path : chartPath ,
599
- }, util .TempPathForObj ("" , ".tgz" , obj ), opts )
614
+ }, util .TempPathForObj ("" , ".tgz" , obj ), opts , keyring )
600
615
if err != nil {
601
616
return sreconcile .ResultEmpty , err
602
617
}
@@ -670,6 +685,18 @@ func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, obj *source
670
685
return sreconcile .ResultEmpty , e
671
686
}
672
687
688
+ // the provenance file artifact is not recorded, but it shadows the HelmChart artifact
689
+ // under the assumption that the file is always available at "chart.tgz.prov"
690
+ if b .ProvFilePath != "" {
691
+ provArtifact := r .Storage .NewArtifactFor (obj .Kind , obj .GetObjectMeta (), b .Version , fmt .Sprintf ("%s-%s.tgz.prov" , b .Name , b .Version ))
692
+ if err = r .Storage .CopyFromPath (& provArtifact , b .ProvFilePath ); err != nil {
693
+ return sreconcile .ResultEmpty , & serror.Event {
694
+ Err : fmt .Errorf ("unable to copy Helm chart provenance file to storage: %w" , err ),
695
+ Reason : sourcev1 .StorageOperationFailedReason ,
696
+ }
697
+ }
698
+ }
699
+
673
700
// Record it on the object
674
701
obj .Status .Artifact = artifact .DeepCopy ()
675
702
obj .Status .ObservedChartName = b .Name
@@ -861,6 +888,22 @@ func (r *HelmChartReconciler) getHelmRepositorySecret(ctx context.Context, repos
861
888
return & secret , nil
862
889
}
863
890
891
+ func (r * HelmChartReconciler ) getVerificationKeyringSecret (ctx context.Context , chart * sourcev1.HelmChart ) (* corev1.Secret , error ) {
892
+ if chart .Spec .VerificationKeyring == nil {
893
+ return nil , nil
894
+ }
895
+ name := types.NamespacedName {
896
+ Namespace : chart .GetNamespace (),
897
+ Name : chart .Spec .VerificationKeyring .SecretRef .Name ,
898
+ }
899
+ var secret corev1.Secret
900
+ err := r .Client .Get (ctx , name , & secret )
901
+ if err != nil {
902
+ return nil , err
903
+ }
904
+ return & secret , nil
905
+ }
906
+
864
907
func (r * HelmChartReconciler ) indexHelmRepositoryByURL (o client.Object ) []string {
865
908
repo , ok := o .(* sourcev1.HelmRepository )
866
909
if ! ok {
@@ -1021,3 +1064,33 @@ func reasonForBuild(build *chart.Build) string {
1021
1064
}
1022
1065
return sourcev1 .ChartPullSucceededReason
1023
1066
}
1067
+
1068
+ func (r * HelmChartReconciler ) getProvenanceKeyring (ctx context.Context , chart * sourcev1.HelmChart ) ([]byte , error ) {
1069
+ if chart .Spec .VerificationKeyring == nil {
1070
+ return nil , nil
1071
+ }
1072
+ name := types.NamespacedName {
1073
+ Namespace : chart .GetNamespace (),
1074
+ Name : chart .Spec .VerificationKeyring .SecretRef .Name ,
1075
+ }
1076
+ var secret corev1.Secret
1077
+ err := r .Client .Get (ctx , name , & secret )
1078
+ if err != nil {
1079
+ e := & serror.Event {
1080
+ Err : fmt .Errorf ("failed to get secret '%s': %w" , chart .Spec .VerificationKeyring .SecretRef .Name , err ),
1081
+ Reason : sourcev1 .AuthenticationFailedReason ,
1082
+ }
1083
+ return nil , e
1084
+ }
1085
+ key := chart .Spec .VerificationKeyring .Key
1086
+ if val , ok := secret .Data [key ]; ! ok {
1087
+ err = fmt .Errorf ("secret doesn't contain the advertised verification keyring name %s" , key )
1088
+ e := & serror.Event {
1089
+ Err : fmt .Errorf ("invalid secret '%s': %w" , secret .GetName (), err ),
1090
+ Reason : sourcev1 .AuthenticationFailedReason ,
1091
+ }
1092
+ return nil , e
1093
+ } else {
1094
+ return val , nil
1095
+ }
1096
+ }
0 commit comments