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
2 changes: 2 additions & 0 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type Options struct {
Inputs Inputs

Ref string
Frontend string
FrontendAttrs map[string]string
Allow []string
Attests map[string]*string
BuildArgs map[string]string
Expand Down
62 changes: 57 additions & 5 deletions build/opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,16 +303,23 @@ func toSolveOpt(ctx context.Context, np *noderesolver.ResolvedNode, multiDriver
cacheFrom = append(cacheFrom, e)
}

frontend := opt.Frontend
if frontend == "" {
frontend = "dockerfile.v0"
}
so := client.SolveOpt{
Ref: opt.Ref,
Frontend: "dockerfile.v0",
FrontendAttrs: map[string]string{},
Frontend: frontend,
FrontendAttrs: maps.Clone(opt.FrontendAttrs),
LocalMounts: map[string]fsutil.FS{},
CacheExports: cacheTo,
CacheImports: cacheFrom,
AllowedEntitlements: opt.Allow,
SourcePolicy: opt.SourcePolicy,
}
if so.FrontendAttrs == nil {
so.FrontendAttrs = map[string]string{}
}

if opt.CgroupParent != "" {
so.FrontendAttrs["cgroup-parent"] = opt.CgroupParent
Expand Down Expand Up @@ -667,11 +674,45 @@ func proxyArgKeyExists(buildArgs map[string]string, key string) bool {
return false
}

// splitPolicyConfigs separates programmatic policy callbacks from file-based
// policy configs. A config carries either a callback or policy files.
func splitPolicyConfigs(configs []buildflags.PolicyConfig) ([]policysession.PolicyCallback, []buildflags.PolicyConfig, error) {
var callbacks []policysession.PolicyCallback
var fileConfigs []buildflags.PolicyConfig
for _, p := range configs {
if p.Callback == nil {
fileConfigs = append(fileConfigs, p)
continue
}
if len(p.Files) > 0 {
return nil, nil, errors.New("policy config cannot set both a callback and policy files")
}
callbacks = append(callbacks, p.Callback)
}
return callbacks, fileConfigs, nil
}

func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, opt *Options, cfg *confutil.Config, bopts gateway.BuildOpts, so *client.SolveOpt, pw progress.Writer) (_ []func(error), err error) {
callbackOnly, fileConfigs, err := splitPolicyConfigs(opt.Policy)
if err != nil {
return nil, err
}

// Any callback-only entry requires the session policy capability, the
// same way a Strict declarative policy does.
if len(callbackOnly) > 0 {
if bopts.LLBCaps.Supports(pb.CapSourcePolicySession) != nil {
return nil, errors.New("session source policy is not supported by the current BuildKit daemon, please upgrade to version v0.27+")
}
}
if opt.Inputs.policy == nil {
if len(opt.Policy) > 0 {
if len(fileConfigs) > 0 {
return nil, errors.New("policy file specified but no policy FS in build context")
}
if len(callbackOnly) > 0 {
so.SourcePolicyProvider = policysession.NewPolicyProvider(policy.MultiPolicyCallback(callbackOnly...))
return nil, nil
}
so.SourcePolicyProvider = nil
return nil, nil
}
Expand All @@ -687,7 +728,7 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
env.Target = opt.Target
env.Labels = opt.Labels

popts, err := withPolicyConfig(*opt.Inputs.policy, opt.Policy)
popts, err := withPolicyConfig(*opt.Inputs.policy, fileConfigs)
if err != nil {
return nil, err
}
Expand All @@ -697,7 +738,7 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
// (docker/dockerfile, docker/dockerfile-upstream) that may be implicitly
// loaded during a build, and passes through any other source so user
// policies retain full control.
if policy.DefaultPolicyEnabled() && !policyExplicitlyDisabled(opt.Policy) {
if policy.DefaultPolicyEnabled() && !policyExplicitlyDisabled(fileConfigs) {
builtin := policyOpt{
Files: []policyFileSpec{{
Filename: policy.DefaultPolicyFilename,
Expand All @@ -709,6 +750,10 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
}

if len(popts) == 0 {
if len(callbackOnly) > 0 {
so.SourcePolicyProvider = policysession.NewPolicyProvider(policy.MultiPolicyCallback(callbackOnly...))
return nil, nil
}
so.SourcePolicyProvider = nil
return nil, nil
}
Expand Down Expand Up @@ -738,6 +783,10 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
return nil, err
}
if len(loadedOpts) == 0 {
if len(callbackOnly) > 0 {
so.SourcePolicyProvider = policysession.NewPolicyProvider(policy.MultiPolicyCallback(callbackOnly...))
return defers, nil
}
so.SourcePolicyProvider = nil
return defers, nil
}
Expand Down Expand Up @@ -802,6 +851,9 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
policyLogger.Log("policy enabled network proxy")
}
}
// Callback-only policy entries compose as the last (most-strict)
// entries, allowing file-based policies to still run first.
cbs = append(cbs, callbackOnly...)
so.SourcePolicyProvider = policysession.NewPolicyProvider(policy.MultiPolicyCallback(cbs...))
return defers, nil
}
Expand Down
18 changes: 18 additions & 0 deletions build/policy_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package build

import (
"context"
"testing"

"github.com/docker/buildx/policy"
"github.com/docker/buildx/util/buildflags"
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/sourcepolicy/policysession"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -125,3 +128,18 @@ func TestWithPolicyConfigMultipleFilesAndOverrides(t *testing.T) {
require.False(t, out[2].Files[0].Optional)
require.True(t, out[2].Strict)
}

func TestSplitPolicyConfigs(t *testing.T) {
cb := func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
return nil, nil, nil
}
file := buildflags.PolicyConfig{Files: []policy.File{{Filename: "policy.rego"}}}

callbacks, files, err := splitPolicyConfigs([]buildflags.PolicyConfig{file, {Callback: cb}})
require.NoError(t, err)
require.Len(t, callbacks, 1)
require.Equal(t, []buildflags.PolicyConfig{file}, files)

_, _, err = splitPolicyConfigs([]buildflags.PolicyConfig{{Files: file.Files, Callback: cb}})
require.ErrorContains(t, err, "cannot set both a callback and policy files")
}
Loading
Loading