Skip to content

security entitlement support #570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 106 additions & 1 deletion client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/moby/buildkit/session/secrets/secretsprovider"
"github.com/moby/buildkit/session/sshforward/sshprovider"
"github.com/moby/buildkit/util/contentutil"
"github.com/moby/buildkit/util/entitlements"
"github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/httpserver"
"github.com/moby/buildkit/util/testutil/integration"
Expand All @@ -51,6 +52,8 @@ type nopWriteCloser struct {
func (nopWriteCloser) Close() error { return nil }

func TestClientIntegration(t *testing.T) {
mirrors := integration.WithMirroredImages(integration.OfficialImages("busybox:latest", "alpine:latest"))

integration.Run(t, []integration.Test{
testRelativeWorkDir,
testFileOpMkdirMkfile,
Expand Down Expand Up @@ -89,8 +92,17 @@ func TestClientIntegration(t *testing.T) {
testPushByDigest,
testBasicInlineCacheImportExport,
testExportBusyboxLocal,
}, mirrors)

integration.Run(t, []integration.Test{
testSecurityMode,
testSecurityModeErrors,
},
integration.WithMirroredImages(integration.OfficialImages("busybox:latest", "alpine:latest")),
mirrors,
integration.WithMatrix("secmode", map[string]interface{}{
"sandbox": securitySandbox,
"insecure": securityInsecure,
}),
)
}

Expand Down Expand Up @@ -419,6 +431,84 @@ func testPushByDigest(t *testing.T, sb integration.Sandbox) {
require.True(t, desc.Size > 0)
}

func testSecurityMode(t *testing.T, sb integration.Sandbox) {
var command string
mode := llb.SecurityModeSandbox
var allowedEntitlements []entitlements.Entitlement
secMode := sb.Value("secmode")
if secMode == securitySandbox {
/*
$ capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
*/
command = `sh -c 'cat /proc/self/status | grep CapEff | grep "00000000a80425fb"'`
allowedEntitlements = []entitlements.Entitlement{}
} else {
/*
$ capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,
cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
*/
command = `sh -c 'cat /proc/self/status | grep CapEff | grep "0000003fffffffff"'`
mode = llb.SecurityModeInsecure
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
}

c, err := New(context.TODO(), sb.Address())
require.NoError(t, err)
defer c.Close()

st := llb.Image("busybox:latest").
Run(llb.Shlex(command),
llb.Security(mode))

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

_, err = c.Solve(context.TODO(), def, SolveOpt{
AllowedEntitlements: allowedEntitlements,
}, nil)

require.NoError(t, err)
}

func testSecurityModeErrors(t *testing.T, sb integration.Sandbox) {

c, err := New(context.TODO(), sb.Address())
require.NoError(t, err)
defer c.Close()
secMode := sb.Value("secmode")
if secMode == securitySandbox {

st := llb.Image("busybox:latest").
Run(llb.Shlex(`sh -c 'echo sandbox'`))

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

_, err = c.Solve(context.TODO(), def, SolveOpt{
AllowedEntitlements: []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure},
}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
}
if secMode == securityInsecure {
st := llb.Image("busybox:latest").
Run(llb.Shlex(`sh -c 'echo insecure'`), llb.Security(llb.SecurityModeInsecure))

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

_, err = c.Solve(context.TODO(), def, SolveOpt{}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
}
}

func testFrontendImageNaming(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
c, err := New(context.TODO(), sb.Address())
Expand Down Expand Up @@ -2334,3 +2424,18 @@ func (s *server) run(a agent.Agent) error {
go agent.ServeAgent(a, c)
}
}

type secModeSandbox struct{}

func (*secModeSandbox) UpdateConfigFile(in string) string {
return in
}

type secModeInsecure struct{}

func (*secModeInsecure) UpdateConfigFile(in string) string {
return in + "\n\ninsecure-entitlements = [\"security.insecure\"]\n"
}

var securitySandbox integration.ConfigUpdater = &secModeSandbox{}
var securityInsecure integration.ConfigUpdater = &secModeInsecure{}
21 changes: 19 additions & 2 deletions client/llb/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Meta struct {
ProxyEnv *ProxyEnv
ExtraHosts []HostIP
Network pb.NetMode
Security pb.SecurityMode
}

func NewExecOp(root Output, meta Meta, readOnly bool, c Constraints) *ExecOp {
Expand Down Expand Up @@ -166,13 +167,18 @@ func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
}

peo := &pb.ExecOp{
Meta: meta,
Network: e.meta.Network,
Meta: meta,
Network: e.meta.Network,
Security: e.meta.Security,
}
if e.meta.Network != NetModeSandbox {
addCap(&e.constraints, pb.CapExecMetaNetwork)
}

if e.meta.Security != SecurityModeInsecure {
addCap(&e.constraints, pb.CapExecMetaSecurity)
}

if p := e.meta.ProxyEnv; p != nil {
peo.Meta.ProxyEnv = &pb.ProxyEnv{
HttpProxy: p.HttpProxy,
Expand Down Expand Up @@ -408,6 +414,12 @@ func Network(n pb.NetMode) RunOption {
})
}

func Security(s pb.SecurityMode) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = security(s)(ei.State)
})
}

