Skip to content

Commit

Permalink
Start to sketch out integration with cosign.
Browse files Browse the repository at this point in the history
When `COSIGN_EXPERIMENTAL=true`, this will verify base images, and sign produced images using the KEYLESS flow.

Fixes: ko-build#357
Fixes: ko-build#356
  • Loading branch information
mattmoor committed Sep 13, 2021
1 parent 501111b commit 4932ddc
Show file tree
Hide file tree
Showing 5,859 changed files with 1,617,593 additions and 3,455 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
8 changes: 3 additions & 5 deletions go.mod
Expand Up @@ -4,25 +4,23 @@ go 1.16

require (
github.com/containerd/stargz-snapshotter/estargz v0.8.0
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/docker/docker v20.10.8+incompatible
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960
github.com/evanphx/json-patch/v5 v5.5.0 // indirect
github.com/fsnotify/fsnotify v1.5.1
github.com/go-training/helloworld v0.0.0-20200225145412-ba5f4379d78b
github.com/google/go-cmp v0.5.6
github.com/google/go-containerregistry v0.6.0
github.com/mattmoor/dep-notify v0.0.0-20190205035814-a45dec370a17
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7
github.com/sigstore/cosign v1.1.1-0.20210912183820-3f83940d3f3d
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.8.1
github.com/theupdateframework/go-tuf v0.0.0-20210804171843-477a5d73800a // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.1.5
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/apimachinery v0.22.0
k8s.io/apimachinery v0.22.1
sigs.k8s.io/kind v0.11.1
)
1,302 changes: 1,282 additions & 20 deletions go.sum

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions pkg/commands/config.go
Expand Up @@ -25,6 +25,7 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"

Expand All @@ -37,6 +38,7 @@ import (
"github.com/google/ko/pkg/build"
"github.com/google/ko/pkg/commands/options"
"github.com/google/ko/pkg/publish"
"github.com/sigstore/cosign/cmd/cosign/cli"
"github.com/spf13/viper"
"golang.org/x/tools/go/packages"
)
Expand All @@ -55,6 +57,7 @@ var (
// getBaseImage returns a function that determines the base image for a given import path.
// If the `bo.BaseImage` parameter is non-empty, it overrides base image configuration from `.ko.yaml`.
func getBaseImage(platform string, bo *options.BuildOptions) build.GetBase {
v := &verifier{entries: make(map[string]*entry, 10)}
return func(ctx context.Context, s string) (name.Reference, build.Result, error) {
s = strings.TrimPrefix(s, build.StrictScheme)
// Viper configuration file keys are case insensitive, and are
Expand Down Expand Up @@ -124,6 +127,18 @@ func getBaseImage(platform string, bo *options.BuildOptions) build.GetBase {
if err != nil {
return nil, nil, err
}

// With the resolved digest in hand (in descriptor), we verify the
// signature of the base image.
if cli.EnableExperimental() {
digest := ref.Context().String() + "@" + desc.Digest.String()
if err := v.verify(ctx, digest); err != nil {
log.Printf("WARNING: unabled to verify %s: %v", digest, err)
// TODO(mattmoor): Plumb through a build option to make this fatal.
// return nil, nil, err
}
}

switch desc.MediaType {
case types.OCIImageIndex, types.DockerManifestList:
if multiplatform {
Expand All @@ -139,6 +154,52 @@ func getBaseImage(platform string, bo *options.BuildOptions) build.GetBase {
}
}

// verifier memoizes digest verification results, so that we avoid redundant
// digest verification.
type verifier struct {
m sync.Mutex
entries map[string]*entry
}

// entry is used to hold the sync.Once used to execute the digest verification,
// and the resulting error (possibly nil), that results from verification.
type entry struct {
once sync.Once
result error
}

func (v *verifier) verify(ctx context.Context, digest string) error {
e := v.getEntry(digest)
e.once.Do(func() {
// cosign emits a lot of stuff to STDOUT that we don't want
// there, so temporarily redirect STDOUT to STDERR.
defer redirectOutput()()
e.result = cli.Verify().Exec(ctx, []string{digest})
})
return e.result
}

func (v *verifier) getEntry(digest string) *entry {
v.m.Lock()
defer v.m.Unlock()
// See if there's already an entry for this digest.
if e, ok := v.entries[digest]; ok {
return e
}
// If not, then create one, register and return it.
e := &entry{}
v.entries[digest] = e
return e
}

func redirectOutput() context.CancelFunc {
stdout := os.Stdout
os.Stdout = os.Stderr
return func() {
os.Stdout = stdout
}
}

func getTimeFromEnv(env string) (*v1.Time, error) {
epoch := os.Getenv(env)
if epoch == "" {
Expand Down
55 changes: 55 additions & 0 deletions pkg/commands/resolver.go
Expand Up @@ -37,9 +37,11 @@ import (
"github.com/google/ko/pkg/publish"
"github.com/google/ko/pkg/resolve"
"github.com/mattmoor/dep-notify/pkg/graph"
"github.com/sigstore/cosign/cmd/cosign/cli"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v3"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
)

// ua returns the ko user agent.
Expand Down Expand Up @@ -218,6 +220,14 @@ func makePublisher(po *options.PublishOptions) (publish.Interface, error) {
if err != nil {
return nil, err
}

if cli.EnableExperimental() {
dp = &signPublisher{
digests: sets.NewString(),
inner: dp,
}
}

publishers = append(publishers, dp)
}

Expand Down Expand Up @@ -258,6 +268,51 @@ func (n nopPublisher) Publish(_ context.Context, br build.Result, s string) (nam

func (n nopPublisher) Close() error { return nil }

// signPublisher signs the collection of images
type signPublisher struct {
// m guards digests
m sync.Mutex
digests sets.String

inner publish.Interface
}

var _ publish.Interface = (*signPublisher)(nil)

// Publish implements publish.Interface
func (n *signPublisher) Publish(ctx context.Context, br build.Result, s string) (name.Reference, error) {
ref, err := n.inner.Publish(ctx, br, s)
if err != nil {
return nil, err
}

if _, ok := ref.(*name.Digest); ok {
n.m.Lock()
defer n.m.Unlock()
n.digests.Insert(ref.String())
} else {
log.Printf("Skipping non-digest reference: %v", ref)
}
return ref, nil
}

// Close implements publish.Interface
func (n *signPublisher) Close() error {
if err := func() error {
n.m.Lock()
defer n.m.Unlock()

// cosign emits a lot of stuff to STDOUT that we don't want
// there, so temporarily redirect STDOUT to STDERR.
defer redirectOutput()()
return cli.Sign().Exec(context.Background(), n.digests.List())
}(); err != nil {
return err
}

return n.inner.Close()
}

// resolvedFuture represents a "future" for the bytes of a resolved file.
type resolvedFuture chan []byte

Expand Down
27 changes: 27 additions & 0 deletions vendor/bitbucket.org/creachadair/shell/LICENSE

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

7 changes: 7 additions & 0 deletions vendor/bitbucket.org/creachadair/shell/README.md

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

29 changes: 29 additions & 0 deletions vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml

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

3 changes: 3 additions & 0 deletions vendor/bitbucket.org/creachadair/shell/go.mod

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

0 comments on commit 4932ddc

Please sign in to comment.