Skip to content

Commit

Permalink
Merge pull request #5742 from tonistiigi/cdi-entitlements
Browse files Browse the repository at this point in the history
add autoallow and entitlements support to CDI devices
  • Loading branch information
crazy-max authored Feb 18, 2025
2 parents dae48b3 + 5f6b265 commit 14e8808
Show file tree
Hide file tree
Showing 24 changed files with 544 additions and 99 deletions.
14 changes: 7 additions & 7 deletions client/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,7 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox

command := []string{"sh", "-c", `cat /proc/self/status | grep CapEff | cut -f 2`}
mode := llb.SecurityModeSandbox
var allowedEntitlements []entitlements.Entitlement
var allowedEntitlements []string
var assertCaps func(caps uint64)
secMode := sb.Value("secmode")
if secMode == securitySandbox {
Expand All @@ -1850,7 +1850,7 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
*/
require.Equal(t, uint64(0xa80425fb), caps)
}
allowedEntitlements = []entitlements.Entitlement{}
allowedEntitlements = []string{}
if expectFail {
return
}
Expand All @@ -1869,9 +1869,9 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
require.Equal(t, uint64(0x3fffffffff), caps&0x3fffffffff)
}
mode = llb.SecurityModeInsecure
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
allowedEntitlements = []string{entitlements.EntitlementSecurityInsecure.String()}
if expectFail {
allowedEntitlements = []entitlements.Entitlement{}
allowedEntitlements = []string{}
}
}

Expand Down Expand Up @@ -2046,13 +2046,13 @@ func testClientGatewayContainerHostNetworking(t *testing.T, sb integration.Sandb
ctx := sb.Context()
product := "buildkit_test"

var allowedEntitlements []entitlements.Entitlement
var allowedEntitlements []string
netMode := pb.NetMode_UNSET
if sb.Value("netmode") == hostNetwork {
netMode = pb.NetMode_HOST
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementNetworkHost}
allowedEntitlements = []string{entitlements.EntitlementNetworkHost.String()}
if expectFail {
allowedEntitlements = []entitlements.Entitlement{}
allowedEntitlements = []string{}
}
}
c, err := New(sb.Context(), sb.Address())
Expand Down
135 changes: 122 additions & 13 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sa

