From f79f1a98c42c73b69839a42969b59bc022404819 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 2 Oct 2025 17:27:22 -0700 Subject: [PATCH 1/3] policysession: dynamic source policy support Add support for dynamic source policies via client session. Client session can allow or deny specific source or ask additional metadata information via sourcemetaresolver if that is needed to make the decision. Signed-off-by: Tonis Tiigi --- api/services/control/control.pb.go | 13 +- api/services/control/control.proto | 1 + api/services/control/control_vtproto.pb.go | 47 + client/client_test.go | 1 + client/policy_test.go | 148 ++ client/solve.go | 14 +- control/control.go | 2 +- solver/llbsolver/bridge.go | 36 +- solver/llbsolver/policy.go | 161 +++ solver/llbsolver/solver.go | 28 +- solver/llbsolver/sourcepolicy.go | 2 +- solver/llbsolver/vertex.go | 2 +- .../policysession/policysession.pb.go | 367 +++++ .../policysession/policysession.proto | 36 + .../policysession/policysession_grpc.pb.go | 119 ++ .../policysession/policysession_vtproto.pb.go | 1261 +++++++++++++++++ sourcepolicy/policysession/provider.go | 46 + sourcepolicy/policysession/verifier.go | 30 + 18 files changed, 2300 insertions(+), 14 deletions(-) create mode 100644 client/policy_test.go create mode 100644 solver/llbsolver/policy.go create mode 100644 sourcepolicy/policysession/policysession.pb.go create mode 100644 sourcepolicy/policysession/policysession.proto create mode 100644 sourcepolicy/policysession/policysession_grpc.pb.go create mode 100644 sourcepolicy/policysession/policysession_vtproto.pb.go create mode 100644 sourcepolicy/policysession/provider.go create mode 100644 sourcepolicy/policysession/verifier.go diff --git a/api/services/control/control.pb.go b/api/services/control/control.pb.go index 0277173f9cf8..2703e042ae02 100644 --- a/api/services/control/control.pb.go +++ b/api/services/control/control.pb.go @@ -408,6 +408,7 @@ type SolveRequest struct { SourcePolicy *pb1.Policy `protobuf:"bytes,12,opt,name=SourcePolicy,proto3" json:"SourcePolicy,omitempty"` Exporters []*Exporter `protobuf:"bytes,13,rep,name=Exporters,proto3" json:"Exporters,omitempty"` EnableSessionExporter bool `protobuf:"varint,14,opt,name=EnableSessionExporter,proto3" json:"EnableSessionExporter,omitempty"` + SourcePolicySession string `protobuf:"bytes,15,opt,name=SourcePolicySession,proto3" json:"SourcePolicySession,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -540,6 +541,13 @@ func (x *SolveRequest) GetEnableSessionExporter() bool { return false } +func (x *SolveRequest) GetSourcePolicySession() string { + if x != nil { + return x.SourcePolicySession + } + return "" +} + type CacheOptions struct { state protoimpl.MessageState `protogen:"open.v1"` // ExportRefDeprecated is deprecated in favor or the new Exports since BuildKit v0.4.0. @@ -2050,7 +2058,7 @@ const file_github_com_moby_buildkit_api_services_control_control_proto_rawDesc = " \x01(\tR\n" + "RecordType\x12\x16\n" + "\x06Shared\x18\v \x01(\bR\x06Shared\x12\x18\n" + - "\aParents\x18\f \x03(\tR\aParents\"\xf4\a\n" + + "\aParents\x18\f \x03(\tR\aParents\"\xa6\b\n" + "\fSolveRequest\x12\x10\n" + "\x03Ref\x18\x01 \x01(\tR\x03Ref\x12.\n" + "\n" + @@ -2068,7 +2076,8 @@ const file_github_com_moby_buildkit_api_services_control_control_proto_rawDesc = "\bInternal\x18\v \x01(\bR\bInternal\x12I\n" + "\fSourcePolicy\x18\f \x01(\v2%.moby.buildkit.v1.sourcepolicy.PolicyR\fSourcePolicy\x128\n" + "\tExporters\x18\r \x03(\v2\x1a.moby.buildkit.v1.ExporterR\tExporters\x124\n" + - "\x15EnableSessionExporter\x18\x0e \x01(\bR\x15EnableSessionExporter\x1aJ\n" + + "\x15EnableSessionExporter\x18\x0e \x01(\bR\x15EnableSessionExporter\x120\n" + + "\x13SourcePolicySession\x18\x0f \x01(\tR\x13SourcePolicySession\x1aJ\n" + "\x1cExporterAttrsDeprecatedEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a@\n" + diff --git a/api/services/control/control.proto b/api/services/control/control.proto index 3e47752ff2f9..128408e8426a 100644 --- a/api/services/control/control.proto +++ b/api/services/control/control.proto @@ -76,6 +76,7 @@ message SolveRequest { moby.buildkit.v1.sourcepolicy.Policy SourcePolicy = 12; repeated Exporter Exporters = 13; bool EnableSessionExporter = 14; + string SourcePolicySession = 15; } message CacheOptions { diff --git a/api/services/control/control_vtproto.pb.go b/api/services/control/control_vtproto.pb.go index 1d72ca07e501..4faf1b112c5d 100644 --- a/api/services/control/control_vtproto.pb.go +++ b/api/services/control/control_vtproto.pb.go @@ -142,6 +142,7 @@ func (m *SolveRequest) CloneVT() *SolveRequest { r.Internal = m.Internal r.SourcePolicy = m.SourcePolicy.CloneVT() r.EnableSessionExporter = m.EnableSessionExporter + r.SourcePolicySession = m.SourcePolicySession if rhs := m.ExporterAttrsDeprecated; rhs != nil { tmpContainer := make(map[string]string, len(rhs)) for k, v := range rhs { @@ -1039,6 +1040,9 @@ func (this *SolveRequest) EqualVT(that *SolveRequest) bool { if this.EnableSessionExporter != that.EnableSessionExporter { return false } + if this.SourcePolicySession != that.SourcePolicySession { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -2245,6 +2249,13 @@ func (m *SolveRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.SourcePolicySession) > 0 { + i -= len(m.SourcePolicySession) + copy(dAtA[i:], m.SourcePolicySession) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SourcePolicySession))) + i-- + dAtA[i] = 0x7a + } if m.EnableSessionExporter { i-- if m.EnableSessionExporter { @@ -4159,6 +4170,10 @@ func (m *SolveRequest) SizeVT() (n int) { if m.EnableSessionExporter { n += 2 } + l = len(m.SourcePolicySession) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -6279,6 +6294,38 @@ func (m *SolveRequest) UnmarshalVT(dAtA []byte) error { } } m.EnableSessionExporter = bool(v != 0) + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePolicySession", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourcePolicySession = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/client/client_test.go b/client/client_test.go index b73fffb915dd..28a400b46d37 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -251,6 +251,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){ testHTTPResolveMultiBuild, testGitResolveMutatedSource, testImageResolveAttestationChainRequiresNetwork, + testSourcePolicySession, } func TestIntegration(t *testing.T) { diff --git a/client/policy_test.go b/client/policy_test.go new file mode 100644 index 000000000000..6ff5bdf21461 --- /dev/null +++ b/client/policy_test.go @@ -0,0 +1,148 @@ +package client + +import ( + "context" + "encoding/json" + "io" + "runtime" + "testing" + + "github.com/moby/buildkit/client/llb" + pb "github.com/moby/buildkit/frontend/gateway/pb" + sourcepolicpb "github.com/moby/buildkit/sourcepolicy/pb" + "github.com/moby/buildkit/sourcepolicy/policysession" + "github.com/moby/buildkit/util/testutil/integration" + "github.com/moby/buildkit/util/testutil/workers" + digest "github.com/opencontainers/go-digest" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +func testSourcePolicySession(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter) + + ctx := sb.Context() + + c, err := New(ctx, sb.Address()) + require.NoError(t, err) + defer c.Close() + + type tcase struct { + name string + state func() llb.State + callbacks []policysession.PolicyCallback + expectedError string + } + + tcases := []tcase{ + { + name: "basic alpine", + state: func() llb.State { return llb.Image("alpine") }, + callbacks: []policysession.PolicyCallback{ + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, runtime.GOOS, req.Platform.OS) + require.Equal(t, runtime.GOARCH, req.Platform.Architecture) + + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + return &policysession.DecisionResponse{ + Action: sourcepolicpb.PolicyAction_ALLOW, + }, nil, nil + }, + }, + }, + { + name: "alpine with attrs", + state: func() llb.State { return llb.Image("alpine", llb.WithLayerLimit(1)) }, + callbacks: []policysession.PolicyCallback{ + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + require.Equal(t, map[string]string{ + "image.layerlimit": "1", + }, req.Source.Source.Attrs) + return &policysession.DecisionResponse{ + Action: sourcepolicpb.PolicyAction_ALLOW, + }, nil, nil + }, + }, + }, + { + name: "deny alpine", + state: func() llb.State { return llb.Image("alpine") }, + callbacks: []policysession.PolicyCallback{ + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + return nil, nil, errors.New("policy denied") + }, + }, + expectedError: "policy denied", + }, + { + name: "alpine with digest policy", + state: func() llb.State { return llb.Image("alpine") }, + callbacks: []policysession.PolicyCallback{ + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + require.Nil(t, req.Source.Image) + return nil, &pb.ResolveSourceMetaRequest{ + Source: req.Source.Source, + Platform: req.Platform, + // TODO: resolveMode + }, nil + }, + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + require.NotEmpty(t, req.Source.Image.Digest) + _, err := digest.Parse(req.Source.Image.Digest) + require.NoError(t, err) + require.NotEmpty(t, req.Source.Image.Config) + var cfg ocispecs.Image + err = json.Unmarshal(req.Source.Image.Config, &cfg) + require.NoError(t, err) + require.NotEmpty(t, cfg.RootFS) + return &policysession.DecisionResponse{ + Action: sourcepolicpb.PolicyAction_ALLOW, + }, nil, nil + }, + }, + }, + } + + for _, tc := range tcases { + t.Run(tc.name, func(t *testing.T) { + st := tc.state() + def, err := st.Marshal(ctx) + require.NoError(t, err) + + callCounter := 0 + + p := policysession.NewPolicyProvider(func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + if callCounter >= len(tc.callbacks) { + return nil, nil, errors.Errorf("too many calls to policy callback %d", callCounter) + } + cb := tc.callbacks[callCounter] + callCounter++ + return cb(ctx, req) + }) + + _, err = c.Solve(ctx, def, SolveOpt{ + SourcePolicyProvider: p, + Exports: []ExportEntry{ + { + Type: ExporterOCI, + Output: fixedWriteCloser(nopWriteCloser{io.Discard}), + }, + }, + }, nil) + if tc.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedError) + return + } + require.NoError(t, err) + + require.Equal(t, len(tc.callbacks), callCounter, "not all policy callbacks were called") + }) + } +} diff --git a/client/solve.go b/client/solve.go index 991a37418d77..d94662a23df6 100644 --- a/client/solve.go +++ b/client/solve.go @@ -51,6 +51,7 @@ type SolveOpt struct { SessionPreInitialized bool // TODO: refactor to better session syncing Internal bool SourcePolicy *spb.Policy + SourcePolicyProvider session.Attachable Ref string } @@ -219,6 +220,10 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG s.Allow(filesync.NewFSSyncTarget(syncTargets...)) } + if opt.SourcePolicyProvider != nil { + s.Allow(opt.SourcePolicyProvider) + } + eg.Go(func() error { sd := c.sessionDialer if sd == nil { @@ -275,7 +280,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG }) } - resp, err := c.ControlClient().Solve(ctx, &controlapi.SolveRequest{ + sopt := &controlapi.SolveRequest{ Ref: ref, Definition: pbd, Exporters: exports, @@ -290,7 +295,12 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG Entitlements: slices.Clone(opt.AllowedEntitlements), Internal: opt.Internal, SourcePolicy: opt.SourcePolicy, - }) + } + if opt.SourcePolicyProvider != nil { + sopt.SourcePolicySession = s.ID() + } + + resp, err := c.ControlClient().Solve(ctx, sopt) if err != nil { return errors.Wrap(err, "failed to solve") } diff --git a/control/control.go b/control/control.go index 1f560b2880cf..64b0ee1ccc49 100644 --- a/control/control.go +++ b/control/control.go @@ -541,7 +541,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* Exporters: expis, CacheExporters: cacheExporters, EnableSessionExporter: req.EnableSessionExporter, - }, entitlementsFromPB(req.Entitlements), procs, req.Internal, req.SourcePolicy) + }, entitlementsFromPB(req.Entitlements), procs, req.Internal, req.SourcePolicy, req.SourcePolicySession) if err != nil { return nil, err } diff --git a/solver/llbsolver/bridge.go b/solver/llbsolver/bridge.go index 278337f466f5..0da43601d4fc 100644 --- a/solver/llbsolver/bridge.go +++ b/solver/llbsolver/bridge.go @@ -85,7 +85,7 @@ func (b *llbBridge) loadResult(ctx context.Context, def *pb.Definition, cacheImp if err != nil { return nil, err } - var polEngine SourcePolicyEvaluator + var polEngine *sourcepolicy.Engine if srcPol != nil || len(pol) > 0 { for _, p := range pol { if p == nil { @@ -143,7 +143,7 @@ func (b *llbBridge) loadResult(ctx context.Context, def *pb.Definition, cacheImp } dpc := &detectPrunedCacheID{} - edge, err := Load(ctx, def, polEngine, dpc.Load, ValidateEntitlements(ent, w.CDIManager()), WithCacheSources(cms), NormalizeRuntimePlatforms(), WithValidateCaps()) + edge, err := Load(ctx, def, b.policy(polEngine), dpc.Load, ValidateEntitlements(ent, w.CDIManager()), WithCacheSources(cms), NormalizeRuntimePlatforms(), WithValidateCaps()) if err != nil { return nil, errors.Wrap(err, "failed to load LLB") } @@ -163,6 +163,13 @@ func (b *llbBridge) loadResult(ctx context.Context, def *pb.Definition, cacheImp return res, nil } +func (b *llbBridge) policy(engine *sourcepolicy.Engine) SourcePolicyEvaluator { + return &policyEvaluator{ + llbBridge: b, + engine: engine, + } +} + func (b *llbBridge) validateEntitlements(p executor.ProcessInfo) error { ent, err := loadEntitlements(b.builder) if err != nil { @@ -349,6 +356,10 @@ func (rp *resultProxy) Result(ctx context.Context) (res solver.CachedResult, err } func (b *llbBridge) ResolveSourceMetadata(ctx context.Context, op *pb.SourceOp, opt sourceresolver.Opt) (resp *sourceresolver.MetaResponse, err error) { + return b.resolveSourceMetadata(ctx, op, opt, true) +} + +func (b *llbBridge) resolveSourceMetadata(ctx context.Context, op *pb.SourceOp, opt sourceresolver.Opt, withPolicy bool) (resp *sourceresolver.MetaResponse, err error) { w, err := b.resolveWorker() if err != nil { return nil, err @@ -379,8 +390,25 @@ func (b *llbBridge) ResolveSourceMetadata(ctx context.Context, op *pb.SourceOp, opt.SourcePolicies = append(opt.SourcePolicies, pol) } - if _, err := sourcepolicy.NewEngine(opt.SourcePolicies).Evaluate(ctx, op); err != nil { - return nil, errors.Wrap(err, "could not resolve image due to policy") + engine := sourcepolicy.NewEngine(opt.SourcePolicies) + + if !withPolicy { + if _, err := engine.Evaluate(ctx, op); err != nil { + return nil, errors.Wrap(err, "could not resolve image due to policy") + } + } else { + var p *ocispecs.Platform + if opt.ImageOpt != nil { + p = opt.ImageOpt.Platform + } else if opt.OCILayoutOpt != nil { + p = opt.OCILayoutOpt.Platform + } + if _, err := b.policy(engine).Evaluate(ctx, &pb.Op{ + Op: &pb.Op_Source{Source: op}, + Platform: toPBPlatform(p), + }); err != nil { + return nil, errors.Wrap(err, "could not resolve image due to policy") + } } // policy is evaluated, so we can remove it from the options diff --git a/solver/llbsolver/policy.go b/solver/llbsolver/policy.go new file mode 100644 index 000000000000..867630777e04 --- /dev/null +++ b/solver/llbsolver/policy.go @@ -0,0 +1,161 @@ +package llbsolver + +import ( + "context" + "strings" + + "github.com/moby/buildkit/client/llb/sourceresolver" + gatewaypb "github.com/moby/buildkit/frontend/gateway/pb" + "github.com/moby/buildkit/solver/pb" + "github.com/moby/buildkit/sourcepolicy" + spb "github.com/moby/buildkit/sourcepolicy/pb" + "github.com/moby/buildkit/sourcepolicy/policysession" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type policyEvaluator struct { + *llbBridge + engine *sourcepolicy.Engine +} + +func (p *policyEvaluator) Evaluate(ctx context.Context, op *pb.Op) (bool, error) { + source := op.GetSource() + if source == nil { + return false, nil + } + ok, err := p.engine.Evaluate(ctx, source) + if err != nil { + return false, err + } + sid, err := loadSourcePolicySession(p.builder) + if err != nil { + return false, err + } + if sid == "" { + return ok, nil + } + caller, err := p.sm.Get(ctx, sid, false) + if err != nil { + return false, err + } + + verifier := policysession.NewPolicyVerifierClient(caller.Conn()) + req := &policysession.CheckPolicyRequest{ + Platform: op.Platform, + Source: &gatewaypb.ResolveSourceMetaResponse{ + Source: source, + }, + } + + max := 0 + for { + max++ + if max > 10 { // TODO: better loop detection + return false, errors.Errorf("too many policy requests") + } + resp, err := verifier.CheckPolicy(ctx, req) + if err != nil { + return false, err + } + + metareq := resp.GetRequest() + if metareq != nil { + op := sourceresolver.Opt{ + LogName: metareq.LogName, + } + if metareq.Source.Identifier != source.Identifier { + return false, errors.Errorf("policy requested different source identifier: %q != %q", metareq.Source.Identifier, source.Identifier) + } + if err := mapsEqual(source.Attrs, metareq.Source.Attrs); err != nil { + return false, errors.Wrap(err, "policy requested different source attrs") + } + if metareq.ResolveMode != "" { + if strings.HasPrefix(metareq.Source.Identifier, "docker-image://") { + op.ImageOpt = &sourceresolver.ResolveImageOpt{ + ResolveMode: metareq.ResolveMode, + } + } + } + if strings.HasPrefix(metareq.Source.Identifier, "docker-image://") { + if op.ImageOpt == nil { + op.ImageOpt = &sourceresolver.ResolveImageOpt{} + } + op.ImageOpt.Platform = toOCIPlatform(metareq.Platform) + } + if strings.HasPrefix(metareq.Source.Identifier, "oci-layout://") { + op.OCILayoutOpt = &sourceresolver.ResolveOCILayoutOpt{ + Platform: toOCIPlatform(metareq.Platform), + } + } + resp, err := p.resolveSourceMetadata(ctx, metareq.Source, op, false) + if err != nil { + return false, errors.Wrap(err, "error resolving source metadata from policy request") + } + req.Source = &gatewaypb.ResolveSourceMetaResponse{ + Source: resp.Op, + } + if resp.Image != nil { + req.Source.Image = &gatewaypb.ResolveSourceImageResponse{ + Digest: resp.Image.Digest.String(), + Config: resp.Image.Config, + } + } + continue + } + + decision := resp.GetDecision() + if decision == nil { + return false, errors.Errorf("no decision in policy response") + } + if decision.Action == spb.PolicyAction_CONVERT { + return false, errors.Errorf("convert action not yet supported") + } + if decision.Action != spb.PolicyAction_ALLOW { + return false, errors.Errorf("source %q not allowed by policy: action %s", source.Identifier, decision.Action.String()) + } + return ok, nil + } +} + +func mapsEqual[K comparable, V comparable](a, b map[K]V) error { + if len(a) != len(b) { + return errors.Errorf("map length mismatch: %d != %d", len(a), len(b)) + } + for k, v := range a { + vb, ok := b[k] + if !ok { + return errors.Errorf("key %v missing from second map", k) + } + if vb != v { + return errors.Errorf("value mismatch for key %v: %v != %v", k, v, vb) + } + } + return nil +} + +func toPBPlatform(p *ocispecs.Platform) *pb.Platform { + if p == nil { + return nil + } + return &pb.Platform{ + Architecture: p.Architecture, + OS: p.OS, + Variant: p.Variant, + OSVersion: p.OSVersion, + OSFeatures: p.OSFeatures, + } +} + +func toOCIPlatform(p *pb.Platform) *ocispecs.Platform { + if p == nil { + return nil + } + return &ocispecs.Platform{ + Architecture: p.Architecture, + OS: p.OS, + Variant: p.Variant, + OSVersion: p.OSVersion, + OSFeatures: p.OSFeatures, + } +} diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 319034cf294d..f5d6dc43c206 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -52,8 +52,9 @@ import ( ) const ( - keyEntitlements = "llb.entitlements" - keySourcePolicy = "llb.sourcepolicy" + keyEntitlements = "llb.entitlements" + keySourcePolicy = "llb.sourcepolicy" + keySourcePolicySession = "llb.sourcepolicysession" ) type ExporterRequest struct { @@ -489,7 +490,7 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend }, nil } -func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req frontend.SolveRequest, exp ExporterRequest, ent []entitlements.Entitlement, post []Processor, internal bool, srcPol *spb.Policy) (_ *client.SolveResponse, err error) { +func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req frontend.SolveRequest, exp ExporterRequest, ent []entitlements.Entitlement, post []Processor, internal bool, srcPol *spb.Policy, policySession string) (_ *client.SolveResponse, err error) { j, err := s.solver.NewJob(id) if err != nil { return nil, err @@ -535,6 +536,9 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro } j.SetValue(keySourcePolicy, srcPol) } + if policySession != "" { + j.SetValue(keySourcePolicySession, policySession) + } j.SessionID = sessionID @@ -1248,3 +1252,21 @@ func loadSourcePolicy(b solver.Builder) (*spb.Policy, error) { } return &srcPol, nil } + +func loadSourcePolicySession(b solver.Builder) (string, error) { + var session string + err := b.EachValue(context.TODO(), keySourcePolicySession, func(v any) error { + x, ok := v.(string) + if !ok { + return errors.Errorf("invalid source policy session %T", v) + } + if x != "" { + session = x + } + return nil + }) + if err != nil { + return "", err + } + return session, nil +} diff --git a/solver/llbsolver/sourcepolicy.go b/solver/llbsolver/sourcepolicy.go index ac61bb547aac..11a49616b301 100644 --- a/solver/llbsolver/sourcepolicy.go +++ b/solver/llbsolver/sourcepolicy.go @@ -7,5 +7,5 @@ import ( ) type SourcePolicyEvaluator interface { - Evaluate(ctx context.Context, op *pb.SourceOp) (bool, error) + Evaluate(ctx context.Context, op *pb.Op) (bool, error) } diff --git a/solver/llbsolver/vertex.go b/solver/llbsolver/vertex.go index c80345403229..166676006b32 100644 --- a/solver/llbsolver/vertex.go +++ b/solver/llbsolver/vertex.go @@ -334,7 +334,7 @@ func loadLLB(ctx context.Context, def *pb.Definition, polEngine SourcePolicyEval } dgst := digest.FromBytes(dt) if polEngine != nil { - if _, err := polEngine.Evaluate(ctx, pbop.GetSource()); err != nil { + if _, err := polEngine.Evaluate(ctx, &pbop); err != nil { return solver.Edge{}, errors.Wrap(err, "error evaluating the source policy") } } diff --git a/sourcepolicy/policysession/policysession.pb.go b/sourcepolicy/policysession/policysession.pb.go new file mode 100644 index 000000000000..1c964b4d2cc4 --- /dev/null +++ b/sourcepolicy/policysession/policysession.pb.go @@ -0,0 +1,367 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.9 +// protoc v3.11.4 +// source: github.com/moby/buildkit/sourcepolicy/policysession/policysession.proto + +package policysession + +import ( + pb1 "github.com/moby/buildkit/frontend/gateway/pb" + pb "github.com/moby/buildkit/solver/pb" + pb2 "github.com/moby/buildkit/sourcepolicy/pb" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CheckPolicyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Platform *pb.Platform `protobuf:"bytes,1,opt,name=Platform,proto3" json:"Platform,omitempty"` + Source *pb1.ResolveSourceMetaResponse `protobuf:"bytes,2,opt,name=Source,proto3" json:"Source,omitempty"` + Caps map[string]bool `protobuf:"bytes,3,rep,name=caps,proto3" json:"caps,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CheckPolicyRequest) Reset() { + *x = CheckPolicyRequest{} + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CheckPolicyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckPolicyRequest) ProtoMessage() {} + +func (x *CheckPolicyRequest) ProtoReflect() protoreflect.Message { + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckPolicyRequest.ProtoReflect.Descriptor instead. +func (*CheckPolicyRequest) Descriptor() ([]byte, []int) { + return file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescGZIP(), []int{0} +} + +func (x *CheckPolicyRequest) GetPlatform() *pb.Platform { + if x != nil { + return x.Platform + } + return nil +} + +func (x *CheckPolicyRequest) GetSource() *pb1.ResolveSourceMetaResponse { + if x != nil { + return x.Source + } + return nil +} + +func (x *CheckPolicyRequest) GetCaps() map[string]bool { + if x != nil { + return x.Caps + } + return nil +} + +type CheckPolicyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Result: + // + // *CheckPolicyResponse_Decision + // *CheckPolicyResponse_Request + Result isCheckPolicyResponse_Result `protobuf_oneof:"result"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CheckPolicyResponse) Reset() { + *x = CheckPolicyResponse{} + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CheckPolicyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckPolicyResponse) ProtoMessage() {} + +func (x *CheckPolicyResponse) ProtoReflect() protoreflect.Message { + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckPolicyResponse.ProtoReflect.Descriptor instead. +func (*CheckPolicyResponse) Descriptor() ([]byte, []int) { + return file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescGZIP(), []int{1} +} + +func (x *CheckPolicyResponse) GetResult() isCheckPolicyResponse_Result { + if x != nil { + return x.Result + } + return nil +} + +func (x *CheckPolicyResponse) GetDecision() *DecisionResponse { + if x != nil { + if x, ok := x.Result.(*CheckPolicyResponse_Decision); ok { + return x.Decision + } + } + return nil +} + +func (x *CheckPolicyResponse) GetRequest() *pb1.ResolveSourceMetaRequest { + if x != nil { + if x, ok := x.Result.(*CheckPolicyResponse_Request); ok { + return x.Request + } + } + return nil +} + +type isCheckPolicyResponse_Result interface { + isCheckPolicyResponse_Result() +} + +type CheckPolicyResponse_Decision struct { + Decision *DecisionResponse `protobuf:"bytes,1,opt,name=decision,proto3,oneof"` +} + +type CheckPolicyResponse_Request struct { + Request *pb1.ResolveSourceMetaRequest `protobuf:"bytes,2,opt,name=request,proto3,oneof"` +} + +func (*CheckPolicyResponse_Decision) isCheckPolicyResponse_Result() {} + +func (*CheckPolicyResponse_Request) isCheckPolicyResponse_Result() {} + +type DecisionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Action pb2.PolicyAction `protobuf:"varint,1,opt,name=action,proto3,enum=moby.buildkit.v1.sourcepolicy.PolicyAction" json:"action,omitempty"` + DenyMessages []*DenyMessage `protobuf:"bytes,2,rep,name=denyMessages,proto3" json:"denyMessages,omitempty"` + Update *pb.SourceOp `protobuf:"bytes,3,opt,name=update,proto3" json:"update,omitempty"` // TODO: metadata and description? + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DecisionResponse) Reset() { + *x = DecisionResponse{} + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DecisionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DecisionResponse) ProtoMessage() {} + +func (x *DecisionResponse) ProtoReflect() protoreflect.Message { + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DecisionResponse.ProtoReflect.Descriptor instead. +func (*DecisionResponse) Descriptor() ([]byte, []int) { + return file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescGZIP(), []int{2} +} + +func (x *DecisionResponse) GetAction() pb2.PolicyAction { + if x != nil { + return x.Action + } + return pb2.PolicyAction(0) +} + +func (x *DecisionResponse) GetDenyMessages() []*DenyMessage { + if x != nil { + return x.DenyMessages + } + return nil +} + +func (x *DecisionResponse) GetUpdate() *pb.SourceOp { + if x != nil { + return x.Update + } + return nil +} + +type DenyMessage struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DenyMessage) Reset() { + *x = DenyMessage{} + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DenyMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DenyMessage) ProtoMessage() {} + +func (x *DenyMessage) ProtoReflect() protoreflect.Message { + mi := &file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DenyMessage.ProtoReflect.Descriptor instead. +func (*DenyMessage) Descriptor() ([]byte, []int) { + return file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescGZIP(), []int{3} +} + +func (x *DenyMessage) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto protoreflect.FileDescriptor + +const file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDesc = "" + + "\n" + + "Ggithub.com/moby/buildkit/sourcepolicy/policysession/policysession.proto\x12+moby.buildkit.v1.sourcepolicy.policysession\x1a:github.com/moby/buildkit/frontend/gateway/pb/gateway.proto\x1a,github.com/moby/buildkit/solver/pb/ops.proto\x1a5github.com/moby/buildkit/sourcepolicy/pb/policy.proto\"\xa4\x02\n" + + "\x12CheckPolicyRequest\x12(\n" + + "\bPlatform\x18\x01 \x01(\v2\f.pb.PlatformR\bPlatform\x12L\n" + + "\x06Source\x18\x02 \x01(\v24.moby.buildkit.v1.frontend.ResolveSourceMetaResponseR\x06Source\x12]\n" + + "\x04caps\x18\x03 \x03(\v2I.moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest.CapsEntryR\x04caps\x1a7\n" + + "\tCapsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\bR\x05value:\x028\x01\"\xcd\x01\n" + + "\x13CheckPolicyResponse\x12[\n" + + "\bdecision\x18\x01 \x01(\v2=.moby.buildkit.v1.sourcepolicy.policysession.DecisionResponseH\x00R\bdecision\x12O\n" + + "\arequest\x18\x02 \x01(\v23.moby.buildkit.v1.frontend.ResolveSourceMetaRequestH\x00R\arequestB\b\n" + + "\x06result\"\xdb\x01\n" + + "\x10DecisionResponse\x12C\n" + + "\x06action\x18\x01 \x01(\x0e2+.moby.buildkit.v1.sourcepolicy.PolicyActionR\x06action\x12\\\n" + + "\fdenyMessages\x18\x02 \x03(\v28.moby.buildkit.v1.sourcepolicy.policysession.DenyMessageR\fdenyMessages\x12$\n" + + "\x06update\x18\x03 \x01(\v2\f.pb.SourceOpR\x06update\"'\n" + + "\vDenyMessage\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage2\xa3\x01\n" + + "\x0ePolicyVerifier\x12\x90\x01\n" + + "\vCheckPolicy\x12?.moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest\x1a@.moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyResponseB5Z3github.com/moby/buildkit/sourcepolicy/policysessionb\x06proto3" + +var ( + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescOnce sync.Once + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescData []byte +) + +func file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescGZIP() []byte { + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescOnce.Do(func() { + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDesc), len(file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDesc))) + }) + return file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDescData +} + +var file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_goTypes = []any{ + (*CheckPolicyRequest)(nil), // 0: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest + (*CheckPolicyResponse)(nil), // 1: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyResponse + (*DecisionResponse)(nil), // 2: moby.buildkit.v1.sourcepolicy.policysession.DecisionResponse + (*DenyMessage)(nil), // 3: moby.buildkit.v1.sourcepolicy.policysession.DenyMessage + nil, // 4: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest.CapsEntry + (*pb.Platform)(nil), // 5: pb.Platform + (*pb1.ResolveSourceMetaResponse)(nil), // 6: moby.buildkit.v1.frontend.ResolveSourceMetaResponse + (*pb1.ResolveSourceMetaRequest)(nil), // 7: moby.buildkit.v1.frontend.ResolveSourceMetaRequest + (pb2.PolicyAction)(0), // 8: moby.buildkit.v1.sourcepolicy.PolicyAction + (*pb.SourceOp)(nil), // 9: pb.SourceOp +} +var file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_depIdxs = []int32{ + 5, // 0: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest.Platform:type_name -> pb.Platform + 6, // 1: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest.Source:type_name -> moby.buildkit.v1.frontend.ResolveSourceMetaResponse + 4, // 2: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest.caps:type_name -> moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest.CapsEntry + 2, // 3: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyResponse.decision:type_name -> moby.buildkit.v1.sourcepolicy.policysession.DecisionResponse + 7, // 4: moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyResponse.request:type_name -> moby.buildkit.v1.frontend.ResolveSourceMetaRequest + 8, // 5: moby.buildkit.v1.sourcepolicy.policysession.DecisionResponse.action:type_name -> moby.buildkit.v1.sourcepolicy.PolicyAction + 3, // 6: moby.buildkit.v1.sourcepolicy.policysession.DecisionResponse.denyMessages:type_name -> moby.buildkit.v1.sourcepolicy.policysession.DenyMessage + 9, // 7: moby.buildkit.v1.sourcepolicy.policysession.DecisionResponse.update:type_name -> pb.SourceOp + 0, // 8: moby.buildkit.v1.sourcepolicy.policysession.PolicyVerifier.CheckPolicy:input_type -> moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyRequest + 1, // 9: moby.buildkit.v1.sourcepolicy.policysession.PolicyVerifier.CheckPolicy:output_type -> moby.buildkit.v1.sourcepolicy.policysession.CheckPolicyResponse + 9, // [9:10] is the sub-list for method output_type + 8, // [8:9] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name +} + +func init() { file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_init() } +func file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_init() { + if File_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto != nil { + return + } + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes[1].OneofWrappers = []any{ + (*CheckPolicyResponse_Decision)(nil), + (*CheckPolicyResponse_Request)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDesc), len(file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_rawDesc)), + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_goTypes, + DependencyIndexes: file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_depIdxs, + MessageInfos: file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_msgTypes, + }.Build() + File_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto = out.File + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_goTypes = nil + file_github_com_moby_buildkit_sourcepolicy_policysession_policysession_proto_depIdxs = nil +} diff --git a/sourcepolicy/policysession/policysession.proto b/sourcepolicy/policysession/policysession.proto new file mode 100644 index 000000000000..c7724413f67f --- /dev/null +++ b/sourcepolicy/policysession/policysession.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package moby.buildkit.v1.sourcepolicy.policysession; + +option go_package = "github.com/moby/buildkit/sourcepolicy/policysession"; + +import "github.com/moby/buildkit/frontend/gateway/pb/gateway.proto"; +import "github.com/moby/buildkit/solver/pb/ops.proto"; +import "github.com/moby/buildkit/sourcepolicy/pb/policy.proto"; + +service PolicyVerifier { + rpc CheckPolicy(CheckPolicyRequest) returns (CheckPolicyResponse); +} + +message CheckPolicyRequest { + pb.Platform Platform = 1; + moby.buildkit.v1.frontend.ResolveSourceMetaResponse Source = 2; + map caps = 3; +} + +message CheckPolicyResponse { + oneof result { + DecisionResponse decision = 1; + moby.buildkit.v1.frontend.ResolveSourceMetaRequest request = 2; + } +} + +message DecisionResponse { + moby.buildkit.v1.sourcepolicy.PolicyAction action = 1; + repeated DenyMessage denyMessages = 2; + pb.SourceOp update = 3; // TODO: metadata and description? +} + +message DenyMessage { + string message = 1; +} \ No newline at end of file diff --git a/sourcepolicy/policysession/policysession_grpc.pb.go b/sourcepolicy/policysession/policysession_grpc.pb.go new file mode 100644 index 000000000000..7439acd4ccb5 --- /dev/null +++ b/sourcepolicy/policysession/policysession_grpc.pb.go @@ -0,0 +1,119 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.11.4 +// source: github.com/moby/buildkit/sourcepolicy/policysession/policysession.proto + +package policysession + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + PolicyVerifier_CheckPolicy_FullMethodName = "/moby.buildkit.v1.sourcepolicy.policysession.PolicyVerifier/CheckPolicy" +) + +// PolicyVerifierClient is the client API for PolicyVerifier service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PolicyVerifierClient interface { + CheckPolicy(ctx context.Context, in *CheckPolicyRequest, opts ...grpc.CallOption) (*CheckPolicyResponse, error) +} + +type policyVerifierClient struct { + cc grpc.ClientConnInterface +} + +func NewPolicyVerifierClient(cc grpc.ClientConnInterface) PolicyVerifierClient { + return &policyVerifierClient{cc} +} + +func (c *policyVerifierClient) CheckPolicy(ctx context.Context, in *CheckPolicyRequest, opts ...grpc.CallOption) (*CheckPolicyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CheckPolicyResponse) + err := c.cc.Invoke(ctx, PolicyVerifier_CheckPolicy_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PolicyVerifierServer is the server API for PolicyVerifier service. +// All implementations should embed UnimplementedPolicyVerifierServer +// for forward compatibility. +type PolicyVerifierServer interface { + CheckPolicy(context.Context, *CheckPolicyRequest) (*CheckPolicyResponse, error) +} + +// UnimplementedPolicyVerifierServer should be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedPolicyVerifierServer struct{} + +func (UnimplementedPolicyVerifierServer) CheckPolicy(context.Context, *CheckPolicyRequest) (*CheckPolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CheckPolicy not implemented") +} +func (UnimplementedPolicyVerifierServer) testEmbeddedByValue() {} + +// UnsafePolicyVerifierServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PolicyVerifierServer will +// result in compilation errors. +type UnsafePolicyVerifierServer interface { + mustEmbedUnimplementedPolicyVerifierServer() +} + +func RegisterPolicyVerifierServer(s grpc.ServiceRegistrar, srv PolicyVerifierServer) { + // If the following call pancis, it indicates UnimplementedPolicyVerifierServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&PolicyVerifier_ServiceDesc, srv) +} + +func _PolicyVerifier_CheckPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CheckPolicyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PolicyVerifierServer).CheckPolicy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PolicyVerifier_CheckPolicy_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PolicyVerifierServer).CheckPolicy(ctx, req.(*CheckPolicyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PolicyVerifier_ServiceDesc is the grpc.ServiceDesc for PolicyVerifier service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PolicyVerifier_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "moby.buildkit.v1.sourcepolicy.policysession.PolicyVerifier", + HandlerType: (*PolicyVerifierServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CheckPolicy", + Handler: _PolicyVerifier_CheckPolicy_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/moby/buildkit/sourcepolicy/policysession/policysession.proto", +} diff --git a/sourcepolicy/policysession/policysession_vtproto.pb.go b/sourcepolicy/policysession/policysession_vtproto.pb.go new file mode 100644 index 000000000000..d98e3b1cf9c0 --- /dev/null +++ b/sourcepolicy/policysession/policysession_vtproto.pb.go @@ -0,0 +1,1261 @@ +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: v0.6.1-0.20240319094008-0393e58bdf10 +// source: github.com/moby/buildkit/sourcepolicy/policysession/policysession.proto + +package policysession + +import ( + fmt "fmt" + pb "github.com/moby/buildkit/frontend/gateway/pb" + pb1 "github.com/moby/buildkit/solver/pb" + pb2 "github.com/moby/buildkit/sourcepolicy/pb" + protohelpers "github.com/planetscale/vtprotobuf/protohelpers" + proto "google.golang.org/protobuf/proto" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +func (m *CheckPolicyRequest) CloneVT() *CheckPolicyRequest { + if m == nil { + return (*CheckPolicyRequest)(nil) + } + r := new(CheckPolicyRequest) + r.Platform = m.Platform.CloneVT() + r.Source = m.Source.CloneVT() + if rhs := m.Caps; rhs != nil { + tmpContainer := make(map[string]bool, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v + } + r.Caps = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CheckPolicyRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *CheckPolicyResponse) CloneVT() *CheckPolicyResponse { + if m == nil { + return (*CheckPolicyResponse)(nil) + } + r := new(CheckPolicyResponse) + if m.Result != nil { + r.Result = m.Result.(interface { + CloneVT() isCheckPolicyResponse_Result + }).CloneVT() + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *CheckPolicyResponse) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *CheckPolicyResponse_Decision) CloneVT() isCheckPolicyResponse_Result { + if m == nil { + return (*CheckPolicyResponse_Decision)(nil) + } + r := new(CheckPolicyResponse_Decision) + r.Decision = m.Decision.CloneVT() + return r +} + +func (m *CheckPolicyResponse_Request) CloneVT() isCheckPolicyResponse_Result { + if m == nil { + return (*CheckPolicyResponse_Request)(nil) + } + r := new(CheckPolicyResponse_Request) + r.Request = m.Request.CloneVT() + return r +} + +func (m *DecisionResponse) CloneVT() *DecisionResponse { + if m == nil { + return (*DecisionResponse)(nil) + } + r := new(DecisionResponse) + r.Action = m.Action + r.Update = m.Update.CloneVT() + if rhs := m.DenyMessages; rhs != nil { + tmpContainer := make([]*DenyMessage, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.DenyMessages = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DecisionResponse) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *DenyMessage) CloneVT() *DenyMessage { + if m == nil { + return (*DenyMessage)(nil) + } + r := new(DenyMessage) + r.Message = m.Message + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *DenyMessage) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (this *CheckPolicyRequest) EqualVT(that *CheckPolicyRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if !this.Platform.EqualVT(that.Platform) { + return false + } + if !this.Source.EqualVT(that.Source) { + return false + } + if len(this.Caps) != len(that.Caps) { + return false + } + for i, vx := range this.Caps { + vy, ok := that.Caps[i] + if !ok { + return false + } + if vx != vy { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CheckPolicyRequest) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*CheckPolicyRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *CheckPolicyResponse) EqualVT(that *CheckPolicyResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Result == nil && that.Result != nil { + return false + } else if this.Result != nil { + if that.Result == nil { + return false + } + if !this.Result.(interface { + EqualVT(isCheckPolicyResponse_Result) bool + }).EqualVT(that.Result) { + return false + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *CheckPolicyResponse) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*CheckPolicyResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *CheckPolicyResponse_Decision) EqualVT(thatIface isCheckPolicyResponse_Result) bool { + that, ok := thatIface.(*CheckPolicyResponse_Decision) + if !ok { + return false + } + if this == that { + return true + } + if this == nil && that != nil || this != nil && that == nil { + return false + } + if p, q := this.Decision, that.Decision; p != q { + if p == nil { + p = &DecisionResponse{} + } + if q == nil { + q = &DecisionResponse{} + } + if !p.EqualVT(q) { + return false + } + } + return true +} + +func (this *CheckPolicyResponse_Request) EqualVT(thatIface isCheckPolicyResponse_Result) bool { + that, ok := thatIface.(*CheckPolicyResponse_Request) + if !ok { + return false + } + if this == that { + return true + } + if this == nil && that != nil || this != nil && that == nil { + return false + } + if p, q := this.Request, that.Request; p != q { + if p == nil { + p = &pb.ResolveSourceMetaRequest{} + } + if q == nil { + q = &pb.ResolveSourceMetaRequest{} + } + if !p.EqualVT(q) { + return false + } + } + return true +} + +func (this *DecisionResponse) EqualVT(that *DecisionResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Action != that.Action { + return false + } + if len(this.DenyMessages) != len(that.DenyMessages) { + return false + } + for i, vx := range this.DenyMessages { + vy := that.DenyMessages[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &DenyMessage{} + } + if q == nil { + q = &DenyMessage{} + } + if !p.EqualVT(q) { + return false + } + } + } + if !this.Update.EqualVT(that.Update) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DecisionResponse) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DecisionResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *DenyMessage) EqualVT(that *DenyMessage) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Message != that.Message { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *DenyMessage) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*DenyMessage) + if !ok { + return false + } + return this.EqualVT(that) +} +func (m *CheckPolicyRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CheckPolicyRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CheckPolicyRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Caps) > 0 { + for k := range m.Caps { + v := m.Caps[k] + baseI := i + i-- + if v { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if m.Source != nil { + size, err := m.Source.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Platform != nil { + size, err := m.Platform.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CheckPolicyResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CheckPolicyResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CheckPolicyResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if vtmsg, ok := m.Result.(interface { + MarshalToSizedBufferVT([]byte) (int, error) + }); ok { + size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + } + return len(dAtA) - i, nil +} + +func (m *CheckPolicyResponse_Decision) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CheckPolicyResponse_Decision) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Decision != nil { + size, err := m.Decision.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } else { + i = protohelpers.EncodeVarint(dAtA, i, 0) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *CheckPolicyResponse_Request) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *CheckPolicyResponse_Request) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Request != nil { + size, err := m.Request.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } else { + i = protohelpers.EncodeVarint(dAtA, i, 0) + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *DecisionResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DecisionResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DecisionResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Update != nil { + size, err := m.Update.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x1a + } + if len(m.DenyMessages) > 0 { + for iNdEx := len(m.DenyMessages) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.DenyMessages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + } + if m.Action != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DenyMessage) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DenyMessage) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *DenyMessage) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CheckPolicyRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Platform != nil { + l = m.Platform.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Source != nil { + l = m.Source.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.Caps) > 0 { + for k, v := range m.Caps { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + 1 + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *CheckPolicyResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if vtmsg, ok := m.Result.(interface{ SizeVT() int }); ok { + n += vtmsg.SizeVT() + } + n += len(m.unknownFields) + return n +} + +func (m *CheckPolicyResponse_Decision) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Decision != nil { + l = m.Decision.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } else { + n += 3 + } + return n +} +func (m *CheckPolicyResponse_Request) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Request != nil { + l = m.Request.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } else { + n += 3 + } + return n +} +func (m *DecisionResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Action != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) + } + if len(m.DenyMessages) > 0 { + for _, e := range m.DenyMessages { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if m.Update != nil { + l = m.Update.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *DenyMessage) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *CheckPolicyRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CheckPolicyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CheckPolicyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Platform == nil { + m.Platform = &pb1.Platform{} + } + if err := m.Platform.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Source == nil { + m.Source = &pb.ResolveSourceMetaResponse{} + } + if err := m.Source.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Caps", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Caps == nil { + m.Caps = make(map[string]bool) + } + var mapkey string + var mapvalue bool + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapvaluetemp int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapvaluetemp |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + mapvalue = bool(mapvaluetemp != 0) + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Caps[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CheckPolicyResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CheckPolicyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CheckPolicyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Decision", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if oneof, ok := m.Result.(*CheckPolicyResponse_Decision); ok { + if err := oneof.Decision.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + v := &DecisionResponse{} + if err := v.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Result = &CheckPolicyResponse_Decision{Decision: v} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if oneof, ok := m.Result.(*CheckPolicyResponse_Request); ok { + if err := oneof.Request.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + } else { + v := &pb.ResolveSourceMetaRequest{} + if err := v.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Result = &CheckPolicyResponse_Request{Request: v} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DecisionResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DecisionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DecisionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) + } + m.Action = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Action |= pb2.PolicyAction(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenyMessages", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DenyMessages = append(m.DenyMessages, &DenyMessage{}) + if err := m.DenyMessages[len(m.DenyMessages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Update", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Update == nil { + m.Update = &pb1.SourceOp{} + } + if err := m.Update.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DenyMessage) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DenyMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DenyMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/sourcepolicy/policysession/provider.go b/sourcepolicy/policysession/provider.go new file mode 100644 index 000000000000..88cdf39bed0f --- /dev/null +++ b/sourcepolicy/policysession/provider.go @@ -0,0 +1,46 @@ +package policysession + +import ( + context "context" + + pb "github.com/moby/buildkit/frontend/gateway/pb" + "github.com/pkg/errors" + grpc "google.golang.org/grpc" +) + +type PolicyCallback func(context.Context, *CheckPolicyRequest) (*DecisionResponse, *pb.ResolveSourceMetaRequest, error) + +type PolicyProvider struct { + f PolicyCallback +} + +func NewPolicyProvider(f PolicyCallback) *PolicyProvider { + return &PolicyProvider{ + f: f, + } +} + +func (p *PolicyProvider) CheckPolicy(ctx context.Context, req *CheckPolicyRequest) (*CheckPolicyResponse, error) { + decision, metareq, err := p.f(ctx, req) + if err != nil { + return nil, err + } + if metareq != nil && decision != nil { + return nil, errors.Errorf("cannot return both decision and meta request") + } + resp := &CheckPolicyResponse{} + if decision != nil { + resp.Result = &CheckPolicyResponse_Decision{ + Decision: decision, + } + } else if metareq != nil { + resp.Result = &CheckPolicyResponse_Request{ + Request: metareq, + } + } + return resp, nil +} + +func (p *PolicyProvider) Register(server *grpc.Server) { + RegisterPolicyVerifierServer(server, p) +} diff --git a/sourcepolicy/policysession/verifier.go b/sourcepolicy/policysession/verifier.go new file mode 100644 index 000000000000..2daf59c8ec8b --- /dev/null +++ b/sourcepolicy/policysession/verifier.go @@ -0,0 +1,30 @@ +package policysession + +import ( + context "context" + + "github.com/moby/buildkit/session" +) + +func NewVerifier(ctx context.Context, sm *session.Manager, gid string) (*PolicyVerifier, error) { + c, err := sm.Get(ctx, gid, false) + if err != nil { + return nil, err + } + client := NewPolicyVerifierClient(c.Conn()) + return &PolicyVerifier{ + client: client, + }, nil +} + +type PolicyVerifier struct { + client PolicyVerifierClient +} + +func (p *PolicyVerifier) Check(ctx context.Context, req *CheckPolicyRequest) (*CheckPolicyResponse, error) { + resp, err := p.client.CheckPolicy(ctx, req) + if err != nil { + return nil, err + } + return resp, nil +} From badc4d97564351b19108c16a0d03d3043b7f0819 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Tue, 7 Oct 2025 11:46:58 -0700 Subject: [PATCH 2/3] client: add policy test for source meta resolver Signed-off-by: Tonis Tiigi --- client/client_test.go | 1 + client/policy_test.go | 101 ++++++++++++++++-- .../policysession/policysession.pb.go | 2 +- .../policysession/policysession.proto | 2 +- 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 28a400b46d37..ae8bf0901d15 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -252,6 +252,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){ testGitResolveMutatedSource, testImageResolveAttestationChainRequiresNetwork, testSourcePolicySession, + testSourceMetaPolicySession, } func TestIntegration(t *testing.T) { diff --git a/client/policy_test.go b/client/policy_test.go index 6ff5bdf21461..90f28dbd50f5 100644 --- a/client/policy_test.go +++ b/client/policy_test.go @@ -3,16 +3,18 @@ package client import ( "context" "encoding/json" - "io" "runtime" "testing" + "github.com/containerd/platforms" "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/client/llb/sourceresolver" + client "github.com/moby/buildkit/frontend/gateway/client" pb "github.com/moby/buildkit/frontend/gateway/pb" + opspb "github.com/moby/buildkit/solver/pb" sourcepolicpb "github.com/moby/buildkit/sourcepolicy/pb" "github.com/moby/buildkit/sourcepolicy/policysession" "github.com/moby/buildkit/util/testutil/integration" - "github.com/moby/buildkit/util/testutil/workers" digest "github.com/opencontainers/go-digest" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -21,7 +23,6 @@ import ( func testSourcePolicySession(t *testing.T, sb integration.Sandbox) { requiresLinux(t) - workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter) ctx := sb.Context() @@ -88,7 +89,6 @@ func testSourcePolicySession(t *testing.T, sb integration.Sandbox) { return nil, &pb.ResolveSourceMetaRequest{ Source: req.Source.Source, Platform: req.Platform, - // TODO: resolveMode }, nil }, func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { @@ -128,13 +128,96 @@ func testSourcePolicySession(t *testing.T, sb integration.Sandbox) { _, err = c.Solve(ctx, def, SolveOpt{ SourcePolicyProvider: p, - Exports: []ExportEntry{ - { - Type: ExporterOCI, - Output: fixedWriteCloser(nopWriteCloser{io.Discard}), - }, + }, nil) + if tc.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedError) + return + } + require.NoError(t, err) + + require.Equal(t, len(tc.callbacks), callCounter, "not all policy callbacks were called") + }) + } +} + +func testSourceMetaPolicySession(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + + ctx := sb.Context() + + c, err := New(ctx, sb.Address()) + require.NoError(t, err) + defer c.Close() + + type tcase struct { + name string + source func() (*opspb.SourceOp, sourceresolver.Opt) + callbacks []policysession.PolicyCallback + expectedError string + } + tcases := []tcase{ + { + name: "basic alpine", + source: func() (*opspb.SourceOp, sourceresolver.Opt) { + p := platforms.DefaultSpec() + return &opspb.SourceOp{ + Identifier: "docker-image://docker.io/library/alpine:latest", + }, sourceresolver.Opt{ + ImageOpt: &sourceresolver.ResolveImageOpt{ + Platform: &p, + }, + } + }, + callbacks: []policysession.PolicyCallback{ + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, runtime.GOOS, req.Platform.OS) + require.Equal(t, runtime.GOARCH, req.Platform.Architecture) + + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + return &policysession.DecisionResponse{ + Action: sourcepolicpb.PolicyAction_ALLOW, + }, nil, nil }, + }, + }, + { + name: "alpine denied", + source: func() (*opspb.SourceOp, sourceresolver.Opt) { + return &opspb.SourceOp{ + Identifier: "docker-image://docker.io/library/alpine:latest", + }, sourceresolver.Opt{} + }, + callbacks: []policysession.PolicyCallback{ + func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + require.Equal(t, "docker-image://docker.io/library/alpine:latest", req.Source.Source.Identifier) + return nil, nil, errors.New("policy denied") + }, + }, + expectedError: "policy denied", + }, + } + + for _, tc := range tcases { + t.Run(tc.name, func(t *testing.T) { + callCounter := 0 + + p := policysession.NewPolicyProvider(func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + if callCounter >= len(tc.callbacks) { + return nil, nil, errors.Errorf("too many calls to policy callback %d", callCounter) + } + cb := tc.callbacks[callCounter] + callCounter++ + return cb(ctx, req) + }) + _, err = c.Build(ctx, SolveOpt{ + SourcePolicyProvider: p, + }, "test", func(ctx context.Context, c client.Client) (*client.Result, error) { + sop, opts := tc.source() + _, err = c.ResolveSourceMetadata(ctx, sop, opts) + return nil, err }, nil) + if tc.expectedError != "" { require.Error(t, err) require.Contains(t, err.Error(), tc.expectedError) diff --git a/sourcepolicy/policysession/policysession.pb.go b/sourcepolicy/policysession/policysession.pb.go index 1c964b4d2cc4..1dbcd1a95c45 100644 --- a/sourcepolicy/policysession/policysession.pb.go +++ b/sourcepolicy/policysession/policysession.pb.go @@ -170,7 +170,7 @@ type DecisionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Action pb2.PolicyAction `protobuf:"varint,1,opt,name=action,proto3,enum=moby.buildkit.v1.sourcepolicy.PolicyAction" json:"action,omitempty"` DenyMessages []*DenyMessage `protobuf:"bytes,2,rep,name=denyMessages,proto3" json:"denyMessages,omitempty"` - Update *pb.SourceOp `protobuf:"bytes,3,opt,name=update,proto3" json:"update,omitempty"` // TODO: metadata and description? + Update *pb.SourceOp `protobuf:"bytes,3,opt,name=update,proto3" json:"update,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } diff --git a/sourcepolicy/policysession/policysession.proto b/sourcepolicy/policysession/policysession.proto index c7724413f67f..209a124bc45a 100644 --- a/sourcepolicy/policysession/policysession.proto +++ b/sourcepolicy/policysession/policysession.proto @@ -28,7 +28,7 @@ message CheckPolicyResponse { message DecisionResponse { moby.buildkit.v1.sourcepolicy.PolicyAction action = 1; repeated DenyMessage denyMessages = 2; - pb.SourceOp update = 3; // TODO: metadata and description? + pb.SourceOp update = 3; } message DenyMessage { From bab17afc53d8c8388408f0a0621a710451aa6726 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 8 Oct 2025 10:11:55 -0700 Subject: [PATCH 3/3] llbsolver: update policy validation to parallel Signed-off-by: Tonis Tiigi --- client/client_test.go | 1 + client/policy_test.go | 63 +++++++++++++++++++ solver/llbsolver/vertex.go | 24 +++++-- .../policysession/policysession.pb.go | 2 +- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index ae8bf0901d15..a84dd93191f1 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -253,6 +253,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){ testImageResolveAttestationChainRequiresNetwork, testSourcePolicySession, testSourceMetaPolicySession, + testSourcePolicyParallelSession, } func TestIntegration(t *testing.T) { diff --git a/client/policy_test.go b/client/policy_test.go index 90f28dbd50f5..ca20097f5399 100644 --- a/client/policy_test.go +++ b/client/policy_test.go @@ -4,7 +4,9 @@ import ( "context" "encoding/json" "runtime" + "strings" "testing" + "time" "github.com/containerd/platforms" "github.com/moby/buildkit/client/llb" @@ -229,3 +231,64 @@ func testSourceMetaPolicySession(t *testing.T, sb integration.Sandbox) { }) } } + +func testSourcePolicyParallelSession(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + + ctx := sb.Context() + + c, err := New(ctx, sb.Address()) + require.NoError(t, err) + defer c.Close() + + def, err := llb.Image("alpine").File(llb.Copy(llb.Image("busybox"), "/etc/passwd", "passwd2")).Marshal(ctx) + require.NoError(t, err) + + countAlpine := 0 + countBusybox := 0 + waitBusyboxStart := make(chan struct{}) + waitAlpineDone := make(chan struct{}) + + p := policysession.NewPolicyProvider(func(ctx context.Context, req *policysession.CheckPolicyRequest) (*policysession.DecisionResponse, *pb.ResolveSourceMetaRequest, error) { + switch req.Source.Source.Identifier { + case "docker-image://docker.io/library/alpine:latest": + switch countAlpine { + case 0: + <-waitBusyboxStart + require.Nil(t, req.Source.Image) + countAlpine++ + return nil, &pb.ResolveSourceMetaRequest{ + Source: req.Source.Source, + Platform: req.Platform, + }, nil + case 1: + require.NotNil(t, req.Source.Image) + require.True(t, strings.HasPrefix(req.Source.Image.Digest, "sha256:")) + countAlpine++ + close(waitAlpineDone) + return &policysession.DecisionResponse{ + Action: sourcepolicpb.PolicyAction_ALLOW, + }, nil, nil + default: + require.Fail(t, "too many calls for alpine") + } + case "docker-image://docker.io/library/busybox:latest": + time.Sleep(200 * time.Millisecond) + close(waitBusyboxStart) + countBusybox++ + <-waitAlpineDone + return &policysession.DecisionResponse{ + Action: sourcepolicpb.PolicyAction_ALLOW, + }, nil, nil + } + return nil, nil, errors.Errorf("unexpected source %q", req.Source.Source.Identifier) + }) + + _, err = c.Solve(ctx, def, SolveOpt{ + SourcePolicyProvider: p, + }, nil) + require.NoError(t, err) + + require.Equal(t, 2, countAlpine) + require.Equal(t, 1, countBusybox) +} diff --git a/solver/llbsolver/vertex.go b/solver/llbsolver/vertex.go index 166676006b32..ca839ccb622c 100644 --- a/solver/llbsolver/vertex.go +++ b/solver/llbsolver/vertex.go @@ -16,6 +16,7 @@ import ( digest "github.com/opencontainers/go-digest" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "golang.org/x/sync/errgroup" ) type vertex struct { @@ -324,6 +325,7 @@ func loadLLB(ctx context.Context, def *pb.Definition, polEngine SourcePolicyEval } allOps := make(map[digest.Digest]*op) + sources := make(map[digest.Digest]struct{}) var lastDgst digest.Digest @@ -333,10 +335,8 @@ func loadLLB(ctx context.Context, def *pb.Definition, polEngine SourcePolicyEval return solver.Edge{}, errors.Wrap(err, "failed to parse llb proto op") } dgst := digest.FromBytes(dt) - if polEngine != nil { - if _, err := polEngine.Evaluate(ctx, &pbop); err != nil { - return solver.Edge{}, errors.Wrap(err, "error evaluating the source policy") - } + if pbop.GetSource() != nil { + sources[dgst] = struct{}{} } allOps[dgst] = &op{ Op: &pbop, @@ -345,6 +345,22 @@ func loadLLB(ctx context.Context, def *pb.Definition, polEngine SourcePolicyEval lastDgst = dgst } + if polEngine != nil && len(sources) > 0 { + var eg errgroup.Group + for dgst := range sources { + eg.Go(func() error { + op := allOps[dgst] + if _, err := polEngine.Evaluate(ctx, op.Op); err != nil { + return errors.Wrap(err, "error evaluating the source policy") + } + return nil + }) + } + if err := eg.Wait(); err != nil { + return solver.Edge{}, err + } + } + mutatedDigests := make(map[digest.Digest]digest.Digest) // key: old, val: new for dgst := range allOps { if _, err := recomputeDigests(ctx, allOps, mutatedDigests, dgst); err != nil { diff --git a/sourcepolicy/policysession/policysession.pb.go b/sourcepolicy/policysession/policysession.pb.go index 1dbcd1a95c45..5f37e0098edd 100644 --- a/sourcepolicy/policysession/policysession.pb.go +++ b/sourcepolicy/policysession/policysession.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 +// protoc-gen-go v1.36.10 // protoc v3.11.4 // source: github.com/moby/buildkit/sourcepolicy/policysession/policysession.proto