Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions cmd/buildctl/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,26 +278,13 @@ func buildAction(clicontext *cli.Context) error {
}
}()

solveAttr := map[string]string{}
frontendAttr := map[string]string{}
for k, v := range solveOpt.FrontendAttrs {
if strings.HasPrefix(k, "attest:") || strings.HasPrefix(k, "build-arg:BUILDKIT_ATTEST_") {
frontendAttr[k] = v
} else {
solveAttr[k] = v
}
}

sreq := gateway.SolveRequest{
Frontend: solveOpt.Frontend,
FrontendOpt: solveAttr,
FrontendOpt: solveOpt.FrontendAttrs,
}
if def != nil {
sreq.Definition = def.ToPB()
}
solveOpt.Frontend = ""
solveOpt.FrontendAttrs = frontendAttr

resp, err := c.Build(ctx, solveOpt, "buildctl", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
_, isSubRequest := sreq.FrontendOpt["requestid"]
if isSubRequest {
Expand Down
2 changes: 1 addition & 1 deletion examples/dockerfile2llb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func xmain() error {

caps := pb.Caps.CapSet(pb.Caps.All())

state, img, err := dockerfile2llb.Dockerfile2LLB(appcontext.Context(), df, dockerfile2llb.ConvertOpt{
state, img, _, err := dockerfile2llb.Dockerfile2LLB(appcontext.Context(), df, dockerfile2llb.ConvertOpt{
MetaResolver: imagemetaresolver.Default(),
Target: opt.target,
LLBCaps: &caps,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package attest
package sbom

import (
"context"
Expand Down
27 changes: 10 additions & 17 deletions frontend/dockerfile/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend"
"github.com/moby/buildkit/frontend/attest"
Comment thread
jedevc marked this conversation as resolved.
"github.com/moby/buildkit/frontend/attestations"
"github.com/moby/buildkit/frontend/attestations/sbom"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
"github.com/moby/buildkit/frontend/dockerfile/parser"
Expand Down Expand Up @@ -434,8 +434,9 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
return nil, err
}

target := opts[keyTarget]
convertOpt := dockerfile2llb.ConvertOpt{
Target: opts[keyTarget],
Target: target,
MetaResolver: c,
BuildArgs: filter(opts, buildArgPrefix),
Labels: filter(opts, labelPrefix),
Expand Down Expand Up @@ -489,7 +490,7 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
}
}

var scanner attest.Scanner
var scanner sbom.Scanner
attests, err := attestations.Parse(opts)
if err != nil {
return nil, err
Expand All @@ -506,11 +507,12 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
ref = reference.TagNameOnly(ref)
exportMap = true

scanner, err = attest.CreateSBOMScanner(ctx, c, ref.String())
scanner, err = sbom.CreateSBOMScanner(ctx, c, ref.String())
if err != nil {
return nil, err
}
}
scanTargets := make([]*dockerfile2llb.SBOMTargets, len(targetPlatforms))

eg, ctx2 = errgroup.WithContext(ctx)

Expand All @@ -523,8 +525,7 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
opt.Warn = nil
}
opt.ContextByName = contextByNameFunc(c, c.BuildOpts().SessionID)
st, img, err := dockerfile2llb.Dockerfile2LLB(ctx2, dtDockerfile, opt)

st, img, scanTarget, err := dockerfile2llb.Dockerfile2LLB(ctx2, dtDockerfile, opt)
if err != nil {
return err
}
Expand Down Expand Up @@ -601,6 +602,7 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
Platform: p,
}
}
scanTargets[i] = scanTarget
return nil
})
}(i, tp)
Expand All @@ -611,17 +613,8 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
}

