@@ -20,6 +20,7 @@ import (
20
20
"context"
21
21
"crypto/tls"
22
22
"crypto/x509"
23
+ "encoding/json"
23
24
"errors"
24
25
"fmt"
25
26
"io"
@@ -72,6 +73,10 @@ import (
72
73
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
73
74
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
74
75
"github.com/fluxcd/source-controller/internal/util"
76
+
77
+ _ "github.com/notaryproject/notation-core-go/signature/cose"
78
+ _ "github.com/notaryproject/notation-core-go/signature/jws"
79
+ "github.com/notaryproject/notation-go/verifier/trustpolicy"
75
80
)
76
81
77
82
// ociRepositoryReadyCondition contains the information required to summarize a
@@ -440,7 +445,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
440
445
return sreconcile .ResultEmpty , e
441
446
}
442
447
443
- err := r .verifySignature (ctx , obj , url , opts .verifyOpts ... )
448
+ err := r .verifySignature (ctx , obj , url , opts .keychain , opts . verifyOpts ... )
444
449
if err != nil {
445
450
provider := obj .Spec .Verify .Provider
446
451
if obj .Spec .Verify .SecretRef == nil {
@@ -627,7 +632,7 @@ func (r *OCIRepositoryReconciler) digestFromRevision(revision string) string {
627
632
// verifySignature verifies the authenticity of the given image reference URL.
628
633
// First, it tries to use a key if a Secret with a valid public key is provided.
629
634
// If not, it falls back to a keyless approach for verification.
630
- func (r * OCIRepositoryReconciler ) verifySignature (ctx context.Context , obj * ociv1.OCIRepository , url string , opt ... remote.Option ) error {
635
+ func (r * OCIRepositoryReconciler ) verifySignature (ctx context.Context , obj * ociv1.OCIRepository , url string , auth authn. Keychain , opt ... remote.Option ) error {
631
636
ctxTimeout , cancel := context .WithTimeout (ctx , obj .Spec .Timeout .Duration )
632
637
defer cancel ()
633
638
@@ -700,6 +705,67 @@ func (r *OCIRepositoryReconciler) verifySignature(ctx context.Context, obj *ociv
700
705
}
701
706
702
707
return fmt .Errorf ("no matching signatures were found for '%s'" , url )
708
+ case "notation" :
709
+ ref , err := name .ParseReference (url )
710
+ if err != nil {
711
+ return err
712
+ }
713
+
714
+ // get the public keys from the given secret
715
+ if secretRef := obj .Spec .Verify .SecretRef ; secretRef != nil {
716
+ certSecretName := types.NamespacedName {
717
+ Namespace : obj .Namespace ,
718
+ Name : secretRef .Name ,
719
+ }
720
+
721
+ var pubSecret corev1.Secret
722
+ if err := r .Get (ctxTimeout , certSecretName , & pubSecret ); err != nil {
723
+ return err
724
+ }
725
+
726
+ var doc trustpolicy.Document
727
+
728
+ signatureVerified := false
729
+ for k , data := range pubSecret .Data {
730
+ if strings .HasSuffix (k , ".json" ) {
731
+ if err := json .Unmarshal (data , & doc ); err != nil {
732
+ return err
733
+ }
734
+ }
735
+ }
736
+
737
+ defaultNotaryOciOpts := []soci.NotationOptions {
738
+ soci .WithTrustStore (& doc ),
739
+ soci .WithNotaryRemoteOptions (opt ... ),
740
+ }
741
+
742
+ for k , data := range pubSecret .Data {
743
+ // search for public keys in the secret
744
+ if strings .HasSuffix (k , ".pem" ) {
745
+
746
+ verifier , err := soci .NewNotaryVerifier (append (defaultNotaryOciOpts , soci .WithNotaryPublicKey (data ), soci .WithNotaryKeychain (auth ))... )
747
+ if err != nil {
748
+ return err
749
+ }
750
+
751
+ signatures , err := verifier .Verify (ctxTimeout , ref )
752
+ if err != nil {
753
+ continue
754
+ }
755
+
756
+ if signatures {
757
+ signatureVerified = true
758
+ break
759
+ }
760
+ }
761
+ }
762
+
763
+ if ! signatureVerified {
764
+ return fmt .Errorf ("no matching signatures were found for '%s'" , url )
765
+ }
766
+ return nil
767
+ }
768
+ return nil
703
769
}
704
770
705
771
return nil
@@ -1197,6 +1263,8 @@ func makeRemoteOptions(ctxTimeout context.Context, obj *ociv1.OCIRepository, tra
1197
1263
verifyOpts : []remote.Option {},
1198
1264
}
1199
1265
1266
+ o .keychain = keychain
1267
+
1200
1268
if transport != nil {
1201
1269
o .craneOpts = append (o .craneOpts , crane .WithTransport (transport ))
1202
1270
o .verifyOpts = append (o .verifyOpts , remote .WithTransport (transport ))
@@ -1221,6 +1289,7 @@ func makeRemoteOptions(ctxTimeout context.Context, obj *ociv1.OCIRepository, tra
1221
1289
type remoteOptions struct {
1222
1290
craneOpts []crane.Option
1223
1291
verifyOpts []remote.Option
1292
+ keychain authn.Keychain
1224
1293
}
1225
1294
1226
1295
// ociContentConfigChanged evaluates the current spec with the observations
0 commit comments