Skip to content

Commit

Permalink
Honor creation timestamp for signatures again (sigstore#3549)
Browse files Browse the repository at this point in the history
* Honor creation timestamp for signatures again

Signed-off-by: ttrabelsi <[email protected]>

* setting creation timestamp behind a feature flag to preserve current behavior

Signed-off-by: Tobias Trabelsi <[email protected]>

* review feedback

Signed-off-by: Tobias Trabelsi <[email protected]>

* additional review feedback

Signed-off-by: Tobias Trabelsi <[email protected]>

---------

Signed-off-by: ttrabelsi <[email protected]>
Signed-off-by: Tobias Trabelsi <[email protected]>
  • Loading branch information
Lerentis authored and tommyd450 committed Jun 7, 2024
1 parent e40115d commit 3f4094a
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 38 deletions.
43 changes: 23 additions & 20 deletions cmd/cosign/cli/options/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,27 @@ import (

// SignOptions is the top level wrapper for the sign command.
type SignOptions struct {
Key string
Cert string
CertChain string
Upload bool
Output string // deprecated: TODO remove when the output flag is fully deprecated
OutputSignature string // TODO: this should be the root output file arg.
OutputPayload string
OutputCertificate string
PayloadPath string
Recursive bool
Attachment string
SkipConfirmation bool
TlogUpload bool
TSAClientCACert string
TSAClientCert string
TSAClientKey string
TSAServerName string
TSAServerURL string
IssueCertificate bool
SignContainerIdentity string
Key string
Cert string
CertChain string
Upload bool
Output string // deprecated: TODO remove when the output flag is fully deprecated
OutputSignature string // TODO: this should be the root output file arg.
OutputPayload string
OutputCertificate string
PayloadPath string
Recursive bool
Attachment string
SkipConfirmation bool
TlogUpload bool
TSAClientCACert string
TSAClientCert string
TSAClientKey string
TSAServerName string
TSAServerURL string
IssueCertificate bool
SignContainerIdentity string
RecordCreationTimestamp bool

Rekor RekorOptions
Fulcio FulcioOptions
Expand Down Expand Up @@ -130,4 +131,6 @@ func (o *SignOptions) AddFlags(cmd *cobra.Command) {

cmd.Flags().StringVar(&o.SignContainerIdentity, "sign-container-identity", "",
"manually set the .critical.docker-reference field for the signed identity, which is useful when image proxies are being used where the pull reference should match the signature")

cmd.Flags().BoolVar(&o.RecordCreationTimestamp, "record-creation-timestamp", false, "set the createdAt timestamp in the signature artifact to the time it was created; by default, cosign sets this to the zero value")
}
5 changes: 4 additions & 1 deletion cmd/cosign/cli/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ race conditions or (worse) malicious tampering.
cosign sign --key cosign.key --tlog-upload=false <IMAGE DIGEST>
# sign a container image by manually setting the container image identity
cosign sign --sign-container-identity <NEW IMAGE DIGEST> <IMAGE DIGEST>`,
cosign sign --sign-container-identity <NEW IMAGE DIGEST> <IMAGE DIGEST>
# sign a container image and honor the creation timestamp of the signature
cosign sign --key cosign.key --record-creation-timestamp <IMAGE DIGEST>`,

Args: cobra.MinimumNArgs(1),
PersistentPreRun: options.BindViper,
Expand Down
2 changes: 1 addition & 1 deletion cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func signDigest(ctx context.Context, digest name.Digest, payload []byte, ko opti
}

// Attach the signature to the entity.
newSE, err := mutate.AttachSignatureToEntity(se, ociSig, mutate.WithDupeDetector(dd))
newSE, err := mutate.AttachSignatureToEntity(se, ociSig, mutate.WithDupeDetector(dd), mutate.WithRecordCreationTimestamp(signOpts.RecordCreationTimestamp))
if err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions doc/cosign_sign.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/oci/mutate/mutate.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,5 +377,5 @@ func (so *signOpts) dedupeAndReplace(sig oci.Signature, basefn func() (oci.Signa
}
return ReplaceSignatures(replace)
}
return AppendSignatures(base, sig)
return AppendSignatures(base, so.rct, sig)
}
11 changes: 9 additions & 2 deletions pkg/oci/mutate/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ type ReplaceOp interface {
type SignOption func(*signOpts)

type signOpts struct {
dd DupeDetector
ro ReplaceOp
dd DupeDetector
ro ReplaceOp
rct bool
}

func makeSignOpts(opts ...SignOption) *signOpts {
Expand All @@ -59,6 +60,12 @@ func WithReplaceOp(ro ReplaceOp) SignOption {
}
}

func WithRecordCreationTimestamp(rct bool) SignOption {
return func(so *signOpts) {
so.rct = rct
}
}

type signatureOpts struct {
annotations map[string]string
bundle *bundle.RekorBundle
Expand Down
16 changes: 15 additions & 1 deletion pkg/oci/mutate/signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ import (
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/sigstore/cosign/v2/internal/pkg/now"
"github.com/sigstore/cosign/v2/pkg/oci"
)

// AppendSignatures produces a new oci.Signatures with the provided signatures
// appended to the provided base signatures.
func AppendSignatures(base oci.Signatures, sigs ...oci.Signature) (oci.Signatures, error) {
func AppendSignatures(base oci.Signatures, recordCreationTimestamp bool, sigs ...oci.Signature) (oci.Signatures, error) {
adds := make([]mutate.Addendum, 0, len(sigs))
for _, sig := range sigs {
ann, err := sig.Annotations()
Expand All @@ -42,6 +43,19 @@ func AppendSignatures(base oci.Signatures, sigs ...oci.Signature) (oci.Signature
return nil, err
}

if recordCreationTimestamp {
t, err := now.Now()
if err != nil {
return nil, err
}

// Set the Created date to time of execution
img, err = mutate.CreatedAt(img, v1.Time{Time: t})
if err != nil {
return nil, err
}
}

return &sigAppender{
Image: img,
base: base,
Expand Down
16 changes: 11 additions & 5 deletions pkg/oci/mutate/signatures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ func TestAppendSignatures(t *testing.T) {
t.Fatalf("NewSignature() = %v", err)
}

oneSig, err := AppendSignatures(base, s1)
oneSig, err := AppendSignatures(base, false, s1)
if err != nil {
t.Fatalf("AppendSignatures() = %v", err)
}

twoSig, err := AppendSignatures(oneSig, s2)
twoSig, err := AppendSignatures(oneSig, false, s2)
if err != nil {
t.Fatalf("AppendSignatures() = %v", err)
}

threeSig, err := AppendSignatures(oneSig, s2, s3)
threeSig, err := AppendSignatures(oneSig, true, s2, s3)
if err != nil {
t.Fatalf("AppendSignatures() = %v", err)
}
Expand All @@ -73,7 +73,13 @@ func TestAppendSignatures(t *testing.T) {

if testCfg, err := threeSig.ConfigFile(); err != nil {
t.Fatalf("ConfigFile() = %v", err)
} else if !testCfg.Created.Time.IsZero() {
t.Errorf("Date of Signature was not Zero")
} else if testCfg.Created.Time.IsZero() {
t.Errorf("Date of Signature was Zero")
}

if testDefaultCfg, err := twoSig.ConfigFile(); err != nil {
t.Fatalf("ConfigFile() = %v", err)
} else if !testDefaultCfg.Created.Time.IsZero() {
t.Errorf("Date of Signature was Zero")
}
}
14 changes: 14 additions & 0 deletions pkg/oci/static/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/sigstore/cosign/v2/internal/pkg/now"
"github.com/sigstore/cosign/v2/pkg/oci"
"github.com/sigstore/cosign/v2/pkg/oci/signed"
)
Expand All @@ -48,6 +49,19 @@ func NewFile(payload []byte, opts ...Option) (oci.File, error) {
// Add annotations from options
img = mutate.Annotations(img, o.Annotations).(v1.Image)

if o.RecordCreationTimestamp {
t, err := now.Now()
if err != nil {
return nil, err
}

// Set the Created date to time of execution
img, err = mutate.CreatedAt(img, v1.Time{Time: t})
if err != nil {
return nil, err
}
}

return &file{
SignedImage: signed.Image(img),
layer: layer,
Expand Down
13 changes: 13 additions & 0 deletions pkg/oci/static/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ func TestNewFile(t *testing.T) {
t.Fatalf("NewFile() = %v", err)
}

timestampedFile, err := NewFile([]byte(payload), WithLayerMediaType("foo"), WithAnnotations(map[string]string{"foo": "bar"}), WithRecordCreationTimestamp(true))

if err != nil {
t.Fatalf("NewFile() = %v", err)
}

layers, err := file.Layers()
if err != nil {
t.Fatalf("Layers() = %v", err)
Expand Down Expand Up @@ -129,6 +135,13 @@ func TestNewFile(t *testing.T) {
if !fileCfg.Created.Time.IsZero() {
t.Errorf("Date of Signature was not Zero")
}
tsCfg, err := timestampedFile.ConfigFile()
if err != nil {
t.Fatalf("ConfigFile() = %v", err)
}
if tsCfg.Created.Time.IsZero() {
t.Errorf("Date of Signature was Zero")
}
})

t.Run("check annotations", func(t *testing.T) {
Expand Down
22 changes: 15 additions & 7 deletions pkg/oci/static/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ import (
type Option func(*options)

type options struct {
LayerMediaType types.MediaType
ConfigMediaType types.MediaType
Bundle *bundle.RekorBundle
RFC3161Timestamp *bundle.RFC3161Timestamp
Cert []byte
Chain []byte
Annotations map[string]string
LayerMediaType types.MediaType
ConfigMediaType types.MediaType
Bundle *bundle.RekorBundle
RFC3161Timestamp *bundle.RFC3161Timestamp
Cert []byte
Chain []byte
Annotations map[string]string
RecordCreationTimestamp bool
}

func makeOptions(opts ...Option) (*options, error) {
Expand Down Expand Up @@ -112,3 +113,10 @@ func WithCertChain(cert, chain []byte) Option {
o.Chain = chain
}
}

// WithRecordCreationTimestamp sets the feature flag to honor the creation timestamp to time of running
func WithRecordCreationTimestamp(rct bool) Option {
return func(o *options) {
o.RecordCreationTimestamp = rct
}
}

0 comments on commit 3f4094a

Please sign in to comment.