@@ -20,6 +20,7 @@ import (
20
20
"context"
21
21
"crypto/tls"
22
22
"crypto/x509"
23
+ "encoding/base64"
23
24
"errors"
24
25
"fmt"
25
26
"net/http"
@@ -28,6 +29,8 @@ import (
28
29
"strings"
29
30
"time"
30
31
32
+ "github.com/fluxcd/source-controller/internal/signature"
33
+
31
34
"github.com/Masterminds/semver/v3"
32
35
"github.com/google/go-containerregistry/pkg/authn"
33
36
"github.com/google/go-containerregistry/pkg/authn/k8schain"
@@ -362,6 +365,29 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
362
365
return sreconcile .ResultEmpty , e
363
366
}
364
367
368
+ // Verify the image
369
+ if obj .Spec .Verify != nil {
370
+ provider := obj .Spec .Verify .Provider
371
+ if provider == "cosign" {
372
+ if _ , err := r .verify (ctx , obj , url ); err != nil {
373
+ e := serror .NewGeneric (
374
+ fmt .Errorf ("failed to verify '%s' using provider '%s': %w" , url , provider , err ),
375
+ sourcev1 .SourceVerifiedFailedReason ,
376
+ )
377
+ conditions .MarkFalse (obj , sourcev1 .SourceVerifiedCondition , e .Reason , e .Err .Error ())
378
+ return sreconcile .ResultEmpty , e
379
+ }
380
+ } else {
381
+ err := fmt .Errorf ("could not found the given %s provider, only valid provider for verification is: cosign" , provider )
382
+ e := serror .NewGeneric (
383
+ fmt .Errorf ("failed to verify '%s' using provider '%s': %w" , url , provider , err ),
384
+ sourcev1 .SourceVerifiedFailedReason ,
385
+ )
386
+ conditions .MarkFalse (obj , sourcev1 .SourceVerifiedCondition , e .Reason , e .Err .Error ())
387
+ return sreconcile .ResultEmpty , e
388
+ }
389
+ }
390
+
365
391
// Pull artifact from the remote container registry
366
392
img , err := crane .Pull (url , options ... )
367
393
if err != nil {
@@ -658,7 +684,6 @@ func (r *OCIRepositoryReconciler) transport(ctx context.Context, obj *sourcev1.O
658
684
tlsConfig .RootCAs = syscerts
659
685
}
660
686
return transport , nil
661
-
662
687
}
663
688
664
689
// oidcAuth generates the OIDC credential authenticator based on the specified cloud provider.
@@ -705,7 +730,8 @@ func (r *OCIRepositoryReconciler) craneOptions(ctx context.Context) []crane.Opti
705
730
// The hostname of any URL in the Status of the object are updated, to ensure
706
731
// they match the Storage server hostname of current runtime.
707
732
func (r * OCIRepositoryReconciler ) reconcileStorage (ctx context.Context ,
708
- obj * sourcev1.OCIRepository , _ * sourcev1.Artifact , _ string ) (sreconcile.Result , error ) {
733
+ obj * sourcev1.OCIRepository , _ * sourcev1.Artifact , _ string ,
734
+ ) (sreconcile.Result , error ) {
709
735
// Garbage collect previous advertised artifact(s) from storage
710
736
_ = r .garbageCollect (ctx , obj )
711
737
@@ -741,7 +767,8 @@ func (r *OCIRepositoryReconciler) reconcileStorage(ctx context.Context,
741
767
// On a successful archive, the Artifact in the Status of the object is set,
742
768
// and the symlink in the Storage is updated to its path.
743
769
func (r * OCIRepositoryReconciler ) reconcileArtifact (ctx context.Context ,
744
- obj * sourcev1.OCIRepository , metadata * sourcev1.Artifact , dir string ) (sreconcile.Result , error ) {
770
+ obj * sourcev1.OCIRepository , metadata * sourcev1.Artifact , dir string ,
771
+ ) (sreconcile.Result , error ) {
745
772
// Calculate revision
746
773
revision := metadata .Revision
747
774
@@ -885,7 +912,8 @@ func (r *OCIRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourc
885
912
// that this is a simple log. While the debug log contains complete details
886
913
// about the event.
887
914
func (r * OCIRepositoryReconciler ) eventLogf (ctx context.Context ,
888
- obj runtime.Object , eventType string , reason string , messageFmt string , args ... interface {}) {
915
+ obj runtime.Object , eventType , reason , messageFmt string , args ... interface {},
916
+ ) {
889
917
msg := fmt .Sprintf (messageFmt , args ... )
890
918
// Log and emit event.
891
919
if eventType == corev1 .EventTypeWarning {
@@ -898,7 +926,8 @@ func (r *OCIRepositoryReconciler) eventLogf(ctx context.Context,
898
926
899
927
// notify emits notification related to the reconciliation.
900
928
func (r * OCIRepositoryReconciler ) notify (ctx context.Context ,
901
- oldObj , newObj * sourcev1.OCIRepository , res sreconcile.Result , resErr error ) {
929
+ oldObj , newObj * sourcev1.OCIRepository , res sreconcile.Result , resErr error ,
930
+ ) {
902
931
// Notify successful reconciliation for new artifact and recovery from any
903
932
// failure.
904
933
if resErr == nil && res == sreconcile .ResultSuccess && newObj .Status .Artifact != nil {
@@ -942,3 +971,49 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context,
942
971
}
943
972
}
944
973
}
974
+
975
+ // notify emits notification related to the reconciliation.
976
+ func (r * OCIRepositoryReconciler ) verify (ctx context.Context , obj * sourcev1.OCIRepository , url string ) (bool , error ) {
977
+ // get the public keys from the given secret
978
+ certSecretName := types.NamespacedName {
979
+ Namespace : obj .Namespace ,
980
+ Name : obj .Spec .Verify .SecretRef .Name ,
981
+ }
982
+
983
+ var pubSecret corev1.Secret
984
+ if err := r .Get (ctx , certSecretName , & pubSecret ); err != nil {
985
+ return false , err
986
+ }
987
+
988
+ ref , err := name .ParseReference (url )
989
+ if err != nil {
990
+ return false , err
991
+ }
992
+
993
+ // traverse all public keys and try to verify the signature
994
+ // this is brute-force approach, but it is ok for now
995
+ verified := false
996
+ for _ , data := range pubSecret .Data {
997
+ pubRaw , err := base64 .StdEncoding .DecodeString (string (data ))
998
+ if err != nil {
999
+ return false , err
1000
+ }
1001
+
1002
+ verifier , err := signature .New (signature .WithPublicKey (pubRaw ))
1003
+ if err != nil {
1004
+ return false , err
1005
+ }
1006
+
1007
+ signatures , _ , err := verifier .VerifyImageSignatures (ctx , ref )
1008
+ if err != nil {
1009
+ return false , err
1010
+ }
1011
+
1012
+ if len (signatures ) > 0 {
1013
+ verified = true
1014
+ break
1015
+ }
1016
+ }
1017
+
1018
+ return verified , nil
1019
+ }
0 commit comments