Skip to content

Commit 27e121a

Browse files
committed
Refactor internal OCI package
Signed-off-by: Stefan Prodan <[email protected]>
1 parent 21af88f commit 27e121a

File tree

5 files changed

+47
-44
lines changed

5 files changed

+47
-44
lines changed

controllers/ocirepository_controller.go

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
311311
}
312312
options = append(options, crane.WithAuthFromKeychain(keychain))
313313

314-
if _, ok := keychain.(util.Anonymous); obj.Spec.Provider != sourcev1.GenericOCIProvider && ok {
314+
if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != sourcev1.GenericOCIProvider && ok {
315315
auth, authErr := oidcAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider)
316316
if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) {
317317
e := serror.NewGeneric(
@@ -409,22 +409,27 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
409409
}
410410
}()
411411

412+
// Verify artifact
413+
if obj.Spec.Verify == nil {
414+
// Remove old observations if verification was disabled
415+
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
416+
} else if !obj.GetArtifact().HasRevision(revision) || conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation {
417+
provider := obj.Spec.Verify.Provider
418+
err := r.verifyOCISourceSignature(ctx, obj, url, keychain)
419+
if err != nil {
420+
e := serror.NewGeneric(
421+
fmt.Errorf("failed to verify the signature using provider '%s': %w", provider, err),
422+
sourcev1.VerificationError,
423+
)
424+
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
425+
return sreconcile.ResultEmpty, e
426+
}
427+
428+
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of digest %s", revision)
429+
}
430+
412431
// Extract the content of the first artifact layer
413432
if !obj.GetArtifact().HasRevision(revision) {
414-
if obj.Spec.Verify != nil {
415-
provider := obj.Spec.Verify.Provider
416-
err := r.verifyOCISourceSignature(ctx, obj, url, keychain)
417-
if err != nil {
418-
e := serror.NewGeneric(
419-
fmt.Errorf("failed to verify OCI image signature '%s' using provider '%s': %w", url, provider, err),
420-
sourcev1.VerificationError,
421-
)
422-
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
423-
return sreconcile.ResultEmpty, e
424-
}
425-
426-
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "OCI image %s with digest %s verified.", url, revision)
427-
}
428433
layers, err := img.Layers()
429434
if err != nil {
430435
e := serror.NewGeneric(
@@ -512,7 +517,6 @@ func (r *OCIRepositoryReconciler) verifyOCISourceSignature(ctx context.Context,
512517
case "cosign":
513518
defaultCosignOciOpts := []soci.Options{
514519
soci.WithAuthnKeychain(keychain),
515-
soci.WithContext(ctxTimeout),
516520
}
517521

518522
ref, err := name.ParseReference(url)
@@ -536,12 +540,12 @@ func (r *OCIRepositoryReconciler) verifyOCISourceSignature(ctx context.Context,
536540
for k, data := range pubSecret.Data {
537541
// search for public keys in the secret
538542
if strings.HasSuffix(k, ".pub") {
539-
verifier, err := soci.New(append(defaultCosignOciOpts, soci.WithPublicKey(data))...)
543+
verifier, err := soci.NewVerifier(ctxTimeout, append(defaultCosignOciOpts, soci.WithPublicKey(data))...)
540544
if err != nil {
541545
return err
542546
}
543547

544-
signatures, _, err := verifier.VerifyImageSignatures(ref)
548+
signatures, _, err := verifier.VerifyImageSignatures(ctxTimeout, ref)
545549
if err != nil {
546550
continue
547551
}
@@ -562,12 +566,12 @@ func (r *OCIRepositoryReconciler) verifyOCISourceSignature(ctx context.Context,
562566

563567
// if no secret is provided, try keyless verification
564568
ctrl.LoggerFrom(ctx).Info("no secret reference is provided, trying to verify the image using keyless approach")
565-
verifier, err := soci.New(defaultCosignOciOpts...)
569+
verifier, err := soci.NewVerifier(ctxTimeout, defaultCosignOciOpts...)
566570
if err != nil {
567571
return err
568572
}
569573

570-
signatures, _, err := verifier.VerifyImageSignatures(ref)
574+
signatures, _, err := verifier.VerifyImageSignatures(ctxTimeout, ref)
571575
if err != nil {
572576
return err
573577
}
@@ -689,7 +693,7 @@ func (r *OCIRepositoryReconciler) keychain(ctx context.Context, obj *sourcev1.OC
689693

690694
// if no pullsecrets available return an AnonymousKeychain
691695
if len(pullSecretNames) == 0 {
692-
return util.Anonymous{}, nil
696+
return soci.Anonymous{}, nil
693697
}
694698

695699
// lookup image pull secrets

controllers/ocirepository_controller_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,22 +1042,22 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature(t *testing.T) {
10421042
assertConditions: []metav1.Condition{
10431043
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new digest '<digest>' for '<url>'"),
10441044
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new digest '<digest>' for '<url>'"),
1045-
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "OCI image <url> with digest <digest> verified."),
1045+
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of digest <digest>"),
10461046
},
10471047
},
10481048
{
1049-
name: "not signed image should not pass verification",
1049+
name: "unsigned image should not pass verification",
10501050
reference: &sourcev1.OCIRepositoryRef{
10511051
Tag: "6.1.5",
10521052
},
10531053
digest: img5.digest.Hex,
10541054
wantErr: true,
1055-
wantErrMsg: "failed to verify OCI image signature '<url>' using provider 'cosign': no matching signatures were found for '<url>",
1055+
wantErrMsg: "failed to verify the signature using provider 'cosign': no matching signatures were found for '<url>'",
10561056
want: sreconcile.ResultEmpty,
10571057
assertConditions: []metav1.Condition{
10581058
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new digest '<digest>' for '<url>'"),
10591059
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new digest '<digest>' for '<url>'"),
1060-
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify OCI image signature '<url>' using provider '<provider>': no matching signatures were found for '<url>'"),
1060+
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, sourcev1.VerificationError, "failed to verify the signature using provider '<provider>': no matching signatures were found for '<url>'"),
10611061
},
10621062
},
10631063
}

docs/spec/v1beta2/ocirepositories.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ data:
455455
key2.pub: <BASE64>
456456
```
457457

458-
Note that the keys must have the `.pub` extension for Flux to make user of them.
458+
Note that the keys must have the `.pub` extension for Flux to make use of them.
459459

460460
#### Keyless verification
461461

@@ -482,7 +482,7 @@ The controller verifies the signatures using the Fulcio root CA and the Rekor
482482
instance hosted at [rekor.sigstore.dev](https://rekor.sigstore.dev/).
483483

484484
Note that keyless verification is an **experimental feature**, using
485-
custom root CAs or self-hosted Rekor instances are not currency supported.
485+
custom root CAs or self-hosted Rekor instances are not currently supported.
486486

487487
### Suspend
488488

@@ -839,6 +839,14 @@ and is only present on the OCIRepository while the status value is `"True"`.
839839
There may be more arbitrary values for the `reason` field to provide accurate
840840
reason for a condition.
841841

842+
In addition to the above Condition types, when the signature
843+
[verification](#verification) fails. A condition with
844+
the following attributes is added to the GitRepository's `.status.conditions`:
845+
846+
- `type: SourceVerified`
847+
- `status: "False"`
848+
- `reason: VerificationError`
849+
842850
While the OCIRepository has one or more of these Conditions, the controller
843851
will continue to attempt to produce an Artifact for the resource with an
844852
exponential backoff, until it succeeds and the OCIRepository is marked as

internal/util/auth.go renamed to internal/oci/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package util
17+
package oci
1818

1919
import "github.com/google/go-containerregistry/pkg/authn"
2020

internal/oci/oci.go renamed to internal/oci/verifier.go

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838
type options struct {
3939
PublicKey []byte
4040
Keychain authn.Keychain
41-
Context context.Context
4241
}
4342

4443
// Options is a function that configures the options applied to a Verifier.
@@ -57,20 +56,13 @@ func WithAuthnKeychain(keychain authn.Keychain) Options {
5756
}
5857
}
5958

60-
func WithContext(ctx context.Context) Options {
61-
return func(opts *options) {
62-
opts.Context = ctx
63-
}
64-
}
65-
6659
// Verifier is a struct which is responsible for executing verification logic.
6760
type Verifier struct {
68-
opts *cosign.CheckOpts
69-
context context.Context
61+
opts *cosign.CheckOpts
7062
}
7163

72-
// New initializes a new Verifier.
73-
func New(opts ...Options) (*Verifier, error) {
64+
// NewVerifier initializes a new Verifier.
65+
func NewVerifier(ctx context.Context, opts ...Options) (*Verifier, error) {
7466
o := options{}
7567
for _, opt := range opts {
7668
opt(&o)
@@ -79,7 +71,7 @@ func New(opts ...Options) (*Verifier, error) {
7971
checkOpts := &cosign.CheckOpts{}
8072

8173
ro := coptions.RegistryOptions{}
82-
co, err := ro.ClientOpts(o.Context)
74+
co, err := ro.ClientOpts(ctx)
8375
if err != nil {
8476
return nil, err
8577
}
@@ -124,12 +116,11 @@ func New(opts ...Options) (*Verifier, error) {
124116
}
125117

126118
return &Verifier{
127-
opts: checkOpts,
128-
context: o.Context,
119+
opts: checkOpts,
129120
}, nil
130121
}
131122

132123
// VerifyImageSignatures verify the authenticity of the given ref OCI image.
133-
func (v *Verifier) VerifyImageSignatures(ref name.Reference) ([]oci.Signature, bool, error) {
134-
return cosign.VerifyImageSignatures(v.context, ref, v.opts)
124+
func (v *Verifier) VerifyImageSignatures(ctx context.Context, ref name.Reference) ([]oci.Signature, bool, error) {
125+
return cosign.VerifyImageSignatures(ctx, ref, v.opts)
135126
}

0 commit comments

Comments
 (0)