@@ -95,6 +95,8 @@ var helmChartReadyCondition = summarize.Conditions{
9595 },
9696}
9797
98+ const KeyringFileName = "pubring.gpg"
99+
98100// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts,verbs=get;list;watch;create;update;patch;delete
99101// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmcharts/status,verbs=get;update;patch
100102// +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 *
467469 opts .VersionMetadata = strconv .FormatInt (obj .Generation , 10 )
468470 }
469471
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+
470479 // Build the chart
471480 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+
473483 if err != nil {
474484 return sreconcile .ResultEmpty , err
475485 }
476-
477486 * b = * build
478487 return sreconcile .ResultSuccess , nil
479488}
@@ -590,13 +599,19 @@ func (r *HelmChartReconciler) buildFromTarballArtifact(ctx context.Context, obj
590599 }
591600 opts .VersionMetadata += strconv .FormatInt (obj .Generation , 10 )
592601 }
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+ }
593608
594609 // Build chart
595610 cb := chart .NewLocalBuilder (dm )
596611 build , err := cb .Build (ctx , chart.LocalReference {
597612 WorkDir : sourceDir ,
598613 Path : chartPath ,
599- }, util .TempPathForObj ("" , ".tgz" , obj ), opts )
614+ }, util .TempPathForObj ("" , ".tgz" , obj ), opts , keyring )
600615 if err != nil {
601616 return sreconcile .ResultEmpty , err
602617 }
@@ -670,6 +685,18 @@ func (r *HelmChartReconciler) reconcileArtifact(ctx context.Context, obj *source
670685 return sreconcile .ResultEmpty , e
671686 }
672687
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+
673700 // Record it on the object
674701 obj .Status .Artifact = artifact .DeepCopy ()
675702 obj .Status .ObservedChartName = b .Name
@@ -861,6 +888,22 @@ func (r *HelmChartReconciler) getHelmRepositorySecret(ctx context.Context, repos
861888 return & secret , nil
862889}
863890
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+
864907func (r * HelmChartReconciler ) indexHelmRepositoryByURL (o client.Object ) []string {
865908 repo , ok := o .(* sourcev1.HelmRepository )
866909 if ! ok {
@@ -1021,3 +1064,33 @@ func reasonForBuild(build *chart.Build) string {
10211064 }
10221065 return sourcev1 .ChartPullSucceededReason
10231066}
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