Skip to content

Commit 6b99094

Browse files
authored
Merge pull request moby#3956 from cpuguy83/source_policy_resolve_image
Fix ResolveImageConfig to evaluate source policy
2 parents 86b22f7 + 330cf7a commit 6b99094

File tree

21 files changed

+541
-254
lines changed

21 files changed

+541
-254
lines changed

client/client_test.go

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2787,7 +2787,7 @@ func testSourceDateEpochClamp(t *testing.T, sb integration.Sandbox) {
27872787

27882788
var bboxConfig []byte
27892789
_, err = c.Build(sb.Context(), SolveOpt{}, "", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
2790-
_, bboxConfig, err = c.ResolveImageConfig(ctx, "docker.io/library/busybox:latest", llb.ResolveImageConfigOpt{})
2790+
_, _, bboxConfig, err = c.ResolveImageConfig(ctx, "docker.io/library/busybox:latest", llb.ResolveImageConfigOpt{})
27912791
if err != nil {
27922792
return nil, err
27932793
}
@@ -9470,32 +9470,88 @@ func testSourcePolicy(t *testing.T, sb integration.Sandbox) {
94709470
}
94719471

94729472
t.Run("Frontend policies", func(t *testing.T) {
9473-
denied := "https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md"
9474-
frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
9475-
st := llb.Image("busybox:1.34.1-uclibc").File(
9476-
llb.Copy(llb.HTTP(denied),
9477-
"README.md", "README.md"))
9478-
def, err := st.Marshal(sb.Context())
9479-
if err != nil {
9480-
return nil, err
9473+
t.Run("deny http", func(t *testing.T) {
9474+
denied := "https://raw.githubusercontent.com/moby/buildkit/v0.10.1/README.md"
9475+
frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
9476+
st := llb.Image("busybox:1.34.1-uclibc").File(
9477+
llb.Copy(llb.HTTP(denied),
9478+
"README.md", "README.md"))
9479+
def, err := st.Marshal(sb.Context())
9480+
if err != nil {
9481+
return nil, err
9482+
}
9483+
return c.Solve(ctx, gateway.SolveRequest{
9484+
Definition: def.ToPB(),
9485+
SourcePolicies: []*sourcepolicypb.Policy{{
9486+
Rules: []*sourcepolicypb.Rule{
9487+
{
9488+
Action: sourcepolicypb.PolicyAction_DENY,
9489+
Selector: &sourcepolicypb.Selector{
9490+
Identifier: denied,
9491+
},
9492+
},
9493+
},
9494+
}},
9495+
})
94819496
}
9482-
return c.Solve(ctx, gateway.SolveRequest{
9483-
Definition: def.ToPB(),
9484-
SourcePolicies: []*sourcepolicypb.Policy{{
9485-
Rules: []*sourcepolicypb.Rule{
9486-
{
9487-
Action: sourcepolicypb.PolicyAction_DENY,
9488-
Selector: &sourcepolicypb.Selector{
9489-
Identifier: denied,
9497+
9498+
_, err = c.Build(sb.Context(), SolveOpt{}, "", frontend, nil)
9499+
require.ErrorContains(t, err, sourcepolicy.ErrSourceDenied.Error())
9500+
})
9501+
t.Run("resolve image config", func(t *testing.T) {
9502+
frontend := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
9503+
const (
9504+
origRef = "docker.io/library/busybox:1.34.1-uclibc"
9505+
updatedRef = "docker.io/library/busybox:latest"
9506+
)
9507+
pol := []*sourcepolicypb.Policy{
9508+
{
9509+
Rules: []*sourcepolicypb.Rule{
9510+
{
9511+
Action: sourcepolicypb.PolicyAction_DENY,
9512+
Selector: &sourcepolicypb.Selector{
9513+
Identifier: "*",
9514+
},
9515+
},
9516+
{
9517+
Action: sourcepolicypb.PolicyAction_ALLOW,
9518+
Selector: &sourcepolicypb.Selector{
9519+
Identifier: "docker-image://" + updatedRef + "*",
9520+
},
9521+
},
9522+
{
9523+
Action: sourcepolicypb.PolicyAction_CONVERT,
9524+
Selector: &sourcepolicypb.Selector{
9525+
Identifier: "docker-image://" + origRef,
9526+
},
9527+
Updates: &sourcepolicypb.Update{
9528+
Identifier: "docker-image://" + updatedRef,
9529+
},
94909530
},
94919531
},
94929532
},
9493-
}},
9494-
})
9495-
}
9533+
}
94969534

9497-
_, err = c.Build(sb.Context(), SolveOpt{}, "", frontend, nil)
9498-
require.ErrorContains(t, err, sourcepolicy.ErrSourceDenied.Error())
9535+
ref, dgst, _, err := c.ResolveImageConfig(ctx, origRef, llb.ResolveImageConfigOpt{
9536+
SourcePolicies: pol,
9537+
})
9538+
if err != nil {
9539+
return nil, err
9540+
}
9541+
require.Equal(t, updatedRef, ref)
9542+
st := llb.Image(ref + "@" + dgst.String())
9543+
def, err := st.Marshal(sb.Context())
9544+
if err != nil {
9545+
return nil, err
9546+
}
9547+
return c.Solve(ctx, gateway.SolveRequest{
9548+
Definition: def.ToPB(),
9549+
SourcePolicies: pol,
9550+
})
9551+
}
9552+
_, err = c.Build(sb.Context(), SolveOpt{}, "", frontend, nil)
9553+
require.NoError(t, err)
9554+
})
94999555
})
95009556
}
95019557

client/llb/imagemetaresolver/resolver.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ type imageMetaResolver struct {
7070
}
7171

7272
type resolveResult struct {
73+
ref string
7374
config []byte
7475
dgst digest.Digest
7576
}
7677

77-
func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (digest.Digest, []byte, error) {
78+
func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (string, digest.Digest, []byte, error) {
7879
imr.locker.Lock(ref)
7980
defer imr.locker.Unlock(ref)
8081

@@ -86,16 +87,16 @@ func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string
8687
k := imr.key(ref, platform)
8788

8889
if res, ok := imr.cache[k]; ok {
89-
return res.dgst, res.config, nil
90+
return res.ref, res.dgst, res.config, nil
9091
}
9192

92-
dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, nil, platform)
93+
ref, dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, nil, platform, opt.SourcePolicies)
9394
if err != nil {
94-
return "", nil, err
95+
return "", "", nil, err
9596
}
9697

97-
imr.cache[k] = resolveResult{dgst: dgst, config: config}
98-
return dgst, config, nil
98+
imr.cache[k] = resolveResult{dgst: dgst, config: config, ref: ref}
99+
return ref, dgst, config, nil
99100
}
100101

101102
func (imr *imageMetaResolver) key(ref string, platform *ocispecs.Platform) string {

client/llb/resolver.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package llb
33
import (
44
"context"
55

6+
spb "github.com/moby/buildkit/sourcepolicy/pb"
67
digest "github.com/opencontainers/go-digest"
78
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
89
)
@@ -31,7 +32,7 @@ func WithLayerLimit(l int) ImageOption {
3132

3233
// ImageMetaResolver can resolve image config metadata from a reference
3334
type ImageMetaResolver interface {
34-
ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (digest.Digest, []byte, error)
35+
ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (string, digest.Digest, []byte, error)
3536
}
3637

3738
type ResolverType int
@@ -49,6 +50,8 @@ type ResolveImageConfigOpt struct {
4950
LogName string
5051

5152
Store ResolveImageConfigOptStore
53+
54+
SourcePolicies []*spb.Policy
5255
}
5356

5457
type ResolveImageConfigOptStore struct {

client/llb/resolver_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ type testResolver struct {
7474
platform string
7575
}
7676

77-
func (r *testResolver) ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (digest.Digest, []byte, error) {
77+
func (r *testResolver) ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (string, digest.Digest, []byte, error) {
7878
var img struct {
7979
Config struct {
8080
Env []string `json:"Env,omitempty"`
@@ -92,7 +92,7 @@ func (r *testResolver) ResolveImageConfig(ctx context.Context, ref string, opt R
9292

9393
dt, err := json.Marshal(img)
9494
if err != nil {
95-
return "", nil, errors.WithStack(err)
95+
return "", "", nil, errors.WithStack(err)
9696
}
97-
return r.digest, dt, nil
97+
return ref, r.digest, dt, nil
9898
}

client/llb/source.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func Image(ref string, opts ...ImageOption) State {
135135
if p == nil {
136136
p = c.Platform
137137
}
138-
_, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{
138+
_, _, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{
139139
Platform: p,
140140
ResolveMode: info.resolveMode.String(),
141141
ResolverType: ResolverTypeRegistry,
@@ -151,14 +151,18 @@ func Image(ref string, opts ...ImageOption) State {
151151
if p == nil {
152152
p = c.Platform
153153
}
154-
dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
154+
ref, dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
155155
Platform: p,
156156
ResolveMode: info.resolveMode.String(),
157157
ResolverType: ResolverTypeRegistry,
158158
})
159159
if err != nil {
160160
return State{}, err
161161
}
162+
r, err := reference.ParseNormalizedNamed(ref)
163+
if err != nil {
164+
return State{}, err
165+
}
162166
if dgst != "" {
163167
r, err = reference.WithDigest(r, dgst)
164168
if err != nil {

frontend/attestations/sbom/sbom.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func CreateSBOMScanner(ctx context.Context, resolver llb.ImageMetaResolver, scan
3838
return nil, nil
3939
}
4040

41-
_, dt, err := resolver.ResolveImageConfig(ctx, scanner, resolveOpt)
41+
scanner, _, dt, err := resolver.ResolveImageConfig(ctx, scanner, resolveOpt)
4242
if err != nil {
4343
return nil, err
4444
}

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,23 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
401401
prefix += platforms.Format(*platform) + " "
402402
}
403403
prefix += "internal]"
404-
dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName, llb.ResolveImageConfigOpt{
405-
Platform: platform,
406-
ResolveMode: opt.ImageResolveMode.String(),
407-
LogName: fmt.Sprintf("%s load metadata for %s", prefix, d.stage.BaseName),
408-
ResolverType: llb.ResolverTypeRegistry,
404+
mutRef, dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName, llb.ResolveImageConfigOpt{
405+
Platform: platform,
406+
ResolveMode: opt.ImageResolveMode.String(),
407+
LogName: fmt.Sprintf("%s load metadata for %s", prefix, d.stage.BaseName),
408+
ResolverType: llb.ResolverTypeRegistry,
409+
SourcePolicies: nil,
409410
})
410411
if err != nil {
411412
return suggest.WrapError(errors.Wrap(err, origName), origName, append(allStageNames, commonImageNames()...), true)
412413
}
414+
415+
if ref.String() != mutRef {
416+
ref, err = reference.ParseNormalizedNamed(mutRef)
417+
if err != nil {
418+
return errors.Wrapf(err, "failed to parse ref %q", mutRef)
419+
}
420+
}
413421
var img image.Image
414422
if err := json.Unmarshal(dt, &img); err != nil {
415423
return errors.Wrap(err, "failed to parse image config")

frontend/dockerui/namedcontext.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,36 @@ import (
1313
"github.com/moby/buildkit/exporter/containerimage/image"
1414
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
1515
"github.com/moby/buildkit/frontend/gateway/client"
16+
"github.com/moby/buildkit/util/imageutil"
1617
"github.com/pkg/errors"
1718
)
1819

1920
const (
2021
contextPrefix = "context:"
2122
inputMetadataPrefix = "input-metadata:"
23+
maxContextRecursion = 10
2224
)
2325

2426
func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatform string, opt ContextOpt) (*llb.State, *image.Image, error) {
27+
return bc.namedContextRecursive(ctx, name, nameWithPlatform, opt, 0)
28+
}
29+
30+
func (bc *Client) namedContextRecursive(ctx context.Context, name string, nameWithPlatform string, opt ContextOpt, count int) (*llb.State, *image.Image, error) {
2531
opts := bc.bopts.Opts
2632
v, ok := opts[contextPrefix+nameWithPlatform]
2733
if !ok {
2834
return nil, nil, nil
2935
}
3036

37+
if count > maxContextRecursion {
38+
return nil, nil, errors.New("context recursion limit exceeded; this may indicate a cycle in the provided source policies: " + v)
39+
}
40+
3141
vv := strings.SplitN(v, ":", 2)
3242
if len(vv) != 2 {
3343
return nil, nil, errors.Errorf("invalid context specifier %s for %s", v, nameWithPlatform)
3444
}
45+
3546
// allow git@ without protocol for SSH URLs for backwards compatibility
3647
if strings.HasPrefix(vv[0], "git@") {
3748
vv[0] = "git"
@@ -58,15 +69,20 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor
5869

5970
named = reference.TagNameOnly(named)
6071

61-
dgst, data, err := bc.client.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{
72+
ref, dgst, data, err := bc.client.ResolveImageConfig(ctx, named.String(), llb.ResolveImageConfigOpt{
6273
Platform: opt.Platform,
6374
ResolveMode: opt.ResolveMode,
6475
LogName: fmt.Sprintf("[context %s] load metadata for %s", nameWithPlatform, ref),
6576
ResolverType: llb.ResolverTypeRegistry,
6677
})
6778
if err != nil {
79+
e := &imageutil.ResolveToNonImageError{}
80+
if errors.As(err, &e) {
81+
return bc.namedContextRecursive(ctx, e.Updated, name, opt, count+1)
82+
}
6883
return nil, nil, err
6984
}
85+
7086
var img image.Image
7187
if err := json.Unmarshal(data, &img); err != nil {
7288
return nil, nil, err
@@ -121,7 +137,8 @@ func (bc *Client) namedContext(ctx context.Context, name string, nameWithPlatfor
121137
return nil, nil, errors.Wrapf(err, "could not wrap %q with digest", name)
122138
}
123139

124-
dgst, data, err := bc.client.ResolveImageConfig(ctx, dummyRef.String(), llb.ResolveImageConfigOpt{
140+
// TODO: How should source policy be handled here with a dummy ref?
141+
_, dgst, data, err := bc.client.ResolveImageConfig(ctx, dummyRef.String(), llb.ResolveImageConfigOpt{
125142
Platform: opt.Platform,
126143
ResolveMode: opt.ResolveMode,
127144
LogName: fmt.Sprintf("[context %s] load metadata for %s", nameWithPlatform, dummyRef.String()),

frontend/frontend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type Frontend interface {
2222

2323
type FrontendLLBBridge interface {
2424
Solve(ctx context.Context, req SolveRequest, sid string) (*Result, error)
25-
ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (digest.Digest, []byte, error)
25+
ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (string, digest.Digest, []byte, error)
2626
Warn(ctx context.Context, dgst digest.Digest, msg string, opts WarnOpts) error
2727
}
2828

frontend/gateway/client/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func NewResult() *Result {
2727

2828
type Client interface {
2929
Solve(ctx context.Context, req SolveRequest) (*Result, error)
30-
ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (digest.Digest, []byte, error)
30+
ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (string, digest.Digest, []byte, error)
3131
BuildOpts() BuildOpts
3232
Inputs(ctx context.Context) (map[string]llb.State, error)
3333
NewContainer(ctx context.Context, req NewContainerRequest) (Container, error)

0 commit comments

Comments
 (0)