policy: add multiple policy support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
+48
-33
@@ -327,7 +327,11 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
|
||||
}
|
||||
defers = append(defers, releaseLoad)
|
||||
|
||||
if opt.Inputs.policy != nil {
|
||||
if opt.Inputs.policy == nil {
|
||||
if len(opt.Policy) > 0 {
|
||||
return nil, nil, errors.New("policy file specified but no policy FS in build context")
|
||||
}
|
||||
} else {
|
||||
env := policy.Env{}
|
||||
for k, v := range opt.BuildArgs {
|
||||
if env.Args == nil {
|
||||
@@ -338,16 +342,25 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
|
||||
env.Filename = path.Base(opt.Inputs.DockerfilePath)
|
||||
env.Target = opt.Target
|
||||
env.Labels = opt.Labels
|
||||
p := policy.NewPolicy(policy.Opt{
|
||||
Files: opt.Inputs.policy.Files,
|
||||
Env: env,
|
||||
Log: func(msg string) {
|
||||
log.Printf("[policy] %s", msg)
|
||||
},
|
||||
FS: opt.Inputs.policy.FS,
|
||||
Config: cfg,
|
||||
})
|
||||
so.SourcePolicyProvider = policysession.NewPolicyProvider(p.CheckPolicy)
|
||||
|
||||
popts, err := withPolicyConfig(*opt.Inputs.policy, opt.Policy)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var cbs []policysession.PolicyCallback
|
||||
for _, popt := range popts {
|
||||
p := policy.NewPolicy(policy.Opt{
|
||||
Files: popt.Files,
|
||||
Env: env,
|
||||
Log: func(msg string) {
|
||||
log.Printf("[policy] %s", msg)
|
||||
},
|
||||
FS: opt.Inputs.policy.FS,
|
||||
Config: cfg,
|
||||
})
|
||||
cbs = append(cbs, p.CheckPolicy)
|
||||
}
|
||||
so.SourcePolicyProvider = policysession.NewPolicyProvider(policy.MultiPolicyCallback(cbs...))
|
||||
}
|
||||
|
||||
// add node identifier to shared key if one was specified
|
||||
@@ -561,6 +574,25 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
|
||||
dockerfileName = "Dockerfile"
|
||||
}
|
||||
|
||||
p := &policyOpt{
|
||||
FS: func() (fs.StatFS, func() error, error) {
|
||||
if contextDir == "" {
|
||||
return nil, nil, errors.Errorf("unimplemented, cannot use policy file without a local build context")
|
||||
}
|
||||
root, err := os.OpenRoot(contextDir)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to open root for policy file %s.rego", dockerfileName)
|
||||
}
|
||||
baseFS := root.FS()
|
||||
statFS, ok := baseFS.(fs.StatFS)
|
||||
if !ok {
|
||||
root.Close()
|
||||
return nil, nil, errors.Errorf("invalid root FS type %T", baseFS)
|
||||
}
|
||||
return statFS, root.Close, nil
|
||||
},
|
||||
}
|
||||
|
||||
if dockerfileDir != "" {
|
||||
if err := setLocalMount("dockerfile", dockerfileDir, target); err != nil {
|
||||
return nil, err
|
||||
@@ -573,33 +605,16 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read policy file %s.rego", dockerfileName)
|
||||
}
|
||||
inp.policy = &policyOpt{
|
||||
Files: []policy.File{
|
||||
{
|
||||
Filename: dockerfileName + ".rego",
|
||||
Data: dt,
|
||||
},
|
||||
},
|
||||
FS: func() (fs.StatFS, func() error, error) {
|
||||
if contextDir == "" {
|
||||
return nil, nil, errors.Errorf("unimplemented, cannot use policy file without a local build context")
|
||||
}
|
||||
root, err := os.OpenRoot(contextDir)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to open root for policy file %s.rego", dockerfileName)
|
||||
}
|
||||
baseFS := root.FS()
|
||||
statFS, ok := baseFS.(fs.StatFS)
|
||||
if !ok {
|
||||
root.Close()
|
||||
return nil, nil, errors.Errorf("invalid root FS type %T", baseFS)
|
||||
}
|
||||
return statFS, root.Close, nil
|
||||
p.Files = []policy.File{
|
||||
{
|
||||
Filename: dockerfileName + ".rego",
|
||||
Data: dt,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inp.policy = p
|
||||
|
||||
target.FrontendAttrs["filename"] = dockerfileName
|
||||
|
||||
|
||||
+5
-1
@@ -260,7 +260,11 @@ func parsePolicyConfigs(in []string) ([]build.PolicyConfig, error) {
|
||||
if value == "" {
|
||||
return nil, errors.Errorf("invalid value %s", field)
|
||||
}
|
||||
cfg.Files = append(cfg.Files, policy.File{Filename: value})
|
||||
dt, err := os.ReadFile(value)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read policy file %s", value)
|
||||
}
|
||||
cfg.Files = append(cfg.Files, policy.File{Filename: value, Data: dt})
|
||||
case "reset":
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
moby_buildkit_v1_sourcepolicy "github.com/moby/buildkit/sourcepolicy/pb"
|
||||
"github.com/moby/buildkit/sourcepolicy/policysession"
|
||||
)
|
||||
|
||||
// MultiPolicyCallback returns a policy callback that requires all policies to allow.
|
||||
func MultiPolicyCallback(callbacks ...policysession.PolicyCallback) policysession.PolicyCallback {
|
||||
return func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
if len(callbacks) == 0 {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
}
|
||||
|
||||
for _, cb := range callbacks {
|
||||
if cb == nil {
|
||||
continue
|
||||
}
|
||||
decision, metaReq, err := cb(ctx, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if metaReq != nil {
|
||||
return nil, metaReq, nil
|
||||
}
|
||||
if decision == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch decision.Action {
|
||||
case moby_buildkit_v1_sourcepolicy.PolicyAction_DENY:
|
||||
return decision, nil, nil
|
||||
case moby_buildkit_v1_sourcepolicy.PolicyAction_CONVERT:
|
||||
return decision, nil, nil
|
||||
case moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW:
|
||||
// noop
|
||||
default:
|
||||
// treat unknown actions as allow
|
||||
}
|
||||
}
|
||||
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
moby_buildkit_v1_sourcepolicy "github.com/moby/buildkit/sourcepolicy/pb"
|
||||
"github.com/moby/buildkit/sourcepolicy/policysession"
|
||||
solverpb "github.com/moby/buildkit/solver/pb"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMultiPolicyCallbackNoCallbacks(t *testing.T) {
|
||||
cb := MultiPolicyCallback()
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, meta)
|
||||
require.NotNil(t, decision)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW, decision.Action)
|
||||
}
|
||||
|
||||
func TestMultiPolicyCallbackAllowOnly(t *testing.T) {
|
||||
cb := MultiPolicyCallback(func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
})
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, meta)
|
||||
require.NotNil(t, decision)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW, decision.Action)
|
||||
}
|
||||
|
||||
func TestMultiPolicyCallbackDenyWins(t *testing.T) {
|
||||
denyMsgA := &policysession.DenyMessage{Message: "nope-a"}
|
||||
denyMsgB := &policysession.DenyMessage{Message: "nope-b"}
|
||||
|
||||
cb := MultiPolicyCallback(
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
},
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{
|
||||
Action: moby_buildkit_v1_sourcepolicy.PolicyAction_DENY,
|
||||
DenyMessages: []*policysession.DenyMessage{denyMsgA, denyMsgB},
|
||||
}, nil, nil
|
||||
},
|
||||
)
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, meta)
|
||||
require.NotNil(t, decision)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_DENY, decision.Action)
|
||||
require.Equal(t, []*policysession.DenyMessage{denyMsgA, denyMsgB}, decision.DenyMessages)
|
||||
}
|
||||
|
||||
func TestMultiPolicyCallbackDenyShortCircuits(t *testing.T) {
|
||||
called := false
|
||||
cb := MultiPolicyCallback(
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_DENY}, nil, nil
|
||||
},
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
called = true
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
},
|
||||
)
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, meta)
|
||||
require.NotNil(t, decision)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_DENY, decision.Action)
|
||||
require.False(t, called)
|
||||
}
|
||||
|
||||
func TestMultiPolicyCallbackUpdateReturned(t *testing.T) {
|
||||
update := &solverpb.SourceOp{}
|
||||
cb := MultiPolicyCallback(
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
},
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{
|
||||
Action: moby_buildkit_v1_sourcepolicy.PolicyAction_CONVERT,
|
||||
Update: update,
|
||||
}, nil, nil
|
||||
},
|
||||
)
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, meta)
|
||||
require.NotNil(t, decision)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_CONVERT, decision.Action)
|
||||
require.Equal(t, update, decision.Update)
|
||||
}
|
||||
|
||||
func TestMultiPolicyCallbackMetaRequest(t *testing.T) {
|
||||
metaReq := &gwpb.ResolveSourceMetaRequest{}
|
||||
cb := MultiPolicyCallback(
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return nil, metaReq, nil
|
||||
},
|
||||
func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_DENY}, nil, nil
|
||||
},
|
||||
)
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, decision)
|
||||
require.Equal(t, metaReq, meta)
|
||||
}
|
||||
|
||||
func TestMultiPolicyCallbackNilCallbackIgnored(t *testing.T) {
|
||||
cb := MultiPolicyCallback(nil, func(context.Context, *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
return &policysession.DecisionResponse{Action: moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW}, nil, nil
|
||||
})
|
||||
|
||||
decision, meta, err := cb(context.Background(), &policysession.CheckPolicyRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, meta)
|
||||
require.NotNil(t, decision)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW, decision.Action)
|
||||
}
|
||||
Reference in New Issue
Block a user