mirror of
https://github.com/ko-build/ko.git
synced 2026-06-18 20:14:08 +02:00
Connect SBOMs with SPDX support. (#511)
* Connect SBOMs with SPDX support. This combines Jason's SPDX stuff and my SBOM stuff to support SPDX-based SBOMs by default instead of our `go version -m` invention. * Make ko deps use SPDX by default
This commit is contained in:
@@ -84,7 +84,7 @@ jobs:
|
||||
run: |
|
||||
set -o pipefail
|
||||
|
||||
IMAGE=$(ko publish ./test)
|
||||
IMAGE=$(ko build ./test)
|
||||
SBOM=$(cosign download sbom ${IMAGE})
|
||||
KO_DEPS=$(ko deps ${IMAGE})
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ ko apply -f FILENAME [flags]
|
||||
--push Push images to KO_DOCKER_REPO (default true)
|
||||
-R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (DEPRECATED)
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "go.version-m")
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, go.version-m). (default "spdx")
|
||||
-l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)
|
||||
-s, --server string The address and port of the Kubernetes API server (DEPRECATED)
|
||||
--tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ ko build IMPORTPATH... [flags]
|
||||
--platform string Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*
|
||||
-P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO.
|
||||
--push Push images to KO_DOCKER_REPO (default true)
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "go.version-m")
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, go.version-m). (default "spdx")
|
||||
--tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.
|
||||
-t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest])
|
||||
--tarball string File to save images tarballs
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ ko create -f FILENAME [flags]
|
||||
--push Push images to KO_DOCKER_REPO (default true)
|
||||
-R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (DEPRECATED)
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "go.version-m")
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, go.version-m). (default "spdx")
|
||||
-l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)
|
||||
-s, --server string The address and port of the Kubernetes API server (DEPRECATED)
|
||||
--tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ ko deps IMAGE [flags]
|
||||
|
||||
```
|
||||
-h, --help help for deps
|
||||
--sbom string Format for SBOM output (default "go.version-m")
|
||||
--sbom string Format for SBOM output (supports: spdx, go.version-m). (default "spdx")
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ ko resolve -f FILENAME [flags]
|
||||
-P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO.
|
||||
--push Push images to KO_DOCKER_REPO (default true)
|
||||
-R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "go.version-m")
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, go.version-m). (default "spdx")
|
||||
-l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)
|
||||
--tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.
|
||||
-t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest])
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ ko run IMPORTPATH [flags]
|
||||
--platform string Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*
|
||||
-P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO.
|
||||
--push Push images to KO_DOCKER_REPO (default true)
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "go.version-m")
|
||||
--sbom string The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, go.version-m). (default "spdx")
|
||||
--tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.
|
||||
-t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest])
|
||||
--tarball string File to save images tarballs
|
||||
|
||||
+40
-20
@@ -41,11 +41,13 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
"github.com/google/ko/internal/sbom"
|
||||
specsv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sigstore/cosign/pkg/oci"
|
||||
ocimutate "github.com/sigstore/cosign/pkg/oci/mutate"
|
||||
"github.com/sigstore/cosign/pkg/oci/signed"
|
||||
"github.com/sigstore/cosign/pkg/oci/static"
|
||||
ctypes "github.com/sigstore/cosign/pkg/types"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
@@ -57,7 +59,7 @@ const (
|
||||
type GetBase func(context.Context, string) (name.Reference, Result, error)
|
||||
|
||||
type builder func(context.Context, string, string, v1.Platform, Config) (string, error)
|
||||
type sbomber func(context.Context, string, string) ([]byte, types.MediaType, error)
|
||||
type sbomber func(context.Context, string, string, v1.Image) ([]byte, types.MediaType, error)
|
||||
|
||||
type platformMatcher struct {
|
||||
spec string
|
||||
@@ -71,7 +73,6 @@ type gobuild struct {
|
||||
build builder
|
||||
sbom sbomber
|
||||
disableOptimizations bool
|
||||
disableSBOM bool
|
||||
trimpath bool
|
||||
buildConfigs map[string]Config
|
||||
platformMatcher *platformMatcher
|
||||
@@ -89,7 +90,6 @@ type gobuildOpener struct {
|
||||
build builder
|
||||
sbom sbomber
|
||||
disableOptimizations bool
|
||||
disableSBOM bool
|
||||
trimpath bool
|
||||
buildConfigs map[string]Config
|
||||
platform string
|
||||
@@ -112,7 +112,6 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
|
||||
build: gbo.build,
|
||||
sbom: gbo.sbom,
|
||||
disableOptimizations: gbo.disableOptimizations,
|
||||
disableSBOM: gbo.disableSBOM,
|
||||
trimpath: gbo.trimpath,
|
||||
buildConfigs: gbo.buildConfigs,
|
||||
labels: gbo.labels,
|
||||
@@ -130,8 +129,8 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
|
||||
func NewGo(ctx context.Context, dir string, options ...Option) (Interface, error) {
|
||||
gbo := &gobuildOpener{
|
||||
build: build,
|
||||
sbom: sbom,
|
||||
dir: dir,
|
||||
sbom: spdx("(none)"),
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
@@ -261,7 +260,7 @@ func build(ctx context.Context, ip string, dir string, platform v1.Platform, con
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func sbom(ctx context.Context, file string, appPath string) ([]byte, types.MediaType, error) {
|
||||
func goversionm(ctx context.Context, file string, appPath string, _ v1.Image) ([]byte, types.MediaType, error) {
|
||||
sbom := bytes.NewBuffer(nil)
|
||||
cmd := exec.CommandContext(ctx, "go", "version", "-m", file)
|
||||
cmd.Stdout = sbom
|
||||
@@ -270,14 +269,31 @@ func sbom(ctx context.Context, file string, appPath string) ([]byte, types.Media
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// TODO(imjasonh): Turn the output of `go version -m` on
|
||||
// file into a standard format and attach here.
|
||||
|
||||
// In order to get deterministics SBOMs replace our randomized
|
||||
// file name with the path the app will get inside of the container.
|
||||
return []byte(strings.Replace(sbom.String(), file, appPath, 1)), "application/vnd.go.version-m", nil
|
||||
}
|
||||
|
||||
func spdx(version string) sbomber {
|
||||
return func(ctx context.Context, file string, appPath string, img v1.Image) ([]byte, types.MediaType, error) {
|
||||
b, _, err := goversionm(ctx, file, appPath, img)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
cfg, err := img.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
b, err = sbom.GenerateSPDX(version, cfg.Created.Time, b)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return b, ctypes.SPDXMediaType, nil
|
||||
}
|
||||
}
|
||||
|
||||
// buildEnv creates the environment variables used by the `go build` command.
|
||||
// From `os/exec.Cmd`: If Env contains duplicate environment keys, only the last
|
||||
// value in the slice for each duplicate key is used.
|
||||
@@ -724,19 +740,23 @@ func (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, pl
|
||||
}
|
||||
}
|
||||
|
||||
if g.disableSBOM {
|
||||
return signed.Image(image), nil
|
||||
}
|
||||
si := signed.Image(image)
|
||||
|
||||
sbom, mt, err := g.sbom(ctx, file, appPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if g.sbom != nil {
|
||||
sbom, mt, err := g.sbom(ctx, file, appPath, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := static.NewFile(sbom, static.WithLayerMediaType(mt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
si, err = ocimutate.AttachFileToImage(si, "sbom", f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f, err := static.NewFile(sbom, static.WithLayerMediaType(mt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ocimutate.AttachFileToImage(signed.Image(image), "sbom", f)
|
||||
return si, nil
|
||||
}
|
||||
|
||||
// Append appPath to the PATH environment variable, if it exists. Otherwise,
|
||||
|
||||
@@ -392,7 +392,7 @@ func nilGetBase(_ context.Context, _ string) (name.Reference, Result, error) {
|
||||
const wantSBOM = "This is our fake SBOM"
|
||||
|
||||
// A helper method we use to substitute for the default "build" method.
|
||||
func fauxSBOM(_ context.Context, _ string, _ string) ([]byte, types.MediaType, error) {
|
||||
func fauxSBOM(_ context.Context, _ string, _ string, _ v1.Image) ([]byte, types.MediaType, error) {
|
||||
return []byte(wantSBOM), "application/vnd.garbage", nil
|
||||
}
|
||||
|
||||
|
||||
+19
-1
@@ -57,7 +57,7 @@ func WithDisabledOptimizations() Option {
|
||||
// WithDisabledSBOM is a functional option for disabling SBOM generation.
|
||||
func WithDisabledSBOM() Option {
|
||||
return func(gbo *gobuildOpener) error {
|
||||
gbo.disableSBOM = true
|
||||
gbo.sbom = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,24 @@ func withBuilder(b builder) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithGoVersionSBOM is a functional option to direct ko to use
|
||||
// go version -m for SBOM format.
|
||||
func WithGoVersionSBOM() Option {
|
||||
return func(gbo *gobuildOpener) error {
|
||||
gbo.sbom = goversionm
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSPDX is a functional option to direct ko to use
|
||||
// SPDX for SBOM format.
|
||||
func WithSPDX(version string) Option {
|
||||
return func(gbo *gobuildOpener) error {
|
||||
gbo.sbom = spdx(version)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// withSBOMber is a functional option for overriding the way SBOMs
|
||||
// are generated.
|
||||
func withSBOMber(sbom sbomber) Option {
|
||||
|
||||
@@ -148,6 +148,6 @@ If the image was not built using ko, or if it was built without embedding depend
|
||||
// unreachable
|
||||
},
|
||||
}
|
||||
deps.Flags().StringVar(&sbomType, "sbom", "go.version-m", "Format for SBOM output")
|
||||
deps.Flags().StringVar(&sbomType, "sbom", "spdx", "Format for SBOM output (supports: spdx, go.version-m).")
|
||||
topLevel.AddCommand(deps)
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ func AddBuildOptions(cmd *cobra.Command, bo *BuildOptions) {
|
||||
"The maximum number of concurrent builds (default GOMAXPROCS)")
|
||||
cmd.Flags().BoolVar(&bo.DisableOptimizations, "disable-optimizations", bo.DisableOptimizations,
|
||||
"Disable optimizations when building Go code. Useful when you want to interactively debug the created container.")
|
||||
cmd.Flags().StringVar(&bo.SBOM, "sbom", "go.version-m",
|
||||
"The SBOM media type to use (none will disable SBOM synthesis and upload).")
|
||||
cmd.Flags().StringVar(&bo.SBOM, "sbom", "spdx",
|
||||
"The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, go.version-m).")
|
||||
cmd.Flags().StringVar(&bo.Platform, "platform", "",
|
||||
"Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*")
|
||||
cmd.Flags().StringSliceVar(&bo.Labels, "image-label", []string{},
|
||||
|
||||
@@ -102,6 +102,10 @@ func gobuildOptions(bo *options.BuildOptions) ([]build.Option, error) {
|
||||
switch bo.SBOM {
|
||||
case "none":
|
||||
opts = append(opts, build.WithDisabledSBOM())
|
||||
case "spdx":
|
||||
opts = append(opts, build.WithSPDX(version()))
|
||||
case "go.version-m":
|
||||
opts = append(opts, build.WithGoVersionSBOM())
|
||||
}
|
||||
opts = append(opts, build.WithTrimpath(bo.Trimpath))
|
||||
for _, lf := range bo.Labels {
|
||||
|
||||
Reference in New Issue
Block a user