Skip to content

Commit

Permalink
buildkitd: Frontend restriction support
Browse files Browse the repository at this point in the history
This commit adds buildkitd configuration options `allowed-frontends`
and `allowed-gateway-source`.  These options enable restricting the
allowed frontends or gateways sources to enforce local policy.

If allowed-frontends is empty (the default), all frontends (e.g,
"dockerfile.v0" and "gateway.v0") are allowed.  Otherwise, only those
listed are allowed

If allowed-gateway-sources is empty (the default), all gateway sources
are allowed.  Otherwise, only sources that match the patterns in this
list will be allowed.  Patterns are matched using
<https://pkg.go.dev/github.com/bmatcuk/doublestar/v4#Match>.  Note
that implicit references to docker.io should not be used in the
patterns since matching occurs on a fully expanded image name (for
example "docker/dockerfile" expands to "docker.io/docker/dockerfile").

Change-Id: Ia484401709ef6c13cf3e5a2e4d0e1c6bd0c47d13
Signed-off-by: Ahmon Dancy <[email protected]>
  • Loading branch information
Ahmon Dancy committed May 3, 2024
1 parent 51d85d7 commit 60e9209
Show file tree
Hide file tree
Showing 20 changed files with 2,302 additions and 4 deletions.
3 changes: 3 additions & 0 deletions cmd/buildkitd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type Config struct {
DNS *DNSConfig `toml:"dns"`

History *HistoryConfig `toml:"history"`

AllowedFrontends []string `toml:"allowed-frontends"`
AllowedGatewaySources []string `toml:"allowed-gateway-sources"`
}

type LogConfig struct {
Expand Down
20 changes: 19 additions & 1 deletion cmd/buildkitd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,25 @@ func newController(c *cli.Context, cfg *config.Config) (*control.Controller, err
}
frontends := map[string]frontend.Frontend{}
frontends["dockerfile.v0"] = forwarder.NewGatewayForwarder(wc.Infos(), dockerfile.Build)
frontends["gateway.v0"] = gateway.NewGatewayFrontend(wc.Infos())
frontends["gateway.v0"] = gateway.NewGatewayFrontend(wc.Infos(), cfg.AllowedGatewaySources)

if len(cfg.AllowedFrontends) > 0 {
var frontendIsAllowed = func(desired_frontend string) bool {
for _, allowedFrontend := range cfg.AllowedFrontends {
if desired_frontend == allowedFrontend {
return true
}
}
return false
}

// Remove any frontend that is not in the allowed list.
for fe := range frontends {
if !frontendIsAllowed(fe) {
delete(frontends, fe)
}
}
}

cacheStorage, err := bboltcachestorage.NewStore(filepath.Join(cfg.Root, "cache.db"))
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions docs/buildkitd.toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ insecure-entitlements = [ "network.host", "security.insecure" ]
options=["edns0"]
searchDomains=["example.com"]

# If allowed-frontends is empty, all frontends (e.g, "dockerfile.v0"
# and "gateway.v0") are allowed. Otherwise, only those listed are
# allowed
allowed-frontends = []

# If allowed-gateway-sources is empty, all gateway sources are allowed.
# Otherwise, only sources that match the patterns in this list will be
# allowed. Patterns are matched using <https://pkg.go.dev/github.com/bmatcuk/doublestar/v4#Match>.
# Note that implicit references to docker.io should not be used in the patterns since
# matching occurs on a fully expanded image name (for example "docker/dockerfile" expands
# to "docker.io/docker/dockerfile").
allowed-gateway-sources = []

[grpc]
address = [ "tcp://0.0.0.0:1234" ]
# debugAddress is address for attaching go profiles and debuggers.
Expand Down
36 changes: 33 additions & 3 deletions frontend/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,25 @@ import (
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/status"

doublestar "github.com/bmatcuk/doublestar/v4"
)

const (
keySource = "source"
keyDevel = "gateway-devel"
)

func NewGatewayFrontend(w worker.Infos) frontend.Frontend {
func NewGatewayFrontend(w worker.Infos, allowedSources []string) frontend.Frontend {
return &gatewayFrontend{
workers: w,
workers: w,
allowedSources: allowedSources,
}
}

type gatewayFrontend struct {
workers worker.Infos
workers worker.Infos
allowedSources []string
}

func filterPrefix(opts map[string]string, pfx string) map[string]string {
Expand All @@ -87,6 +91,27 @@ func filterPrefix(opts map[string]string, pfx string) map[string]string {
return m
}

func (gf *gatewayFrontend) checkSourceIsAllowed(source string) error {
if len(gf.allowedSources) == 0 {
// No source restrictions in place
return nil
}

sourceRef, err := reference.ParseNormalizedNamed(source)
if err != nil {
return err
}

taglessSource := reference.TrimNamed(sourceRef).Name()

for _, allowedFrontend := range gf.allowedSources {
if matched, _ := doublestar.Match(allowedFrontend, taglessSource); matched {
return nil
}
}
return errors.Errorf("'%s' is not an allowed gateway frontend", source)
}

func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, opts map[string]string, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*frontend.Result, error) {
source, ok := opts[keySource]
if !ok {
Expand All @@ -101,6 +126,11 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten

var frontendDef *opspb.Definition

err := gf.checkSourceIsAllowed(source)
if err != nil {
return nil, err
}

if isDevel {
devRes, err := llbBridge.Solve(ctx,
frontend.SolveRequest{
Expand Down
45 changes: 45 additions & 0 deletions frontend/gateway/gateway_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package gateway

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCheckSourceIsAllowed(t *testing.T) {
var gw = gatewayFrontend{
workers: nil,
allowedSources: []string{}, // no restrictions
}

err := gw.checkSourceIsAllowed("anything")
assert.NoError(t, err)

gw.allowedSources = []string{"docker-registry.wikimedia.org/repos/releng/blubber/buildkit"}
err = gw.checkSourceIsAllowed("docker-registry.wikimedia.org/repos/releng/blubber/buildkit")
assert.NoError(t, err)
err = gw.checkSourceIsAllowed("docker-registry.wikimedia.org/repos/releng/blubber/buildkit:v1.2.3")
assert.NoError(t, err)
err = gw.checkSourceIsAllowed("docker-registry.wikimedia.org/something-else")
assert.Error(t, err)

gw.allowedSources = []string{"implicit-docker-io-reference", "docker/dockerfile"}
// This source will be rejected because after parsing it becomes
// "docker.io/library/implicit-docker-io-reference" which does not match
// the allowed source of "implicit-docker-io-reference".
err = gw.checkSourceIsAllowed("implicit-docker-io-reference")
assert.Error(t, err)
// "docker/dockerfile" expands to "docker.io/docker/dockerfile", so no match here.
err = gw.checkSourceIsAllowed("docker/dockerfile")
assert.Error(t, err)

gw.allowedSources = []string{"docker-registry.wikimedia.org/*"}
err = gw.checkSourceIsAllowed("docker-registry.wikimedia.org/something-else")
assert.NoError(t, err)
err = gw.checkSourceIsAllowed("docker-registry.wikimedia.org/topdir/below")
assert.Error(t, err)

gw.allowedSources = []string{"docker-registry.wikimedia.org/**"}
err = gw.checkSourceIsAllowed("docker-registry.wikimedia.org/topdir/below")
assert.NoError(t, err)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
10 changes: 10 additions & 0 deletions vendor/github.com/bmatcuk/doublestar/v4/.codecov.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions vendor/github.com/bmatcuk/doublestar/v4/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions vendor/github.com/bmatcuk/doublestar/v4/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 60e9209

Please sign in to comment.