if scanner != nil {
for _, p := range expPlatforms.Platforms {
ref, ok := res.Refs[p.ID]
if !ok {
return nil, errors.Errorf("could not find ref %s", p.ID)
}
st, err := ref.ToState()
if err != nil {
return nil, err
}

att, st, err := scanner(ctx, p.ID, st, nil)
for i, p := range expPlatforms.Platforms {
att, st, err := scanner(ctx, p.ID, scanTargets[i].Core, scanTargets[i].Extras)
if err != nil {
return nil, err
}
Expand Down
72 changes: 68 additions & 4 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,16 @@ const (
emptyImageName = "scratch"
defaultContextLocalName = "context"
historyComment = "buildkit.dockerfile.v0"

sbomScanContext = "BUILDKIT_SBOM_SCAN_CONTEXT"
sbomScanStage = "BUILDKIT_SBOM_SCAN_STAGE"
)

var nonEnvArgs = map[string]struct{}{
sbomScanContext: {},
sbomScanStage: {},
}

type ConvertOpt struct {
Target string
MetaResolver llb.ImageMetaResolver
Expand Down Expand Up @@ -77,12 +85,36 @@ type ConvertOpt struct {
ContextByName func(ctx context.Context, name, resolveMode string, p *ocispecs.Platform) (*llb.State, *Image, error)
}

func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) {
type SBOMTargets struct {
Core llb.State
Extras map[string]llb.State
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the name in this map retrievable from the final attestation (descriptor)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So by default this is the last part of the path component - I think we should probably document this, so that scanners can use this as the name in the SBOM.

}

func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, *SBOMTargets, error) {
ds, err := toDispatchState(ctx, dt, opt)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

sbom := SBOMTargets{
Core: ds.state,
Extras: map[string]llb.State{},
}
return &ds.state, &ds.image, nil
if ds.scanContext {
sbom.Extras["context"] = ds.opt.buildContext
}
for dsi := ds; dsi != nil; dsi = dsi.base {
if ds != dsi && dsi.scanStage {
sbom.Extras["stage:"+dsi.stageName] = dsi.state
}
for dsi2 := range dsi.deps {
if dsi2.scanStage {
sbom.Extras["stage:"+dsi2.stageName] = dsi2.state
}
}
}

return &ds.state, &ds.image, &sbom, nil
}

func Dockefile2Outline(ctx context.Context, dt []byte, opt ConvertOpt) (*outline.Outline, error) {
Expand Down Expand Up @@ -533,10 +565,23 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
return nil, parser.WithLocation(err, cmd.Location())
}
}
d.opt = opt

for p := range d.ctxPaths {
ctxPaths[p] = struct{}{}
}

locals := []instructions.KeyValuePairOptional{}
locals = append(locals, d.opt.metaArgs...)
locals = append(locals, d.buildArgs...)
for _, a := range locals {
switch a.Key {
case sbomScanStage:
d.scanStage = isEnabledForStage(d.stageName, a.ValueString())
case sbomScanContext:
d.scanContext = isEnabledForStage(d.stageName, a.ValueString())
}
}
}

if len(opt.Labels) != 0 && target.image.Config.Labels == nil {
Expand Down Expand Up @@ -749,6 +794,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
}

type dispatchState struct {
opt dispatchOpt
state llb.State
image Image
platform *ocispecs.Platform
Expand All @@ -769,6 +815,8 @@ type dispatchState struct {
buildInfo binfotypes.BuildInfo
outline outlineCapture
epoch *time.Time
scanStage bool
scanContext bool
}

type dispatchStates struct {
Expand Down Expand Up @@ -1363,7 +1411,9 @@ func dispatchArg(d *dispatchState, c *instructions.ArgCommand, metaArgs []instru
ai := argInfo{definition: arg, location: c.Location()}

if buildArg.Value != nil {
d.state = d.state.AddEnv(buildArg.Key, *buildArg.Value)
if _, ok := nonEnvArgs[buildArg.Key]; !ok {
d.state = d.state.AddEnv(buildArg.Key, *buildArg.Value)
}
ai.value = *buildArg.Value
}

Expand Down Expand Up @@ -1735,3 +1785,17 @@ func clampTimes(img Image, tm *time.Time) Image {
func isHTTPSource(src string) bool {
return strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://")
}

func isEnabledForStage(stage string, value string) bool {
if enabled, err := strconv.ParseBool(value); err == nil {
return enabled
}

vv := strings.Split(value, ",")
for _, v := range vv {
if v == stage {
return true
}
}
return false
}
20 changes: 10 additions & 10 deletions frontend/dockerfile/dockerfile2llb/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ ENV FOO bar
COPY f1 f2 /sub/
RUN ls -l
`
_, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.NoError(t, err)

df = `FROM scratch AS foo
Expand All @@ -40,7 +40,7 @@ FROM foo
COPY --from=foo f1 /
COPY --from=0 f2 /
`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.NoError(t, err)

df = `FROM scratch AS foo
Expand All @@ -49,34 +49,34 @@ FROM foo
COPY --from=foo f1 /
COPY --from=0 f2 /
`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{
Target: "Foo",
})
assert.NoError(t, err)

_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{
Target: "nosuch",
})
assert.Error(t, err)

df = `FROM scratch
ADD https://github.com/moby/buildkit/blob/master/README.md /
`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.NoError(t, err)

df = `FROM scratch
COPY https://github.com/moby/buildkit/blob/master/README.md /
`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.EqualError(t, err, "source can't be a URL for COPY")

df = `FROM "" AS foo`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.Error(t, err)

df = `FROM ${BLANK} AS foo`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.Error(t, err)
}

Expand Down Expand Up @@ -174,7 +174,7 @@ func TestDockerfileCircularDependencies(t *testing.T) {
df := `FROM busybox AS stage0
COPY --from=stage0 f1 /sub/
`
_, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.EqualError(t, err, "circular dependency detected on stage: stage0")

// multiple stages with circular dependency
Expand All @@ -185,6 +185,6 @@ COPY --from=stage0 f2 /sub/
FROM busybox AS stage2
COPY --from=stage1 f2 /sub/
`
_, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
_, _, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{})
assert.EqualError(t, err, "circular dependency detected on stage: stage0")
}
Loading