integration.Run(t, integration.TestFuncs(
testCDI,
testCDINotAllowed,
testCDIEntitlement,
testCDIFirst,
testCDIWildcard,
testCDIClass,
Expand Down Expand Up @@ -400,9 +402,9 @@ func testHostNetworking(t *testing.T, sb integration.Sandbox) {
t.SkipNow()
}
netMode := sb.Value("netmode")
var allowedEntitlements []entitlements.Entitlement
var allowedEntitlements []string
if netMode == hostNetwork {
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementNetworkHost}
allowedEntitlements = []string{entitlements.EntitlementNetworkHost.String()}
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
Expand Down Expand Up @@ -1063,7 +1065,7 @@ func testSecurityMode(t *testing.T, sb integration.Sandbox) {
workers.CheckFeatureCompat(t, sb, workers.FeatureSecurityMode)
command := `sh -c 'cat /proc/self/status | grep CapEff | cut -f 2 > /out'`
mode := llb.SecurityModeSandbox
var allowedEntitlements []entitlements.Entitlement
var allowedEntitlements []string
var assertCaps func(caps uint64)
secMode := sb.Value("secmode")
if secMode == securitySandbox {
Expand All @@ -1075,7 +1077,7 @@ func testSecurityMode(t *testing.T, sb integration.Sandbox) {
*/
require.Equal(t, uint64(0xa80425fb), caps)
}
allowedEntitlements = []entitlements.Entitlement{}
allowedEntitlements = []string{}
} else {
assertCaps = func(caps uint64) {
/*
Expand All @@ -1091,7 +1093,7 @@ func testSecurityMode(t *testing.T, sb integration.Sandbox) {
require.Equal(t, uint64(0x3fffffffff), caps&0x3fffffffff)
}
mode = llb.SecurityModeInsecure
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
allowedEntitlements = []string{entitlements.EntitlementSecurityInsecure.String()}
}

c, err := New(sb.Context(), sb.Address())
Expand Down Expand Up @@ -1138,13 +1140,13 @@ func testSecurityModeSysfs(t *testing.T, sb integration.Sandbox) {
}

mode := llb.SecurityModeSandbox
var allowedEntitlements []entitlements.Entitlement
var allowedEntitlements []string
secMode := sb.Value("secmode")
if secMode == securitySandbox {
allowedEntitlements = []entitlements.Entitlement{}
allowedEntitlements = []string{}
} else {
mode = llb.SecurityModeInsecure
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
allowedEntitlements = []string{entitlements.EntitlementSecurityInsecure.String()}
}

c, err := New(sb.Context(), sb.Address())
Expand Down Expand Up @@ -1191,7 +1193,7 @@ func testSecurityModeErrors(t *testing.T, sb integration.Sandbox) {
require.NoError(t, err)

_, err = c.Solve(sb.Context(), def, SolveOpt{
AllowedEntitlements: []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure},
AllowedEntitlements: []string{entitlements.EntitlementSecurityInsecure.String()},
}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
Expand Down Expand Up @@ -11054,22 +11056,26 @@ func testCDI(t *testing.T, sb integration.Sandbox) {
defer c.Close()

require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(`
cdiVersion: "0.3.0"
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
containerEdits:
env:
- FOO=injected
annotations:
org.mobyproject.buildkit.device.autoallow: true
`), 0600))
require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor2-device.yaml"), []byte(`
cdiVersion: "0.3.0"
cdiVersion: "0.6.0"
kind: "vendor2.com/device"
devices:
- name: bar
containerEdits:
env:
- BAR=injected
annotations:
org.mobyproject.buildkit.device.autoallow: true
`), 0600))

busybox := llb.Image("busybox:latest")
Expand Down Expand Up @@ -11107,6 +11113,104 @@ devices:
require.Contains(t, strings.TrimSpace(string(dt2)), `BAR=injected`)
}

func testCDINotAllowed(t *testing.T, sb integration.Sandbox) {
if sb.Rootless() {
t.SkipNow()
}

integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureCDI)
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()

require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(`
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
containerEdits:
env:
- FOO=injected
`), 0600))

busybox := llb.Image("busybox:latest")
st := llb.Scratch()

run := func(cmd string, ro ...llb.RunOption) {
st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
}

run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device=foo")))

def, err := st.Marshal(sb.Context())
require.NoError(t, err)

destDir := t.TempDir()

_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.Error(t, err)
require.ErrorContains(t, err, "requested by the build but not allowed")
}

func testCDIEntitlement(t *testing.T, sb integration.Sandbox) {
if sb.Rootless() {
t.SkipNow()
}

integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureCDI)
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()

require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(`
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
containerEdits:
env:
- FOO=injected
`), 0600))

busybox := llb.Image("busybox:latest")
st := llb.Scratch()

run := func(cmd string, ro ...llb.RunOption) {
st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
}

run(`sh -c 'env|sort | tee foo.env'`, llb.AddCDIDevice(llb.CDIDeviceName("vendor1.com/device=foo")))

def, err := st.Marshal(sb.Context())
require.NoError(t, err)

destDir := t.TempDir()

_, err = c.Solve(sb.Context(), def, SolveOpt{
AllowedEntitlements: []string{"device=vendor1.com/device"},
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)

dt, err := os.ReadFile(filepath.Join(destDir, "foo.env"))
require.NoError(t, err)
require.Contains(t, strings.TrimSpace(string(dt)), `FOO=injected`)
}

func testCDIFirst(t *testing.T, sb integration.Sandbox) {
if sb.Rootless() {
t.SkipNow()
Expand All @@ -11119,7 +11223,7 @@ func testCDIFirst(t *testing.T, sb integration.Sandbox) {
defer c.Close()

require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(`
cdiVersion: "0.3.0"
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
Expand All @@ -11138,6 +11242,8 @@ devices:
containerEdits:
env:
- QUX=injected
annotations:
org.mobyproject.buildkit.device.autoallow: true
`), 0600))

busybox := llb.Image("busybox:latest")
Expand Down Expand Up @@ -11184,7 +11290,7 @@ func testCDIWildcard(t *testing.T, sb integration.Sandbox) {
defer c.Close()

require.NoError(t, os.WriteFile(filepath.Join(sb.CDISpecDir(), "vendor1-device.yaml"), []byte(`
cdiVersion: "0.3.0"
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
Expand All @@ -11195,6 +11301,8 @@ devices:
containerEdits:
env:
- BAR=injected
annotations:
org.mobyproject.buildkit.device.autoallow: true
`), 0600))

busybox := llb.Image("busybox:latest")
Expand Down Expand Up @@ -11243,6 +11351,7 @@ cdiVersion: "0.6.0"
kind: "vendor1.com/device"
annotations:
foo.bar.baz: FOO
org.mobyproject.buildkit.device.autoallow: true
devices:
- name: foo
annotations:
Expand Down
14 changes: 3 additions & 11 deletions client/solve.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"maps"
"os"
"slices"
"strings"
"time"

Expand All @@ -24,7 +25,6 @@ import (
"github.com/moby/buildkit/solver/pb"
spb "github.com/moby/buildkit/sourcepolicy/pb"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/entitlements"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
Expand All @@ -45,7 +45,7 @@ type SolveOpt struct {
CacheExports []CacheOptionsEntry
CacheImports []CacheOptionsEntry
Session []session.Attachable
AllowedEntitlements []entitlements.Entitlement
AllowedEntitlements []string
SharedSession *session.Session // TODO: refactor to better session syncing
SessionPreInitialized bool // TODO: refactor to better session syncing
Internal bool
Expand Down Expand Up @@ -277,7 +277,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
FrontendAttrs: frontendAttrs,
FrontendInputs: frontendInputs,
Cache: &cacheOpt.options,
Entitlements: entitlementsToPB(opt.AllowedEntitlements),
Entitlements: slices.Clone(opt.AllowedEntitlements),
Internal: opt.Internal,
SourcePolicy: opt.SourcePolicy,
})
Expand Down Expand Up @@ -553,11 +553,3 @@ func prepareMounts(opt *SolveOpt) (map[string]fsutil.FS, error) {
}
return mounts, nil
}

func entitlementsToPB(entitlements []entitlements.Entitlement) []string {
clone := make([]string, len(entitlements))
for i, e := range entitlements {
clone[i] = string(e)
}
return clone
}
5 changes: 2 additions & 3 deletions cmd/buildctl/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,7 @@ func buildAction(clicontext *cli.Context) error {
attachable = append(attachable, secretProvider)
}

allowed, err := build.ParseAllow(clicontext.StringSlice("allow"))
if err != nil {
if err := build.ValidateAllow(clicontext.StringSlice("allow")); err != nil {
return err
}

Expand Down Expand Up @@ -258,7 +257,7 @@ func buildAction(clicontext *cli.Context) error {
CacheExports: cacheExports,
CacheImports: cacheImports,
Session: attachable,
AllowedEntitlements: allowed,
AllowedEntitlements: clicontext.StringSlice("allow"),
SourcePolicy: srcPol,
Ref: ref,
}
Expand Down
12 changes: 5 additions & 7 deletions cmd/buildctl/build/allow.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import (
"github.com/moby/buildkit/util/entitlements"
)

// ParseAllow parses --allow
func ParseAllow(inp []string) ([]entitlements.Entitlement, error) {
ent := make([]entitlements.Entitlement, 0, len(inp))
// ValidateAllow parses --allow
func ValidateAllow(inp []string) error {
for _, v := range inp {
e, err := entitlements.Parse(v)
_, _, err := entitlements.Parse(v)
if err != nil {
return nil, err
return err
}
ent = append(ent, e)
}
return ent, nil
return nil
}
2 changes: 1 addition & 1 deletion cmd/buildctl/debug/workers.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func printWorkersVerbose(tw *tabwriter.Writer, winfo []*client.WorkerInfo) {

for _, k := range sortedKeys(d.Annotations) {
v := d.Annotations[k]
fmt.Fprintf(tw, "\t\t%s:\t%s\n", k, v)
fmt.Fprintf(tw, "\tAnnotations:\t%s:\t%s\n", k, v)
}
}
fmt.Fprint(tw, "\n")
Expand Down
Loading

0 comments on commit 14e8808

Please sign in to comment.