func Shlex(str string) RunOption {
return Shlexf(str)
}
Expand Down Expand Up @@ -623,3 +635,8 @@ const (
NetModeHost = pb.NetMode_HOST
NetModeNone = pb.NetMode_NONE
)

const (
SecurityModeInsecure = pb.SecurityMode_INSECURE
SecurityModeSandbox = pb.SecurityMode_SANDBOX
)
16 changes: 15 additions & 1 deletion client/llb/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
keyExtraHost = contextKeyT("llb.exec.extrahost")
keyPlatform = contextKeyT("llb.platform")
keyNetwork = contextKeyT("llb.network")
keySecurity = contextKeyT("llb.security")
)

func addEnvf(key, value string, v ...interface{}) StateOption {
Expand Down Expand Up @@ -148,7 +149,6 @@ func network(v pb.NetMode) StateOption {
return s.WithValue(keyNetwork, v)
}
}

func getNetwork(s State) pb.NetMode {
v := s.Value(keyNetwork)
if v != nil {
Expand All @@ -158,6 +158,20 @@ func getNetwork(s State) pb.NetMode {
return NetModeSandbox
}

func security(v pb.SecurityMode) StateOption {
return func(s State) State {
return s.WithValue(keySecurity, v)
}
}
func getSecurity(s State) pb.SecurityMode {
v := s.Value(keySecurity)
if v != nil {
n := v.(pb.SecurityMode)
return n
}
return SecurityModeSandbox
}

type EnvList []KeyValue

type KeyValue struct {
Expand Down
8 changes: 8 additions & 0 deletions client/llb/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func (s State) Run(ro ...RunOption) ExecState {
ProxyEnv: ei.ProxyEnv,
ExtraHosts: getExtraHosts(ei.State),
Network: getNetwork(ei.State),
Security: getSecurity(ei.State),
}

exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Constraints)
Expand Down Expand Up @@ -292,6 +293,13 @@ func (s State) Network(n pb.NetMode) State {
func (s State) GetNetwork() pb.NetMode {
return getNetwork(s)
}
func (s State) Security(n pb.SecurityMode) State {
return security(n)(s)
}

func (s State) GetSecurity() pb.SecurityMode {
return getSecurity(s)
}

func (s State) With(so ...StateOption) State {
for _, o := range so {
Expand Down
2 changes: 1 addition & 1 deletion cmd/buildctl/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ var buildCommand = cli.Command{
},
cli.StringSliceFlag{
Name: "allow",
Usage: "Allow extra privileged entitlement, e.g. network.host, security.unconfined",
Usage: "Allow extra privileged entitlement, e.g. network.host, security.insecure",
},
cli.StringSliceFlag{
Name: "ssh",
Expand Down
2 changes: 2 additions & 0 deletions cmd/buildkitd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Config struct {
// Root is the path to a directory where buildkit will store persistent data
Root string `toml:"root"`

//Entitlements e.g. security.insecure, network.host
Entitlements []string `toml:"insecure-entitlements"`
// GRPC configuration settings
GRPC GRPCConfig `toml:"grpc"`

Expand Down
2 changes: 2 additions & 0 deletions cmd/buildkitd/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func TestConfig(t *testing.T) {
const testConfig = `
root = "/foo/bar"
debug=true
insecure-entitlements = ["security.insecure"]

[gc]
enabled=true
Expand Down Expand Up @@ -57,6 +58,7 @@ http=true

require.Equal(t, "/foo/bar", cfg.Root)
require.Equal(t, true, cfg.Debug)
require.Equal(t, "security.insecure", cfg.Entitlements[0])

require.Equal(t, "buildkit.sock", cfg.GRPC.Address[0])
require.Equal(t, "debug.sock", cfg.GRPC.DebugAddress)
Expand Down
24 changes: 24 additions & 0 deletions cmd/buildkitd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ func main() {
Usage: "ca certificate to verify clients",
Value: defaultConf.GRPC.TLS.CA,
},
cli.StringSliceFlag{
Name: "allow-insecure-entitlement",
Usage: "allows insecure entitlements e.g. network.host, security.insecure",
},
)
app.Flags = append(app.Flags, appFlags...)

Expand Down Expand Up @@ -216,6 +220,20 @@ func main() {

controller.Register(server)

ents := c.GlobalStringSlice("allow-insecure-entitlement")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this to config and override with the flag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(ents) > 0 {
cfg.Entitlements = []string{}
for _, e := range ents {
switch e {
case "security.insecure":
cfg.Entitlements = append(cfg.Entitlements, e)
case "network.host":
cfg.Entitlements = append(cfg.Entitlements, e)
default:
return fmt.Errorf("invalid entitlement : %v", e)
}
}
}
errCh := make(chan error, 1)
if err := serveGRPC(cfg.GRPC, server, errCh); err != nil {
return err
Expand Down Expand Up @@ -353,6 +371,11 @@ func applyMainFlags(c *cli.Context, cfg *config.Config, md *toml.MetaData) error
}
}

if c.IsSet("allow-insecure-entitlement") {
//override values from config
cfg.Entitlements = c.StringSlice("allow-insecure-entitlement")
}

if c.IsSet("debugaddr") {
cfg.GRPC.DebugAddress = c.String("debugaddr")
}
Expand Down Expand Up @@ -535,6 +558,7 @@ func newController(c *cli.Context, cfg *config.Config) (*control.Controller, err
ResolveCacheExporterFuncs: remoteCacheExporterFuncs,
ResolveCacheImporterFuncs: remoteCacheImporterFuncs,
CacheKeyStorage: cacheStorage,
Entitlements: cfg.Entitlements,
})
}

Expand Down
3 changes: 2 additions & 1 deletion control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Opt struct {
CacheKeyStorage solver.CacheKeyStorage
ResolveCacheExporterFuncs map[string]remotecache.ResolveCacheExporterFunc
ResolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
Entitlements []string
}

type Controller struct { // TODO: ControlService
Expand All @@ -48,7 +49,7 @@ func NewController(opt Opt) (*Controller, error) {

gatewayForwarder := controlgateway.NewGatewayForwarder()

solver, err := llbsolver.New(opt.WorkerController, opt.Frontends, cache, opt.ResolveCacheImporterFuncs, gatewayForwarder, opt.SessionManager)
solver, err := llbsolver.New(opt.WorkerController, opt.Frontends, cache, opt.ResolveCacheImporterFuncs, gatewayForwarder, opt.SessionManager, opt.Entitlements)
if err != nil {
return nil, errors.Wrap(err, "failed to create solver")
}
Expand Down
6 changes: 1 addition & 5 deletions executor/containerdexecutor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/contrib/seccomp"
containerdoci "github.com/containerd/containerd/oci"
"github.com/moby/buildkit/cache"
"github.com/moby/buildkit/executor"
Expand All @@ -20,7 +19,6 @@ import (
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/network"
"github.com/moby/buildkit/util/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -107,9 +105,7 @@ func (w containerdExecutor) Exec(ctx context.Context, meta executor.Meta, root c
if meta.ReadonlyRootFS {
opts = append(opts, containerdoci.WithRootFSReadonly())
}
if system.SeccompSupported() {
opts = append(opts, seccomp.WithDefaultProfile())
}

if w.cgroupParent != "" {
var cgroupsPath string
lastSeparator := w.cgroupParent[len(w.cgroupParent)-1:]
Expand Down
1 change: 1 addition & 0 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Meta struct {
ReadonlyRootFS bool
ExtraHosts []HostIP
NetMode pb.NetMode
SecurityMode pb.SecurityMode
}

type Mount struct {
Expand Down
Loading