Merge pull request #3895 from tonistiigi/exec-proxy-support
policy: enable proxy network from source policy caps
This commit is contained in:
@@ -140,6 +140,7 @@ type policyFileSpec struct {
|
||||
type policyEvalOpt struct {
|
||||
Strict bool
|
||||
LogLevel *logrus.Level
|
||||
SkipCaps bool
|
||||
}
|
||||
|
||||
type policyOpt struct {
|
||||
|
||||
@@ -667,6 +667,7 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
|
||||
Data: policy.DefaultPolicyData(),
|
||||
}},
|
||||
}
|
||||
builtin.SkipCaps = true
|
||||
popts = append([]policyOpt{builtin}, popts...)
|
||||
}
|
||||
|
||||
@@ -742,6 +743,11 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
|
||||
DefaultPlatform: defaultPlatform(bopts),
|
||||
SourceResolver: sourceResolver,
|
||||
})
|
||||
if !popt.SkipCaps {
|
||||
if err := applyPolicyCaps(ctx, p, bopts, so); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
policies = append(policies, p)
|
||||
cbs = append(cbs, p.CheckPolicy)
|
||||
if popt.Strict {
|
||||
@@ -750,10 +756,30 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
|
||||
}
|
||||
}
|
||||
}
|
||||
if so.ProxyNetwork {
|
||||
if policyLogger != nil {
|
||||
policyLogger.Log("policy enabled network proxy")
|
||||
}
|
||||
}
|
||||
so.SourcePolicyProvider = policysession.NewPolicyProvider(policy.MultiPolicyCallback(cbs...))
|
||||
return defers, nil
|
||||
}
|
||||
|
||||
func applyPolicyCaps(ctx context.Context, p *policy.Policy, bopts gateway.BuildOpts, so *client.SolveOpt) error {
|
||||
caps, err := p.CheckCaps(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to evaluate policy caps")
|
||||
}
|
||||
if !caps[policy.CapExecProxy] {
|
||||
return nil
|
||||
}
|
||||
if err := bopts.LLBCaps.Supports(pb.CapExecMetaNetworkProxy); err != nil {
|
||||
return errors.New("network proxy requested by policy is not supported by the current BuildKit daemon, please upgrade to version v0.31+")
|
||||
}
|
||||
so.ProxyNetwork = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func policyEnvFilename(inp Inputs) string {
|
||||
base := filepath.Base(filepath.Clean(inp.DockerfilePath))
|
||||
if base != "." && base != string(filepath.Separator) {
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/policy"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/ocilayout"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/client/ociindex"
|
||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
@@ -163,6 +167,67 @@ func TestProxyArgKeyExists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPolicyCapsEnablesProxyNetwork(t *testing.T) {
|
||||
p := policyWithDecision(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": false,
|
||||
"caps": {"exec.proxy": true},
|
||||
}
|
||||
`)
|
||||
var so client.SolveOpt
|
||||
|
||||
err := applyPolicyCaps(context.Background(), p, buildOptsWithCaps(pb.CapExecMetaNetworkProxy), &so)
|
||||
require.NoError(t, err)
|
||||
require.True(t, so.ProxyNetwork)
|
||||
}
|
||||
|
||||
func TestApplyPolicyCapsOrsProxyNetwork(t *testing.T) {
|
||||
falsePolicy := policyWithDecision(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": true,
|
||||
"caps": {"exec.proxy": false},
|
||||
}
|
||||
`)
|
||||
truePolicy := policyWithDecision(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": true,
|
||||
"caps": {"exec.proxy": true},
|
||||
}
|
||||
`)
|
||||
var so client.SolveOpt
|
||||
|
||||
err := applyPolicyCaps(context.Background(), falsePolicy, buildOptsWithCaps(pb.CapExecMetaNetworkProxy), &so)
|
||||
require.NoError(t, err)
|
||||
require.False(t, so.ProxyNetwork)
|
||||
|
||||
err = applyPolicyCaps(context.Background(), truePolicy, buildOptsWithCaps(pb.CapExecMetaNetworkProxy), &so)
|
||||
require.NoError(t, err)
|
||||
require.True(t, so.ProxyNetwork)
|
||||
}
|
||||
|
||||
func TestApplyPolicyCapsRequiresBuildKitCap(t *testing.T) {
|
||||
p := policyWithDecision(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": true,
|
||||
"caps": {"exec.proxy": true},
|
||||
}
|
||||
`)
|
||||
var so client.SolveOpt
|
||||
|
||||
err := applyPolicyCaps(context.Background(), p, buildOptsWithCaps(), &so)
|
||||
require.ErrorContains(t, err, "network proxy requested by policy is not supported by the current BuildKit daemon")
|
||||
require.ErrorContains(t, err, "please upgrade to version v0.31+")
|
||||
require.False(t, so.ProxyNetwork)
|
||||
}
|
||||
|
||||
func TestLoadInputsOCILayoutNamedContext(t *testing.T) {
|
||||
layoutPath := t.TempDir()
|
||||
|
||||
@@ -299,3 +364,25 @@ func (w *captureProgressWriter) ValidateLogSource(digest.Digest, any) bool { ret
|
||||
func (w *captureProgressWriter) ClearLogSource(any) {}
|
||||
|
||||
var _ progress.Writer = (*captureProgressWriter)(nil)
|
||||
|
||||
func policyWithDecision(decision string) *policy.Policy {
|
||||
return policy.NewPolicy(policy.Opt{
|
||||
Files: []policy.File{{
|
||||
Filename: "policy.rego",
|
||||
Data: []byte(decision),
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
func buildOptsWithCaps(caps ...apicaps.CapID) gateway.BuildOpts {
|
||||
out := make([]*apicaps.PBCap, 0, len(caps))
|
||||
for _, c := range caps {
|
||||
out = append(out, &apicaps.PBCap{
|
||||
ID: string(c),
|
||||
Enabled: true,
|
||||
})
|
||||
}
|
||||
return gateway.BuildOpts{
|
||||
LLBCaps: pb.Caps.CapSet(out),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,9 +683,23 @@ func decodeDecision(decision any) *Decision {
|
||||
if len(denyMsgs) == 0 {
|
||||
denyMsgs = nil
|
||||
}
|
||||
caps := Caps{}
|
||||
if v, ok := obj["caps"]; ok {
|
||||
if m, ok := v.(map[string]any); ok {
|
||||
for k, entry := range m {
|
||||
if b, ok := entry.(bool); ok {
|
||||
caps[k] = b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(caps) == 0 {
|
||||
caps = nil
|
||||
}
|
||||
return &Decision{
|
||||
Allow: allow,
|
||||
DenyMessages: denyMsgs,
|
||||
Caps: caps,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-5
@@ -21,14 +21,24 @@ type Input struct {
|
||||
type Decision struct {
|
||||
Allow *bool `json:"allow,omitempty"`
|
||||
DenyMessages []string `json:"deny_msg,omitempty"`
|
||||
Caps Caps `json:"caps,omitempty"`
|
||||
}
|
||||
|
||||
type Caps map[string]bool
|
||||
|
||||
const CapExecProxy = "exec.proxy"
|
||||
|
||||
var KnownCaps = map[string]struct{}{
|
||||
CapExecProxy: {},
|
||||
}
|
||||
|
||||
type Env struct {
|
||||
Args map[string]*string `json:"args,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Depth int `json:"depth"`
|
||||
Args map[string]*string `json:"args,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
CapsRequest bool `json:"capsRequest,omitempty"`
|
||||
Depth int `json:"depth"`
|
||||
}
|
||||
|
||||
type HTTP struct {
|
||||
|
||||
+145
-52
@@ -130,27 +130,7 @@ func (p *Policy) IsPolicyError(err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
if req.Source == nil || req.Source.Source == nil {
|
||||
return nil, nil, errors.Errorf("no source info in request")
|
||||
}
|
||||
|
||||
var platform *ocispecs.Platform
|
||||
if req.Platform != nil {
|
||||
pl, err := platformFromReq(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
platform = pl
|
||||
} else {
|
||||
platform = p.opt.DefaultPlatform
|
||||
}
|
||||
|
||||
inp, err := SourceToInput(ctx, p.opt.VerifierProvider, req.Source, platform, p.opt.Log)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to build policy input")
|
||||
}
|
||||
|
||||
func (p *Policy) regoBaseOpts() ([]func(*rego.Rego), func(), error) {
|
||||
caps := &ast.Capabilities{
|
||||
Builtins: builtins(),
|
||||
Features: slices.Clone(ast.Features),
|
||||
@@ -171,11 +151,11 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
|
||||
var root fs.StatFS
|
||||
var closeFS func() error
|
||||
defer func() {
|
||||
closeRoot := func() {
|
||||
if closeFS != nil {
|
||||
closeFS()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
comp = comp.WithModuleLoader(func(resolved map[string]*ast.Module) (parsed map[string]*ast.Module, err error) {
|
||||
out := make(map[string]*ast.Module)
|
||||
@@ -240,6 +220,36 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
baseOpts = append(baseOpts, rego.Module(file.Filename, string(file.Data)))
|
||||
}
|
||||
|
||||
return baseOpts, closeRoot, nil
|
||||
}
|
||||
|
||||
func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
if req.Source == nil || req.Source.Source == nil {
|
||||
return nil, nil, errors.Errorf("no source info in request")
|
||||
}
|
||||
|
||||
var platform *ocispecs.Platform
|
||||
if req.Platform != nil {
|
||||
pl, err := platformFromReq(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
platform = pl
|
||||
} else {
|
||||
platform = p.opt.DefaultPlatform
|
||||
}
|
||||
|
||||
inp, err := SourceToInput(ctx, p.opt.VerifierProvider, req.Source, platform, p.opt.Log)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to build policy input")
|
||||
}
|
||||
|
||||
baseOpts, closeRoot, err := p.regoBaseOpts()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer closeRoot()
|
||||
|
||||
p.log(logrus.InfoLevel, "checking policy for source %s", sourceName(req))
|
||||
|
||||
for range maxResolveIterations {
|
||||
@@ -304,42 +314,23 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
continue
|
||||
}
|
||||
|
||||
if len(rs) == 0 {
|
||||
return nil, nil, errors.Errorf("policy returned zero result")
|
||||
decision, err := policyDecisionFromResult(rs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rsz := rs[0]
|
||||
if len(rsz.Expressions) == 0 {
|
||||
return nil, nil, errors.Errorf("policy returned zero expressions")
|
||||
}
|
||||
v := rsz.Expressions[0].Value
|
||||
vt, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
return nil, nil, errors.Errorf("unexpected policy return type: %T %s", vt, rsz.Expressions[0].Text)
|
||||
}
|
||||
|
||||
resp := &policysession.DecisionResponse{
|
||||
Action: moby_buildkit_v1_sourcepolicy.PolicyAction_DENY,
|
||||
}
|
||||
p.log(logrus.DebugLevel, "policy response: %+v", vt)
|
||||
p.log(logrus.DebugLevel, "policy response: %+v", decision)
|
||||
|
||||
if v, ok := vt["allow"]; ok {
|
||||
if vv, ok := v.(bool); !ok {
|
||||
return nil, nil, errors.Errorf("invalid allowed property type %T, expecting bool", v)
|
||||
} else if vv {
|
||||
resp.Action = moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW
|
||||
}
|
||||
if decision.Allow != nil && *decision.Allow {
|
||||
resp.Action = moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW
|
||||
}
|
||||
|
||||
if v, ok := vt["deny_msg"]; ok {
|
||||
if vv, ok := v.([]any); ok {
|
||||
for _, m := range vv {
|
||||
if m, ok := m.(string); ok {
|
||||
resp.DenyMessages = append(resp.DenyMessages, &policysession.DenyMessage{
|
||||
Message: m,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, m := range decision.DenyMessages {
|
||||
resp.DenyMessages = append(resp.DenyMessages, &policysession.DenyMessage{
|
||||
Message: m,
|
||||
})
|
||||
}
|
||||
|
||||
if resp.Action == moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW {
|
||||
@@ -373,6 +364,108 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
return nil, nil, errors.Errorf("maximum attempts reached for resolving policy metadata")
|
||||
}
|
||||
|
||||
func (p *Policy) CheckCaps(ctx context.Context) (Caps, error) {
|
||||
baseOpts, closeRoot, err := p.regoBaseOpts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer closeRoot()
|
||||
|
||||
env := p.opt.Env
|
||||
env.CapsRequest = true
|
||||
runInput := Input{}
|
||||
applyEnvWithDepth(&runInput, env, 0)
|
||||
|
||||
runOpts := append([]func(*rego.Rego){}, baseOpts...)
|
||||
runOpts = append(runOpts, rego.Input(runInput))
|
||||
|
||||
st := &state{Input: runInput}
|
||||
for _, f := range p.funcs {
|
||||
runOpts = append(runOpts, f.impl(st))
|
||||
}
|
||||
|
||||
dt, err := json.MarshalIndent(runInput, "", " ")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal policy input")
|
||||
}
|
||||
p.log(logrus.DebugLevel, "policy input: %s", dt)
|
||||
|
||||
rs, err := rego.New(runOpts...).Eval(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if refs := runtimeUnknownInputRefs(st); len(refs) > 0 || st.checksumNeededForSignature != nil {
|
||||
return nil, errors.Errorf("policy caps request cannot resolve source metadata: %+v", summarizeUnknownsForLog(refs))
|
||||
}
|
||||
|
||||
decision, err := policyDecisionFromResult(rs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(decision.Caps) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return decision.Caps, nil
|
||||
}
|
||||
|
||||
func policyDecisionFromResult(rs rego.ResultSet) (*Decision, error) {
|
||||
if len(rs) == 0 {
|
||||
return nil, errors.Errorf("policy returned zero result")
|
||||
}
|
||||
rsz := rs[0]
|
||||
if len(rsz.Expressions) == 0 {
|
||||
return nil, errors.Errorf("policy returned zero expressions")
|
||||
}
|
||||
v := rsz.Expressions[0].Value
|
||||
vt, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unexpected policy return type: %T %s", v, rsz.Expressions[0].Text)
|
||||
}
|
||||
return parsePolicyDecision(vt)
|
||||
}
|
||||
|
||||
func parsePolicyDecision(vt map[string]any) (*Decision, error) {
|
||||
decision := &Decision{}
|
||||
|
||||
if v, ok := vt["allow"]; ok {
|
||||
vv, ok := v.(bool)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid allowed property type %T, expecting bool", v)
|
||||
}
|
||||
decision.Allow = &vv
|
||||
}
|
||||
|
||||
if v, ok := vt["deny_msg"]; ok {
|
||||
if vv, ok := v.([]any); ok {
|
||||
for _, m := range vv {
|
||||
if m, ok := m.(string); ok {
|
||||
decision.DenyMessages = append(decision.DenyMessages, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := vt["caps"]; ok {
|
||||
vv, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid caps property type %T, expecting object", v)
|
||||
}
|
||||
decision.Caps = make(Caps, len(vv))
|
||||
for k, cv := range vv {
|
||||
if _, ok := KnownCaps[k]; !ok {
|
||||
return nil, errors.Errorf("unknown policy cap %q", k)
|
||||
}
|
||||
b, ok := cv.(bool)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid caps.%s property type %T, expecting bool", k, cv)
|
||||
}
|
||||
decision.Caps[k] = b
|
||||
}
|
||||
}
|
||||
|
||||
return decision, nil
|
||||
}
|
||||
|
||||
func (p *Policy) resolveUnknowns(ctx context.Context, input *Input, req *policysession.CheckPolicyRequest, defaultPlatform *ocispecs.Platform, unk []string, st *state) (bool, *gwpb.ResolveSourceMetaRequest, error) {
|
||||
var resolver SourceMetadataResolver
|
||||
if p.opt.SourceResolver != nil {
|
||||
|
||||
@@ -883,6 +883,86 @@ func TestSourceToInputSingleSource(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckCaps(t *testing.T) {
|
||||
p := NewPolicy(Opt{
|
||||
Files: []File{{
|
||||
Filename: "policy.rego",
|
||||
Data: []byte(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": false,
|
||||
"deny_msg": ["ignored for caps"],
|
||||
"caps": {
|
||||
"exec.proxy": input.env.capsRequest,
|
||||
},
|
||||
} if {
|
||||
input.env.filename == "Dockerfile"
|
||||
input.env.target == "release"
|
||||
input.env.args.MODE == "prod"
|
||||
}
|
||||
`),
|
||||
}},
|
||||
Env: Env{
|
||||
Args: map[string]*string{"MODE": stringPtr("prod")},
|
||||
Filename: "Dockerfile",
|
||||
Target: "release",
|
||||
},
|
||||
})
|
||||
|
||||
caps, err := p.CheckCaps(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, Caps{
|
||||
CapExecProxy: true,
|
||||
}, caps)
|
||||
}
|
||||
|
||||
func TestCheckCapsMalformedCaps(t *testing.T) {
|
||||
p := NewPolicy(Opt{
|
||||
Files: []File{{
|
||||
Filename: "policy.rego",
|
||||
Data: []byte(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": true,
|
||||
"caps": {
|
||||
"exec.proxy": "yes",
|
||||
},
|
||||
}
|
||||
`),
|
||||
}},
|
||||
})
|
||||
|
||||
_, err := p.CheckCaps(context.Background())
|
||||
require.ErrorContains(t, err, "invalid caps.exec.proxy property type string, expecting bool")
|
||||
}
|
||||
|
||||
func TestCheckCapsUnknownCaps(t *testing.T) {
|
||||
p := NewPolicy(Opt{
|
||||
Files: []File{{
|
||||
Filename: "policy.rego",
|
||||
Data: []byte(`
|
||||
package docker
|
||||
|
||||
decision := {
|
||||
"allow": true,
|
||||
"caps": {
|
||||
"exec.unknown": true,
|
||||
},
|
||||
}
|
||||
`),
|
||||
}},
|
||||
})
|
||||
|
||||
_, err := p.CheckCaps(context.Background())
|
||||
require.ErrorContains(t, err, `unknown policy cap "exec.unknown"`)
|
||||
}
|
||||
|
||||
func stringPtr(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
func mustMarshalImageConfig(t *testing.T, img ocispecs.Image) []byte {
|
||||
t.Helper()
|
||||
dt, err := json.Marshal(img)
|
||||
|
||||
@@ -40,6 +40,82 @@ var policyBuildTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testBuildPolicyRemotePolicyFiles,
|
||||
testBuildPolicyRemoteHTTPPolicyFiles,
|
||||
testBuildPolicyConfigFlags,
|
||||
testBuildPolicyCapsProxy,
|
||||
testBuildPolicyCapsProxyUnsupported,
|
||||
}
|
||||
|
||||
func policyCapsProxyFile(serverURL string) []byte {
|
||||
return fmt.Appendf(nil, `
|
||||
package docker
|
||||
default allow = true
|
||||
default deny_msg := []
|
||||
default caps := {}
|
||||
caps := {"exec.proxy": true} if input.env.capsRequest
|
||||
allow := false if input.http.url == "%s/file"
|
||||
deny_msg := ["exec proxy http source denied by policy"] if input.http.url == "%s/file"
|
||||
decision := {"allow": allow, "deny_msg": deny_msg, "caps": caps}
|
||||
`, serverURL, serverURL)
|
||||
}
|
||||
|
||||
func testBuildPolicyCapsProxy(t *testing.T, sb integration.Sandbox) {
|
||||
if buildkitTag() != "master" {
|
||||
skipNoCompatBuildKit(t, sb, ">= 0.31.0-0", "network proxy requires BuildKit v0.31.0+")
|
||||
}
|
||||
resp := &httpserver.Response{Content: []byte("policy-caps-proxy")}
|
||||
server := httpserver.NewTestServer(map[string]*httpserver.Response{
|
||||
"/file": resp,
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dockerfile := []byte(`
|
||||
FROM busybox:latest
|
||||
RUN wget -O- ` + server.URL + `/file
|
||||
`)
|
||||
dir := tmpdir(
|
||||
t,
|
||||
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||
fstest.CreateFile("Dockerfile.rego", policyCapsProxyFile(server.URL), 0600),
|
||||
)
|
||||
|
||||
cmd := buildxCmd(sb, withDir(dir), withArgs(
|
||||
"build",
|
||||
"--progress=plain",
|
||||
"--output=type=cacheonly",
|
||||
dir,
|
||||
))
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.Error(t, err, string(out))
|
||||
require.Contains(t, string(out), "policy enabled network proxy")
|
||||
require.Contains(t, string(out), "exec proxy http source denied by policy")
|
||||
require.Contains(t, string(out), "policy decision for source "+server.URL+"/file")
|
||||
require.Contains(t, string(out), "DENY")
|
||||
}
|
||||
|
||||
func testBuildPolicyCapsProxyUnsupported(t *testing.T, sb integration.Sandbox) {
|
||||
skipNoCompatBuildKit(t, sb, ">= 0.26.0-0", "policy input requires BuildKit v0.26.0+")
|
||||
if buildkitTag() == "master" || matchesBuildKitVersion(t, sb, ">= 0.31.0-0") {
|
||||
t.Skip("BuildKit daemon supports network proxy")
|
||||
}
|
||||
dockerfile := []byte(`
|
||||
FROM scratch
|
||||
COPY foo /foo
|
||||
`)
|
||||
dir := tmpdir(
|
||||
t,
|
||||
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||
fstest.CreateFile("Dockerfile.rego", policyCapsProxyFile(""), 0600),
|
||||
fstest.CreateFile("foo", []byte("foo"), 0600),
|
||||
)
|
||||
|
||||
cmd := buildxCmd(sb, withDir(dir), withArgs(
|
||||
"build",
|
||||
"--progress=plain",
|
||||
"--output=type=cacheonly",
|
||||
dir,
|
||||
))
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.Error(t, err, string(out))
|
||||
require.Contains(t, string(out), "network proxy requested by policy is not supported by the current BuildKit daemon")
|
||||
}
|
||||
|
||||
func testBuildPolicyAllow(t *testing.T, sb integration.Sandbox) {
|
||||
|
||||
Reference in New Issue
Block a user