From d817d123d9ddde701c23cd690f96b5c84f0f3711 Mon Sep 17 00:00:00 2001 From: Seth Hoenig Date: Thu, 13 May 2021 14:21:52 -0600 Subject: [PATCH] drivers/exec: enable setting allow_caps on exec driver This PR enables setting allow_caps on the exec driver plugin configuration, as well as cap_add and cap_drop in exec task configuration. These options replicate the functionality already present in the docker task driver. Important: this change also reduces the default set of capabilities enabled by the exec driver to match the default set enabled by the docker driver. Until v1.0.5 the exec task driver would enable all capabilities supported by the operating system. v1.0.5 removed NET_RAW from that list of default capabilities, but left may others which could potentially also be leveraged by compromised tasks. Important: the "root" user is still special cased when used with the exec driver. Older versions of Nomad enabled enabled all capabilities supported by the operating system for tasks set with the root user. To maintain compatibility with existing clusters we continue supporting this "feature", however we maintain support for the legacy set of capabilities rather than enabling all capabilities now supported on modern operating systems. --- drivers/docker/config.go | 13 +- drivers/exec/driver.go | 73 +++++++- drivers/exec/driver_test.go | 127 ++++++++++---- drivers/exec/driver_unix_test.go | 127 ++++++++++++++ drivers/shared/capabilities/defaults.go | 114 ++++++++++++ drivers/shared/capabilities/defaults_test.go | 23 +++ drivers/shared/capabilities/set.go | 110 ++++++++++++ drivers/shared/capabilities/set_test.go | 162 ++++++++++++++++++ drivers/shared/executor/executor.go | 3 + drivers/shared/executor/executor_linux.go | 34 ++-- .../shared/executor/executor_linux_test.go | 11 +- drivers/shared/executor/proto/executor.pb.go | 140 ++++++++------- drivers/shared/executor/proto/executor.proto | 1 + 13 files changed, 806 insertions(+), 132 deletions(-) create mode 100644 drivers/shared/capabilities/defaults.go create mode 100644 drivers/shared/capabilities/defaults_test.go create mode 100644 drivers/shared/capabilities/set.go create mode 100644 drivers/shared/capabilities/set_test.go diff --git a/drivers/docker/config.go b/drivers/docker/config.go index ec919360d41..898d9172c87 100644 --- a/drivers/docker/config.go +++ b/drivers/docker/config.go @@ -10,6 +10,7 @@ import ( docker "github.com/fsouza/go-dockerclient" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/nomad/drivers/shared/capabilities" "github.com/hashicorp/nomad/helper/pluginutils/hclutils" "github.com/hashicorp/nomad/helper/pluginutils/loader" "github.com/hashicorp/nomad/plugins/base" @@ -287,7 +288,7 @@ var ( "allow_privileged": hclspec.NewAttr("allow_privileged", "bool", false), "allow_caps": hclspec.NewDefault( hclspec.NewAttr("allow_caps", "list(string)", false), - hclspec.NewLiteral(`["CHOWN","DAC_OVERRIDE","FSETID","FOWNER","MKNOD","SETGID","SETUID","SETFCAP","SETPCAP","NET_BIND_SERVICE","SYS_CHROOT","KILL","AUDIT_WRITE"]`), + hclspec.NewLiteral(capabilities.HCLSpecLiteral), ), "nvidia_runtime": hclspec.NewDefault( hclspec.NewAttr("nvidia_runtime", "string", false), @@ -427,9 +428,9 @@ var ( "work_dir": hclspec.NewAttr("work_dir", "string", false), }) - // capabilities is returned by the Capabilities RPC and indicates what - // optional features this driver supports - capabilities = &drivers.Capabilities{ + // driverCapabilities represents the RPC response for what features are + // implemented by the docker task driver + driverCapabilities = &drivers.Capabilities{ SendSignals: true, Exec: true, FSIsolation: drivers.FSIsolationImage, @@ -788,8 +789,10 @@ func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) { return taskConfigSpec, nil } +// Capabilities is returned by the Capabilities RPC and indicates what optional +// features this driver supports. func (d *Driver) Capabilities() (*drivers.Capabilities, error) { - return capabilities, nil + return driverCapabilities, nil } var _ drivers.InternalCapabilitiesDriver = (*Driver)(nil) diff --git a/drivers/exec/driver.go b/drivers/exec/driver.go index b7cfb0058db..4357965083c 100644 --- a/drivers/exec/driver.go +++ b/drivers/exec/driver.go @@ -10,6 +10,7 @@ import ( "time" "github.com/hashicorp/nomad/client/lib/cgutil" + "github.com/hashicorp/nomad/drivers/shared/capabilities" "github.com/hashicorp/consul-template/signals" hclog "github.com/hashicorp/go-hclog" @@ -74,6 +75,10 @@ var ( hclspec.NewAttr("default_ipc_mode", "string", false), hclspec.NewLiteral(`"private"`), ), + "allow_caps": hclspec.NewDefault( + hclspec.NewAttr("allow_caps", "list(string)", false), + hclspec.NewLiteral(capabilities.HCLSpecLiteral), + ), }) // taskConfigSpec is the hcl specification for the driver config section of @@ -83,11 +88,13 @@ var ( "args": hclspec.NewAttr("args", "list(string)", false), "pid_mode": hclspec.NewAttr("pid_mode", "string", false), "ipc_mode": hclspec.NewAttr("ipc_mode", "string", false), + "cap_add": hclspec.NewAttr("cap_add", "list(string)", false), + "cap_drop": hclspec.NewAttr("cap_drop", "list(string)", false), }) - // capabilities is returned by the Capabilities RPC and indicates what - // optional features this driver supports - capabilities = &drivers.Capabilities{ + // driverCapabilities represents the RPC response for what features are + // implemented by the exec task driver + driverCapabilities = &drivers.Capabilities{ SendSignals: true, Exec: true, FSIsolation: drivers.FSIsolationChroot, @@ -141,6 +148,10 @@ type Config struct { // DefaultModeIPC is the default IPC isolation set for all tasks using // exec-based task drivers. DefaultModeIPC string `codec:"default_ipc_mode"` + + // AllowCaps configures which Linux Capabilities are enabled for tasks + // running on this node. + AllowCaps []string `codec:"allow_caps"` } func (c *Config) validate() error { @@ -156,6 +167,11 @@ func (c *Config) validate() error { return fmt.Errorf("default_ipc_mode must be %q or %q, got %q", executor.IsolationModePrivate, executor.IsolationModeHost, c.DefaultModeIPC) } + badCaps := capabilities.Supported().Difference(capabilities.New(c.AllowCaps)) + if !badCaps.Empty() { + return fmt.Errorf("allow_caps configured with capabilities not supported by system: %s", badCaps) + } + return nil } @@ -174,6 +190,12 @@ type TaskConfig struct { // ModeIPC indicates whether IPC namespace isolation is enabled for the task. // Must be "private" or "host" if set. ModeIPC string `codec:"ipc_mode"` + + // CapAdd is a set of linux capabilities to enable. + CapAdd []string `codec:"cap_add"` + + // CapDrop is a set of linux capabilities to disable. + CapDrop []string `codec:"cap_drop"` } func (tc *TaskConfig) validate() error { @@ -189,6 +211,16 @@ func (tc *TaskConfig) validate() error { return fmt.Errorf("ipc_mode must be %q or %q, got %q", executor.IsolationModePrivate, executor.IsolationModeHost, tc.ModeIPC) } + supported := capabilities.Supported() + badAdds := supported.Difference(capabilities.New(tc.CapAdd)) + if !badAdds.Empty() { + return fmt.Errorf("cap_add configured with capabilities not supported by system: %s", badAdds) + } + badDrops := supported.Difference(capabilities.New(tc.CapDrop)) + if !badDrops.Empty() { + return fmt.Errorf("cap_drop configured with capabilities not supported by system: %s", badDrops) + } + return nil } @@ -266,8 +298,33 @@ func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) { return taskConfigSpec, nil } +// getCaps computes the complete set of linux capabilities to enable for driver, +// which gets passed along to libcontainer. +func (d *Driver) getCaps(tc *TaskConfig) ([]string, error) { + driverAllowed := capabilities.New(d.config.AllowCaps) + + // determine caps the task wants that are not allowed + taskCaps := capabilities.New(tc.CapAdd) + missing := driverAllowed.Difference(taskCaps) + if !missing.Empty() { + return nil, fmt.Errorf("driver does not allow the following capabilities: %s", missing) + } + + // if task did not specify allowed caps, use nomad defaults minus task drops + if len(tc.CapAdd) == 0 { + driverAllowed.Remove(tc.CapDrop) + return driverAllowed.Slice(true), nil + } + + // otherwise task did specify allowed caps, enable exactly those + taskAdd := capabilities.New(tc.CapAdd) + return taskAdd.Slice(true), nil +} + +// Capabilities is returned by the Capabilities RPC and indicates what +// optional features this driver supports func (d *Driver) Capabilities() (*drivers.Capabilities, error) { - return capabilities, nil + return driverCapabilities, nil } func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) { @@ -439,6 +496,11 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive cfg.Mounts = append(cfg.Mounts, dnsMount) } + caps, err := d.getCaps(&driverConfig) + if err != nil { + return nil, nil, err + } + execCmd := &executor.ExecCommand{ Cmd: driverConfig.Command, Args: driverConfig.Args, @@ -455,6 +517,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive NetworkIsolation: cfg.NetworkIsolation, ModePID: executor.IsolationMode(d.config.DefaultModePID, driverConfig.ModePID), ModeIPC: executor.IsolationMode(d.config.DefaultModeIPC, driverConfig.ModeIPC), + Capabilities: caps, } ps, err := exec.Launch(execCmd) @@ -482,7 +545,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive if err := handle.SetDriverState(&driverState); err != nil { d.logger.Error("failed to start task, error setting driver state", "error", err) - exec.Shutdown("", 0) + _ = exec.Shutdown("", 0) pluginClient.Kill() return nil, nil, fmt.Errorf("failed to set driver state: %v", err) } diff --git a/drivers/exec/driver_test.go b/drivers/exec/driver_test.go index be5dda5cf4c..8651eda20d2 100644 --- a/drivers/exec/driver_test.go +++ b/drivers/exec/driver_test.go @@ -764,42 +764,99 @@ func TestExecDriver_NoPivotRoot(t *testing.T) { } func TestDriver_Config_validate(t *testing.T) { - for _, tc := range []struct { - pidMode, ipcMode string - exp error - }{ - {pidMode: "host", ipcMode: "host", exp: nil}, - {pidMode: "private", ipcMode: "host", exp: nil}, - {pidMode: "host", ipcMode: "private", exp: nil}, - {pidMode: "private", ipcMode: "private", exp: nil}, - {pidMode: "other", ipcMode: "private", exp: errors.New(`default_pid_mode must be "private" or "host", got "other"`)}, - {pidMode: "private", ipcMode: "other", exp: errors.New(`default_ipc_mode must be "private" or "host", got "other"`)}, - } { - require.Equal(t, tc.exp, (&Config{ - DefaultModePID: tc.pidMode, - DefaultModeIPC: tc.ipcMode, - }).validate()) - } + t.Run("pid/ipc", func(t *testing.T) { + for _, tc := range []struct { + pidMode, ipcMode string + exp error + }{ + {pidMode: "host", ipcMode: "host", exp: nil}, + {pidMode: "private", ipcMode: "host", exp: nil}, + {pidMode: "host", ipcMode: "private", exp: nil}, + {pidMode: "private", ipcMode: "private", exp: nil}, + {pidMode: "other", ipcMode: "private", exp: errors.New(`default_pid_mode must be "private" or "host", got "other"`)}, + {pidMode: "private", ipcMode: "other", exp: errors.New(`default_ipc_mode must be "private" or "host", got "other"`)}, + } { + require.Equal(t, tc.exp, (&Config{ + DefaultModePID: tc.pidMode, + DefaultModeIPC: tc.ipcMode, + }).validate()) + } + }) + + t.Run("allow_caps", func(t *testing.T) { + for _, tc := range []struct { + ac []string + exp error + }{ + {ac: []string{}, exp: nil}, + {ac: []string{"all"}, exp: nil}, + {ac: []string{"chown", "sys_time"}, exp: nil}, + {ac: []string{"CAP_CHOWN", "cap_sys_time"}, exp: nil}, + {ac: []string{"chown", "not_valid", "sys_time"}, exp: errors.New("allow_caps configured with capabilities not supported by system: not_valid")}, + } { + require.Equal(t, tc.exp, (&Config{ + DefaultModePID: "private", + DefaultModeIPC: "private", + AllowCaps: tc.ac, + }).validate()) + } + }) } func TestDriver_TaskConfig_validate(t *testing.T) { - for _, tc := range []struct { - pidMode, ipcMode string - exp error - }{ - {pidMode: "host", ipcMode: "host", exp: nil}, - {pidMode: "host", ipcMode: "private", exp: nil}, - {pidMode: "host", ipcMode: "", exp: nil}, - {pidMode: "host", ipcMode: "other", exp: errors.New(`ipc_mode must be "private" or "host", got "other"`)}, - - {pidMode: "host", ipcMode: "host", exp: nil}, - {pidMode: "private", ipcMode: "host", exp: nil}, - {pidMode: "", ipcMode: "host", exp: nil}, - {pidMode: "other", ipcMode: "host", exp: errors.New(`pid_mode must be "private" or "host", got "other"`)}, - } { - require.Equal(t, tc.exp, (&TaskConfig{ - ModePID: tc.pidMode, - ModeIPC: tc.ipcMode, - }).validate()) - } + t.Run("pid/ipc", func(t *testing.T) { + for _, tc := range []struct { + pidMode, ipcMode string + exp error + }{ + {pidMode: "host", ipcMode: "host", exp: nil}, + {pidMode: "host", ipcMode: "private", exp: nil}, + {pidMode: "host", ipcMode: "", exp: nil}, + {pidMode: "host", ipcMode: "other", exp: errors.New(`ipc_mode must be "private" or "host", got "other"`)}, + + {pidMode: "host", ipcMode: "host", exp: nil}, + {pidMode: "private", ipcMode: "host", exp: nil}, + {pidMode: "", ipcMode: "host", exp: nil}, + {pidMode: "other", ipcMode: "host", exp: errors.New(`pid_mode must be "private" or "host", got "other"`)}, + } { + require.Equal(t, tc.exp, (&TaskConfig{ + ModePID: tc.pidMode, + ModeIPC: tc.ipcMode, + }).validate()) + } + }) + + t.Run("cap_add", func(t *testing.T) { + for _, tc := range []struct { + adds []string + exp error + }{ + {adds: nil, exp: nil}, + {adds: []string{"chown"}, exp: nil}, + {adds: []string{"CAP_CHOWN"}, exp: nil}, + {adds: []string{"chown", "sys_time"}, exp: nil}, + {adds: []string{"chown", "not_valid", "sys_time"}, exp: errors.New("cap_add configured with capabilities not supported by system: not_valid")}, + } { + require.Equal(t, tc.exp, (&TaskConfig{ + CapAdd: tc.adds, + }).validate()) + } + }) + + t.Run("cap_drop", func(t *testing.T) { + for _, tc := range []struct { + drops []string + exp error + }{ + {drops: nil, exp: nil}, + {drops: []string{"chown"}, exp: nil}, + {drops: []string{"CAP_CHOWN"}, exp: nil}, + {drops: []string{"chown", "sys_time"}, exp: nil}, + {drops: []string{"chown", "not_valid", "sys_time"}, exp: errors.New("cap_drop configured with capabilities not supported by system: not_valid")}, + } { + require.Equal(t, tc.exp, (&TaskConfig{ + CapDrop: tc.drops, + }).validate()) + } + }) } diff --git a/drivers/exec/driver_unix_test.go b/drivers/exec/driver_unix_test.go index f8d77a2d7a1..3b2e60374aa 100644 --- a/drivers/exec/driver_unix_test.go +++ b/drivers/exec/driver_unix_test.go @@ -5,9 +5,13 @@ package exec import ( "context" "fmt" + "strings" "testing" "time" + "github.com/hashicorp/nomad/drivers/shared/capabilities" + "github.com/hashicorp/nomad/drivers/shared/executor" + basePlug "github.com/hashicorp/nomad/plugins/base" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" @@ -173,5 +177,128 @@ func TestExec_dnsConfig(t *testing.T) { dtestutil.TestTaskDNSConfig(t, harness, task.ID, c.cfg) } +} + +func TestExecDriver_Capabilities(t *testing.T) { + ctestutils.ExecCompatible(t) + + task := &drivers.TaskConfig{ + ID: uuid.Generate(), + Name: "sleep", + } + for _, tc := range []struct { + Name string + CapAdd []string + CapDrop []string + AllowList string + StartError string + }{ + { + Name: "default-allowlist-add-allowed", + CapAdd: []string{"fowner", "mknod"}, + CapDrop: []string{"ALL"}, + }, + { + Name: "default-allowlist-add-forbidden", + CapAdd: []string{"net_admin"}, + StartError: "net_admin", + }, + { + Name: "default-allowlist-drop-existing", + CapDrop: []string{"FOWNER", "MKNOD", "NET_RAW"}, + }, + { + Name: "restrictive-allowlist-drop-all", + CapDrop: []string{"ALL"}, + AllowList: "FOWNER,MKNOD", + }, + { + Name: "restrictive-allowlist-add-allowed", + CapAdd: []string{"fowner", "mknod"}, + CapDrop: []string{"ALL"}, + AllowList: "fowner,mknod", + }, + { + Name: "restrictive-allowlist-add-forbidden", + CapAdd: []string{"net_admin", "mknod"}, + CapDrop: []string{"ALL"}, + AllowList: "fowner,mknod", + StartError: "net_admin", + }, + { + Name: "restrictive-allowlist-add-multiple-forbidden", + CapAdd: []string{"net_admin", "mknod", "CAP_SYS_TIME"}, + CapDrop: []string{"ALL"}, + AllowList: "fowner,mknod", + StartError: "net_admin, sys_time", + }, + { + Name: "permissive-allowlist", + CapAdd: []string{"net_admin", "mknod"}, + AllowList: "ALL", + }, + { + Name: "permissive-allowlist-add-all", + CapAdd: []string{"all"}, + AllowList: "ALL", + }, + } { + t.Run(tc.Name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + d := NewExecDriver(ctx, testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) + defer harness.Kill() + + config := &Config{ + NoPivotRoot: true, + DefaultModePID: executor.IsolationModePrivate, + DefaultModeIPC: executor.IsolationModePrivate, + } + + if tc.AllowList != "" { + config.AllowCaps = strings.Split(tc.AllowList, ",") + } else { + // inherit HCL defaults if not set + config.AllowCaps = capabilities.NomadDefaults().Slice(true) + } + + var data []byte + require.NoError(t, basePlug.MsgPackEncode(&data, config)) + baseConfig := &basePlug.Config{PluginConfig: data} + require.NoError(t, harness.SetConfig(baseConfig)) + + cleanup := harness.MkAllocDir(task, false) + defer cleanup() + + tCfg := &TaskConfig{ + Command: "/bin/sleep", + Args: []string{"9000"}, + } + if len(tc.CapAdd) > 0 { + tCfg.CapAdd = tc.CapAdd + } + if len(tc.CapDrop) > 0 { + tCfg.CapDrop = tc.CapDrop + } + require.NoError(t, task.EncodeConcreteDriverConfig(&tCfg)) + + // check the start error against expectations + _, _, err := harness.StartTask(task) + if err == nil && tc.StartError != "" { + t.Fatalf("Expected error in start: %v", tc.StartError) + } else if err != nil { + if tc.StartError == "" { + require.NoError(t, err) + } else { + require.Contains(t, err.Error(), tc.StartError) + } + return + } + + _ = d.DestroyTask(task.ID, true) + }) + } } diff --git a/drivers/shared/capabilities/defaults.go b/drivers/shared/capabilities/defaults.go new file mode 100644 index 00000000000..a1a6e3531e0 --- /dev/null +++ b/drivers/shared/capabilities/defaults.go @@ -0,0 +1,114 @@ +package capabilities + +import ( + "regexp" + + "github.com/syndtr/gocapability/capability" +) + +const ( + // HCLSpecLiteral is an equivalent list to NomadDefaults, expressed as a literal + // HCL string for use in HCL config parsing. + HCLSpecLiteral = `["AUDIT_WRITE","CHOWN","DAC_OVERRIDE","FOWNER","FSETID","KILL","MKNOD","NET_BIND_SERVICE","SETFCAP","SETGID","SETPCAP","SETUID","SYS_CHROOT"]` +) + +var ( + extractLiteral = regexp.MustCompile(`("[\w]+)`) +) + +// NomadDefaults is the set of Linux capabilities that Nomad enables by +// default. This list originates from what Docker enabled by default, but then +// excludes NET_RAW for security reasons. +// +// This set is use in the as HCL configuration default, described by HCLSpecLiteral. +func NomadDefaults() *Set { + return New(extractLiteral.FindAllString(HCLSpecLiteral, -1)) +} + +// DockerDefaults is a list of Linux capabilities enabled by Docker by default +// and is used to compute the set of capabilities to add/drop given docker driver +// configuration. +// +// https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities +func DockerDefaults() *Set { + defaults := NomadDefaults() + defaults.Add("NET_RAW") + return defaults +} + +// Supported returns the set of capabilities supported by the operating system. +// +// Defers to a library generated from +// https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h +func Supported() *Set { + s := New(nil) + + last := capability.CAP_LAST_CAP + + // workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap + if last == capability.Cap(63) { + last = capability.CAP_BLOCK_SUSPEND + } + + // accumulate every capability supported by this system + for _, c := range capability.List() { + if c > last { + continue + } + s.Add(c.String()) + } + + return s +} + +// LegacySupported returns the historical set of capabilities used when a task is +// configured to run as root using the exec task driver. Older versions of Nomad +// always allowed the root user to make use of any capability. Now that the exec +// task driver supports configuring the allowed capabilities, operators are +// encouraged to explicitly opt-in to capabilities beyond this legacy set. We +// maintain the legacy list here, because previous versions of Nomad deferred to +// the capability.List library function, which adds new capabilities over time. +// +// https://github.com/hashicorp/nomad/blob/v1.0.4/vendor/github.com/syndtr/gocapability/capability/enum_gen.go#L88 +func LegacySupported() *Set { + return New([]string{ + "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", + }) +} diff --git a/drivers/shared/capabilities/defaults_test.go b/drivers/shared/capabilities/defaults_test.go new file mode 100644 index 00000000000..2a07afa0ea2 --- /dev/null +++ b/drivers/shared/capabilities/defaults_test.go @@ -0,0 +1,23 @@ +package capabilities + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSet_NomadDefaults(t *testing.T) { + result := NomadDefaults() + require.Len(t, result.Slice(false), 13) + defaults := strings.ToLower(HCLSpecLiteral) + for _, c := range result.Slice(false) { + require.Contains(t, defaults, c) + } +} + +func TestSet_DockerDefaults(t *testing.T) { + result := DockerDefaults() + require.Len(t, result.Slice(false), 14) + require.Contains(t, result.String(), "net_raw") +} diff --git a/drivers/shared/capabilities/set.go b/drivers/shared/capabilities/set.go new file mode 100644 index 00000000000..046573e2a40 --- /dev/null +++ b/drivers/shared/capabilities/set.go @@ -0,0 +1,110 @@ +// Package capabilities is used for managing sets of linux capabilities. +package capabilities + +import ( + "sort" + "strings" +) + +type nothing struct{} + +var null = nothing{} + +// Set represents a group linux capabilities, implementing some useful set +// operations, taking care of name normalization, and sentinel value expansions. +// +// Linux capabilities can be expressed in multiple ways when working with docker +// and/or libcontainer, along with Nomad. +// +// Capability names may be upper or lower case, and may or may not be prefixed +// with "CAP_" or "cap_". On top of that, Nomad interprets the special name "all" +// and "ALL" to mean "all capabilities supported by the operating system". +type Set struct { + data map[string]nothing +} + +func New(caps []string) *Set { + m := make(map[string]nothing, len(caps)) + for _, c := range caps { + insert(m, c) + } + return &Set{data: m} +} + +// Add cap into s. +func (s *Set) Add(cap string) { + insert(s.data, cap) +} + +func insert(data map[string]nothing, cap string) { + switch name := normalize(cap); name { + case "": + case "all": + for k, v := range Supported().data { + data[k] = v + } + return + default: + data[name] = null + } +} + +// Remove caps from s. +func (s *Set) Remove(caps []string) { + for _, c := range caps { + name := normalize(c) + if name == "all" { + s.data = make(map[string]nothing) + return + } + delete(s.data, name) + } +} + +// Difference returns the Set of elements of b not in s. +func (s *Set) Difference(b *Set) *Set { + data := make(map[string]nothing) + for c := range b.data { + if _, exists := s.data[c]; !exists { + data[c] = null + } + } + return &Set{data: data} +} + +// Empty return true if no capabilities exist in s. +func (s *Set) Empty() bool { + return len(s.data) == 0 +} + +// String returns the normalized and sorted string representation of s. +func (s *Set) String() string { + return strings.Join(s.Slice(false), ", ") +} + +// Slice returns a sorted slice of capabilities in s. +// +// big - indicates whether to uppercase and prefix capabilities with CAP_ +func (s *Set) Slice(upper bool) []string { + caps := make([]string, 0, len(s.data)) + for c := range s.data { + if upper { + c = "CAP_" + strings.ToUpper(c) + } + caps = append(caps, c) + } + sort.Strings(caps) + return caps +} + +// linux capabilities are often named in 4 possible ways - upper or lower case, +// and with or without a CAP_ prefix +// +// since we must do comparisons on cap names, always normalize the names before +// letting them into the Set data-structure +func normalize(name string) string { + spaces := strings.TrimSpace(name) + lower := strings.ToLower(spaces) + trim := strings.TrimPrefix(lower, "cap_") + return trim +} diff --git a/drivers/shared/capabilities/set_test.go b/drivers/shared/capabilities/set_test.go new file mode 100644 index 00000000000..8e072e8b0f2 --- /dev/null +++ b/drivers/shared/capabilities/set_test.go @@ -0,0 +1,162 @@ +package capabilities + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSet_Empty(t *testing.T) { + t.Parallel() + + t.Run("nil", func(t *testing.T) { + result := New(nil).Empty() + require.True(t, result) + }) + + t.Run("empty", func(t *testing.T) { + result := New([]string{}).Empty() + require.True(t, result) + }) + + t.Run("full", func(t *testing.T) { + result := New([]string{"chown", "sys_time"}).Empty() + require.False(t, result) + }) +} + +func TestSet_New(t *testing.T) { + t.Parallel() + + t.Run("duplicates", func(t *testing.T) { + result := New([]string{"chown", "sys_time", "chown"}) + require.Equal(t, "chown, sys_time", result.String()) + }) + + t.Run("empty string", func(t *testing.T) { + result := New([]string{""}) + require.True(t, result.Empty()) + }) + + t.Run("all", func(t *testing.T) { + result := New([]string{"all"}) + exp := len(Supported().Slice(false)) + require.Len(t, result.Slice(false), exp) + }) +} + +func TestSet_Slice(t *testing.T) { + t.Parallel() + + exp := []string{"chown", "net_raw", "sys_time"} + + t.Run("lower case", func(t *testing.T) { + s := New([]string{"net_raw", "chown", "sys_time"}) + require.Equal(t, exp, s.Slice(false)) + }) + + t.Run("upper case", func(t *testing.T) { + s := New([]string{"NET_RAW", "CHOWN", "SYS_TIME"}) + require.Equal(t, exp, s.Slice(false)) + }) + + t.Run("prefix", func(t *testing.T) { + s := New([]string{"CAP_net_raw", "sys_TIME", "cap_chown"}) + require.Equal(t, exp, s.Slice(false)) + }) +} + +func TestSet_String(t *testing.T) { + t.Parallel() + + t.Run("empty", func(t *testing.T) { + result := New(nil).String() + require.Equal(t, "", result) + }) + + t.Run("full", func(t *testing.T) { + exp := "chown, net_raw, sys_time" + in := []string{"net_raw", "CAP_CHOWN", "cap_sys_time"} + result := New(in).String() + require.Equal(t, exp, result) + }) +} + +func TestSet_Add(t *testing.T) { + t.Parallel() + + t.Run("add one", func(t *testing.T) { + s := New([]string{"chown", "net_raw"}) + require.Equal(t, "chown, net_raw", s.String()) + + s.Add("CAP_SYS_TIME") + require.Equal(t, "chown, net_raw, sys_time", s.String()) + + s.Add("AF_NET") + require.Equal(t, "af_net, chown, net_raw, sys_time", s.String()) + }) + + t.Run("add empty string", func(t *testing.T) { + s := New([]string{"chown"}) + s.Add("") + require.Equal(t, "chown", s.String()) + }) + + t.Run("add all", func(t *testing.T) { + s := New([]string{"chown", "net_raw"}) + require.Equal(t, "chown, net_raw", s.String()) + + exp := len(Supported().Slice(false)) + s.Add("all") + require.Len(t, s.Slice(false), exp) + }) + +} + +func TestSet_Remove(t *testing.T) { + t.Parallel() + + t.Run("remove one", func(t *testing.T) { + s := New([]string{"af_net", "chown", "net_raw", "seteuid", "sys_time"}) + s.Remove([]string{"CAP_NET_RAW"}) + require.Equal(t, "af_net, chown, seteuid, sys_time", s.String()) + }) + + t.Run("remove couple", func(t *testing.T) { + s := New([]string{"af_net", "chown", "net_raw", "seteuid", "sys_time"}) + s.Remove([]string{"CAP_NET_RAW", "af_net"}) + require.Equal(t, "chown, seteuid, sys_time", s.String()) + }) + + t.Run("remove all", func(t *testing.T) { + s := New([]string{"af_net", "chown", "net_raw", "seteuid", "sys_time"}) + s.Remove([]string{"all"}) + require.True(t, s.Empty()) + require.Equal(t, "", s.String()) + }) +} + +func TestSet_Difference(t *testing.T) { + t.Parallel() + + t.Run("a is empty", func(t *testing.T) { + a := New(nil) + b := New([]string{"chown", "af_net"}) + result := a.Difference(b) + require.Equal(t, "af_net, chown", result.String()) + }) + + t.Run("b is empty", func(t *testing.T) { + a := New([]string{"chown", "af_net"}) + b := New(nil) + result := a.Difference(b) + require.True(t, result.Empty()) + }) + + t.Run("a diff b", func(t *testing.T) { + a := New([]string{"A", "b", "C", "d", "e", "f"}) + b := New([]string{"B", "x", "Y", "a"}) + result := a.Difference(b) + require.Equal(t, "x, y", result.String()) + }) +} diff --git a/drivers/shared/executor/executor.go b/drivers/shared/executor/executor.go index a87674d2ae5..7d738e45f63 100644 --- a/drivers/shared/executor/executor.go +++ b/drivers/shared/executor/executor.go @@ -147,6 +147,9 @@ type ExecCommand struct { // ModeIPC is the IPC isolation mode (private or host). ModeIPC string + + // Capabilities are the linux capabilities to be enabled by the task driver. + Capabilities []string } // SetWriters sets the writer for the process stdout and stderr. This should diff --git a/drivers/shared/executor/executor_linux.go b/drivers/shared/executor/executor_linux.go index 16eb27b4c0f..1bbb2839cbb 100644 --- a/drivers/shared/executor/executor_linux.go +++ b/drivers/shared/executor/executor_linux.go @@ -14,6 +14,7 @@ import ( "syscall" "time" + "github.com/hashicorp/nomad/drivers/shared/capabilities" "github.com/opencontainers/runtime-spec/specs-go" "github.com/armon/circbuf" @@ -532,28 +533,26 @@ func (l *LibcontainerExecutor) handleExecWait(ch chan *waitResult, process *libc ch <- &waitResult{ps, err} } -func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) error { - // TODO(shoenig): allow better control of these - // use capabilities list as prior to adopting libcontainer in 0.9 +func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) { - // match capabilities used in Nomad 0.8 - if command.User == "root" { - allCaps := SupportedCaps(true) + switch command.User { + case "root": + // when running as root, use the legacy set of system capabilities, so + // that we do not break existing nomad clusters using this "feature" + legacyCaps := capabilities.LegacySupported().Slice(true) cfg.Capabilities = &lconfigs.Capabilities{ - Bounding: allCaps, - Permitted: allCaps, - Effective: allCaps, + Bounding: legacyCaps, + Permitted: legacyCaps, + Effective: legacyCaps, Ambient: nil, Inheritable: nil, } - } else { - allCaps := SupportedCaps(false) + default: + // otherwise apply the plugin + task capability configuration cfg.Capabilities = &lconfigs.Capabilities{ - Bounding: allCaps, + Bounding: command.Capabilities, } } - - return nil } func configureNamespaces(pidMode, ipcMode string) lconfigs.Namespaces { @@ -759,16 +758,17 @@ func newLibcontainerConfig(command *ExecCommand) (*lconfigs.Config, error) { }, Version: "1.0.0", } + for _, device := range specconv.AllowedDevices { cfg.Cgroups.Resources.Devices = append(cfg.Cgroups.Resources.Devices, &device.Rule) } - if err := configureCapabilities(cfg, command); err != nil { - return nil, err - } + configureCapabilities(cfg, command) + if err := configureIsolation(cfg, command); err != nil { return nil, err } + if err := configureCgroups(cfg, command); err != nil { return nil, err } diff --git a/drivers/shared/executor/executor_linux_test.go b/drivers/shared/executor/executor_linux_test.go index a8bfba0a4e4..b3576797ad4 100644 --- a/drivers/shared/executor/executor_linux_test.go +++ b/drivers/shared/executor/executor_linux_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/taskenv" "github.com/hashicorp/nomad/client/testutil" + "github.com/hashicorp/nomad/drivers/shared/capabilities" "github.com/hashicorp/nomad/helper/testlog" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/plugins/drivers" @@ -478,7 +479,7 @@ func TestExecutor_Capabilities(t *testing.T) { CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 -CapBnd: 0000003fffffdfff +CapBnd: 00000000a80405fb CapAmb: 0000000000000000`, }, { @@ -494,7 +495,6 @@ CapAmb: 0000000000000000`, for _, c := range cases { t.Run(c.user, func(t *testing.T) { - require := require.New(t) testExecCmd := testExecutorCommandWithChroot(t) execCmd, allocDir := testExecCmd.command, testExecCmd.allocDir @@ -504,12 +504,13 @@ CapAmb: 0000000000000000`, execCmd.ResourceLimits = true execCmd.Cmd = "/bin/bash" execCmd.Args = []string{"-c", "cat /proc/$$/status"} + execCmd.Capabilities = capabilities.NomadDefaults().Slice(true) executor := NewExecutorWithIsolation(testlog.HCLogger(t)) defer executor.Shutdown("SIGKILL", 0) _, err := executor.Launch(execCmd) - require.NoError(err) + require.NoError(t, err) ch := make(chan interface{}) go func() { @@ -521,7 +522,7 @@ CapAmb: 0000000000000000`, case <-ch: // all good case <-time.After(5 * time.Second): - require.Fail("timeout waiting for exec to shutdown") + require.Fail(t, "timeout waiting for exec to shutdown") } canonical := func(s string) string { @@ -538,7 +539,7 @@ CapAmb: 0000000000000000`, return false, fmt.Errorf("capabilities didn't match: want\n%v\n; got:\n%v\n", expected, output) } return true, nil - }, func(err error) { require.NoError(err) }) + }, func(err error) { require.NoError(t, err) }) }) } diff --git a/drivers/shared/executor/proto/executor.pb.go b/drivers/shared/executor/proto/executor.pb.go index f6defb54a30..bf6c980c290 100644 --- a/drivers/shared/executor/proto/executor.pb.go +++ b/drivers/shared/executor/proto/executor.pb.go @@ -44,6 +44,7 @@ type LaunchRequest struct { DefaultPidMode string `protobuf:"bytes,15,opt,name=default_pid_mode,json=defaultPidMode,proto3" json:"default_pid_mode,omitempty"` DefaultIpcMode string `protobuf:"bytes,16,opt,name=default_ipc_mode,json=defaultIpcMode,proto3" json:"default_ipc_mode,omitempty"` CpusetCgroup string `protobuf:"bytes,17,opt,name=cpuset_cgroup,json=cpusetCgroup,proto3" json:"cpuset_cgroup,omitempty"` + AllowCaps []string `protobuf:"bytes,18,rep,name=allow_caps,json=allowCaps,proto3" json:"allow_caps,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -193,6 +194,13 @@ func (m *LaunchRequest) GetCpusetCgroup() string { return "" } +func (m *LaunchRequest) GetAllowCaps() []string { + if m != nil { + return m.AllowCaps + } + return nil +} + type LaunchResponse struct { Process *ProcessState `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -858,71 +866,73 @@ func init() { } var fileDescriptor_66b85426380683f3 = []byte{ - // 1020 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x5b, 0x8f, 0xdb, 0x44, - 0x14, 0xae, 0x37, 0x9b, 0xdb, 0x49, 0xb2, 0x49, 0x47, 0xa8, 0xb8, 0xe1, 0xa1, 0xc1, 0x48, 0x34, - 0x82, 0xe2, 0xac, 0xb6, 0x37, 0x24, 0x24, 0x8a, 0xd8, 0x2d, 0xa8, 0xd2, 0x76, 0x15, 0x39, 0x85, - 0x4a, 0x3c, 0x60, 0x5c, 0xcf, 0x34, 0x19, 0x6d, 0xe2, 0x31, 0x33, 0xe3, 0x74, 0x91, 0x90, 0x78, - 0xe2, 0x1f, 0x80, 0xc4, 0x5f, 0xe5, 0x0d, 0xcd, 0xcd, 0x9b, 0x6c, 0x4b, 0xe5, 0x14, 0xf1, 0x14, - 0xcf, 0xc9, 0xf7, 0x9d, 0xcb, 0x9c, 0x73, 0xbe, 0x81, 0x3b, 0x98, 0xd3, 0x35, 0xe1, 0x62, 0x22, - 0x16, 0x09, 0x27, 0x78, 0x42, 0x2e, 0x48, 0x5a, 0x48, 0xc6, 0x27, 0x39, 0x67, 0x92, 0x95, 0xc7, - 0x50, 0x1f, 0xd1, 0xc7, 0x8b, 0x44, 0x2c, 0x68, 0xca, 0x78, 0x1e, 0x66, 0x6c, 0x95, 0xe0, 0x30, - 0x5f, 0x16, 0x73, 0x9a, 0x89, 0x70, 0x1b, 0x37, 0xbc, 0x35, 0x67, 0x6c, 0xbe, 0x24, 0xc6, 0xc9, - 0x8b, 0xe2, 0xe5, 0x44, 0xd2, 0x15, 0x11, 0x32, 0x59, 0xe5, 0x16, 0x10, 0x58, 0xe2, 0xc4, 0x85, - 0x37, 0xe1, 0xcc, 0xc9, 0x60, 0x82, 0xbf, 0xeb, 0xd0, 0x3b, 0x4d, 0x8a, 0x2c, 0x5d, 0x44, 0xe4, - 0xe7, 0x82, 0x08, 0x89, 0x06, 0x50, 0x4b, 0x57, 0xd8, 0xf7, 0x46, 0xde, 0xb8, 0x1d, 0xa9, 0x4f, - 0x84, 0x60, 0x3f, 0xe1, 0x73, 0xe1, 0xef, 0x8d, 0x6a, 0xe3, 0x76, 0xa4, 0xbf, 0xd1, 0x19, 0xb4, - 0x39, 0x11, 0xac, 0xe0, 0x29, 0x11, 0x7e, 0x6d, 0xe4, 0x8d, 0x3b, 0x47, 0x87, 0xe1, 0xbf, 0x25, - 0x6e, 0xe3, 0x9b, 0x90, 0x61, 0xe4, 0x78, 0xd1, 0xa5, 0x0b, 0x74, 0x0b, 0x3a, 0x42, 0x62, 0x56, - 0xc8, 0x38, 0x4f, 0xe4, 0xc2, 0xdf, 0xd7, 0xd1, 0xc1, 0x98, 0xa6, 0x89, 0x5c, 0x58, 0x00, 0xe1, - 0xdc, 0x00, 0xea, 0x25, 0x80, 0x70, 0xae, 0x01, 0x03, 0xa8, 0x91, 0x6c, 0xed, 0x37, 0x74, 0x92, - 0xea, 0x53, 0xe5, 0x5d, 0x08, 0xc2, 0xfd, 0xa6, 0xc6, 0xea, 0x6f, 0x74, 0x13, 0x5a, 0x32, 0x11, - 0xe7, 0x31, 0xa6, 0xdc, 0x6f, 0x69, 0x7b, 0x53, 0x9d, 0x4f, 0x28, 0x47, 0xb7, 0xa1, 0xef, 0xf2, - 0x89, 0x97, 0x74, 0x45, 0xa5, 0xf0, 0xdb, 0x23, 0x6f, 0xdc, 0x8a, 0x0e, 0x9c, 0xf9, 0x54, 0x5b, - 0xd1, 0x21, 0xbc, 0xf7, 0x22, 0x11, 0x34, 0x8d, 0x73, 0xce, 0x52, 0x22, 0x44, 0x9c, 0xce, 0x39, - 0x2b, 0x72, 0x1f, 0x34, 0x1a, 0xe9, 0xff, 0xa6, 0xe6, 0xaf, 0x63, 0xfd, 0x0f, 0x3a, 0x81, 0xc6, - 0x8a, 0x15, 0x99, 0x14, 0x7e, 0x67, 0x54, 0x1b, 0x77, 0x8e, 0xee, 0x54, 0xbc, 0xaa, 0xa7, 0x8a, - 0x14, 0x59, 0x2e, 0xfa, 0x16, 0x9a, 0x98, 0xac, 0xa9, 0xba, 0xf1, 0xae, 0x76, 0xf3, 0x59, 0x45, - 0x37, 0x27, 0x9a, 0x15, 0x39, 0x36, 0x5a, 0xc0, 0xf5, 0x8c, 0xc8, 0x57, 0x8c, 0x9f, 0xc7, 0x54, - 0xb0, 0x65, 0x22, 0x29, 0xcb, 0xfc, 0x9e, 0x6e, 0xe2, 0x17, 0x15, 0x5d, 0x9e, 0x19, 0xfe, 0x13, - 0x47, 0x9f, 0xe5, 0x24, 0x8d, 0x06, 0xd9, 0x15, 0x2b, 0x0a, 0xa0, 0x97, 0xb1, 0x38, 0xa7, 0x6b, - 0x26, 0x63, 0xce, 0x98, 0xf4, 0x0f, 0xf4, 0x1d, 0x75, 0x32, 0x36, 0x55, 0xb6, 0x88, 0x31, 0x89, - 0xc6, 0x30, 0xc0, 0xe4, 0x65, 0x52, 0x2c, 0x65, 0x9c, 0x53, 0x1c, 0xaf, 0x18, 0x26, 0x7e, 0x5f, - 0xb7, 0xe6, 0xc0, 0xda, 0xa7, 0x14, 0x3f, 0x65, 0x98, 0x6c, 0x22, 0x69, 0x9e, 0x1a, 0xe4, 0x60, - 0x0b, 0xf9, 0x24, 0x4f, 0x35, 0xf2, 0x23, 0xe8, 0xa5, 0x79, 0x21, 0x88, 0x74, 0xbd, 0xb9, 0xae, - 0x61, 0x5d, 0x63, 0x34, 0x5d, 0x09, 0x7e, 0x82, 0x03, 0x37, 0xfa, 0x22, 0x67, 0x99, 0x20, 0xe8, - 0x0c, 0x9a, 0xb6, 0xa7, 0x7a, 0xfe, 0x3b, 0x47, 0xf7, 0xc2, 0x6a, 0xcb, 0x18, 0xda, 0x7e, 0xcf, - 0x64, 0x22, 0x49, 0xe4, 0x9c, 0x04, 0x3d, 0xe8, 0x3c, 0x4f, 0xa8, 0xb4, 0xab, 0x15, 0xfc, 0x08, - 0x5d, 0x73, 0xfc, 0x9f, 0xc2, 0x9d, 0x42, 0x7f, 0xb6, 0x28, 0x24, 0x66, 0xaf, 0x32, 0xb7, 0xcd, - 0x37, 0xa0, 0x21, 0xe8, 0x3c, 0x4b, 0x96, 0x76, 0xa1, 0xed, 0x09, 0x7d, 0x08, 0xdd, 0x39, 0x4f, - 0x52, 0x12, 0xe7, 0x84, 0x53, 0x86, 0xfd, 0xbd, 0x91, 0x37, 0xae, 0x45, 0x1d, 0x6d, 0x9b, 0x6a, - 0x53, 0x80, 0x60, 0x70, 0xe9, 0xcd, 0x64, 0x1c, 0x2c, 0xe0, 0xc6, 0x77, 0x39, 0x56, 0x41, 0xcb, - 0x25, 0xb6, 0x81, 0xb6, 0x04, 0xc1, 0xfb, 0xcf, 0x82, 0x10, 0xdc, 0x84, 0xf7, 0x5f, 0x8b, 0x64, - 0x93, 0x18, 0xc0, 0xc1, 0xf7, 0x84, 0x0b, 0xca, 0x5c, 0x95, 0xc1, 0xa7, 0xd0, 0x2f, 0x2d, 0xf6, - 0x6e, 0x7d, 0x68, 0xae, 0x8d, 0xc9, 0x56, 0xee, 0x8e, 0xc1, 0x27, 0xd0, 0x55, 0xf7, 0x56, 0x66, - 0x3e, 0x84, 0x16, 0xcd, 0x24, 0xe1, 0x6b, 0x7b, 0x49, 0xb5, 0xa8, 0x3c, 0x07, 0xcf, 0xa1, 0x67, - 0xb1, 0xd6, 0xed, 0x37, 0x50, 0x17, 0xca, 0xb0, 0x63, 0x89, 0xcf, 0x12, 0x71, 0x6e, 0x1c, 0x19, - 0x7a, 0x70, 0x1b, 0x7a, 0x33, 0xdd, 0x89, 0x37, 0x37, 0xaa, 0xee, 0x1a, 0xa5, 0x8a, 0x75, 0x40, - 0x5b, 0xfe, 0x39, 0x74, 0x1e, 0x5f, 0x90, 0xd4, 0x11, 0x1f, 0x40, 0x0b, 0x93, 0x04, 0x2f, 0x69, - 0x46, 0x6c, 0x52, 0xc3, 0xd0, 0xbc, 0x0c, 0xa1, 0x7b, 0x19, 0xc2, 0x67, 0xee, 0x65, 0x88, 0x4a, - 0xac, 0xd3, 0xf9, 0xbd, 0xd7, 0x75, 0xbe, 0x76, 0xa9, 0xf3, 0xc1, 0x31, 0x74, 0x4d, 0x30, 0x5b, - 0xff, 0x0d, 0x68, 0xb0, 0x42, 0xe6, 0x85, 0xd4, 0xb1, 0xba, 0x91, 0x3d, 0xa1, 0x0f, 0xa0, 0x4d, - 0x2e, 0xa8, 0x8c, 0x53, 0xb5, 0x93, 0x7b, 0xba, 0x82, 0x96, 0x32, 0x1c, 0x33, 0x4c, 0x82, 0xdf, - 0x3d, 0xe8, 0x6e, 0x4e, 0xac, 0x8a, 0x9d, 0x53, 0x6c, 0x2b, 0x55, 0x9f, 0x6f, 0xe5, 0x6f, 0xdc, - 0x4d, 0x6d, 0xf3, 0x6e, 0x50, 0x08, 0xfb, 0xea, 0xcd, 0xd3, 0xaf, 0xc5, 0xdb, 0xcb, 0xd6, 0xb8, - 0xa3, 0x3f, 0xdb, 0xd0, 0x7a, 0x6c, 0x17, 0x09, 0xfd, 0x02, 0x0d, 0xb3, 0xfd, 0xe8, 0x7e, 0xd5, - 0xad, 0xdb, 0x7a, 0x28, 0x87, 0x0f, 0x76, 0xa5, 0xd9, 0xfe, 0x5d, 0x43, 0x02, 0xf6, 0x95, 0x0e, - 0xa0, 0xbb, 0x55, 0x3d, 0x6c, 0x88, 0xc8, 0xf0, 0xde, 0x6e, 0xa4, 0x32, 0xe8, 0x6f, 0xd0, 0x72, - 0xeb, 0x8c, 0x1e, 0x56, 0xf5, 0x71, 0x45, 0x4e, 0x86, 0x9f, 0xef, 0x4e, 0x2c, 0x13, 0xf8, 0xc3, - 0x83, 0xfe, 0x95, 0x95, 0x46, 0x5f, 0x56, 0xf5, 0xf7, 0x66, 0xd5, 0x19, 0x3e, 0x7a, 0x67, 0x7e, - 0x99, 0xd6, 0xaf, 0xd0, 0xb4, 0xda, 0x81, 0x2a, 0x77, 0x74, 0x5b, 0x7e, 0x86, 0x0f, 0x77, 0xe6, - 0x95, 0xd1, 0x2f, 0xa0, 0xae, 0x75, 0x01, 0x55, 0x6e, 0xeb, 0xa6, 0x76, 0x0d, 0xef, 0xef, 0xc8, - 0x72, 0x71, 0x0f, 0x3d, 0x35, 0xff, 0x46, 0x58, 0xaa, 0xcf, 0xff, 0x96, 0x62, 0x55, 0x9f, 0xff, - 0x2b, 0xfa, 0xa5, 0xe7, 0x5f, 0xad, 0x61, 0xf5, 0xf9, 0xdf, 0xd0, 0xbb, 0xea, 0xf3, 0xbf, 0xa9, - 0x5b, 0xc1, 0x35, 0xf4, 0x97, 0x07, 0x3d, 0x65, 0x9a, 0x49, 0x4e, 0x92, 0x15, 0xcd, 0xe6, 0xe8, - 0x51, 0x45, 0xf1, 0x56, 0x2c, 0x23, 0xe0, 0x96, 0xe9, 0x52, 0xf9, 0xea, 0xdd, 0x1d, 0xb8, 0xb4, - 0xc6, 0xde, 0xa1, 0xf7, 0x75, 0xf3, 0x87, 0xba, 0xd1, 0xac, 0x86, 0xfe, 0xb9, 0xfb, 0x4f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x32, 0xbc, 0x95, 0x4b, 0x31, 0x0c, 0x00, 0x00, + // 1041 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xed, 0x6f, 0x1b, 0xc5, + 0x13, 0xee, 0xc5, 0x89, 0x5f, 0xc6, 0x76, 0xe2, 0xae, 0x7e, 0xca, 0xef, 0x6a, 0x84, 0x6a, 0x0e, + 0x89, 0x5a, 0x50, 0x2e, 0x51, 0xfa, 0x86, 0x84, 0x44, 0x11, 0x49, 0x41, 0x95, 0xd2, 0x28, 0xba, + 0x14, 0x2a, 0xf1, 0x81, 0x63, 0x7b, 0xb7, 0xb5, 0x57, 0xb1, 0x6f, 0x97, 0xdd, 0x3d, 0x27, 0x48, + 0x48, 0x7c, 0xe2, 0x3f, 0x00, 0xa9, 0x7f, 0x2e, 0xda, 0xb7, 0x8b, 0x9d, 0x96, 0xea, 0x5c, 0xc4, + 0x27, 0xdf, 0x8e, 0x9f, 0x67, 0x66, 0x76, 0x67, 0xe6, 0x19, 0xb8, 0x9b, 0x0b, 0xba, 0x20, 0x42, + 0xee, 0xc9, 0x29, 0x16, 0x24, 0xdf, 0x23, 0x97, 0x24, 0x2b, 0x15, 0x13, 0x7b, 0x5c, 0x30, 0xc5, + 0xaa, 0x63, 0x6c, 0x8e, 0xe8, 0x93, 0x29, 0x96, 0x53, 0x9a, 0x31, 0xc1, 0xe3, 0x82, 0xcd, 0x71, + 0x1e, 0xf3, 0x59, 0x39, 0xa1, 0x85, 0x8c, 0x57, 0x71, 0xc3, 0xdb, 0x13, 0xc6, 0x26, 0x33, 0x62, + 0x9d, 0xbc, 0x2c, 0x5f, 0xed, 0x29, 0x3a, 0x27, 0x52, 0xe1, 0x39, 0x77, 0x80, 0xc8, 0x11, 0xf7, + 0x7c, 0x78, 0x1b, 0xce, 0x9e, 0x2c, 0x26, 0x7a, 0xdd, 0x84, 0xfe, 0x31, 0x2e, 0x8b, 0x6c, 0x9a, + 0x90, 0x5f, 0x4a, 0x22, 0x15, 0x1a, 0x40, 0x23, 0x9b, 0xe7, 0x61, 0x30, 0x0a, 0xc6, 0x9d, 0x44, + 0x7f, 0x22, 0x04, 0x9b, 0x58, 0x4c, 0x64, 0xb8, 0x31, 0x6a, 0x8c, 0x3b, 0x89, 0xf9, 0x46, 0x27, + 0xd0, 0x11, 0x44, 0xb2, 0x52, 0x64, 0x44, 0x86, 0x8d, 0x51, 0x30, 0xee, 0x1e, 0xec, 0xc7, 0xff, + 0x94, 0xb8, 0x8b, 0x6f, 0x43, 0xc6, 0x89, 0xe7, 0x25, 0x57, 0x2e, 0xd0, 0x6d, 0xe8, 0x4a, 0x95, + 0xb3, 0x52, 0xa5, 0x1c, 0xab, 0x69, 0xb8, 0x69, 0xa2, 0x83, 0x35, 0x9d, 0x62, 0x35, 0x75, 0x00, + 0x22, 0x84, 0x05, 0x6c, 0x55, 0x00, 0x22, 0x84, 0x01, 0x0c, 0xa0, 0x41, 0x8a, 0x45, 0xd8, 0x34, + 0x49, 0xea, 0x4f, 0x9d, 0x77, 0x29, 0x89, 0x08, 0x5b, 0x06, 0x6b, 0xbe, 0xd1, 0x2d, 0x68, 0x2b, + 0x2c, 0xcf, 0xd3, 0x9c, 0x8a, 0xb0, 0x6d, 0xec, 0x2d, 0x7d, 0x3e, 0xa2, 0x02, 0xdd, 0x81, 0x1d, + 0x9f, 0x4f, 0x3a, 0xa3, 0x73, 0xaa, 0x64, 0xd8, 0x19, 0x05, 0xe3, 0x76, 0xb2, 0xed, 0xcd, 0xc7, + 0xc6, 0x8a, 0xf6, 0xe1, 0x7f, 0x2f, 0xb1, 0xa4, 0x59, 0xca, 0x05, 0xcb, 0x88, 0x94, 0x69, 0x36, + 0x11, 0xac, 0xe4, 0x21, 0x18, 0x34, 0x32, 0xff, 0x9d, 0xda, 0xbf, 0x0e, 0xcd, 0x3f, 0xe8, 0x08, + 0x9a, 0x73, 0x56, 0x16, 0x4a, 0x86, 0xdd, 0x51, 0x63, 0xdc, 0x3d, 0xb8, 0x5b, 0xf3, 0xa9, 0x9e, + 0x69, 0x52, 0xe2, 0xb8, 0xe8, 0x3b, 0x68, 0xe5, 0x64, 0x41, 0xf5, 0x8b, 0xf7, 0x8c, 0x9b, 0xcf, + 0x6b, 0xba, 0x39, 0x32, 0xac, 0xc4, 0xb3, 0xd1, 0x14, 0x6e, 0x16, 0x44, 0x5d, 0x30, 0x71, 0x9e, + 0x52, 0xc9, 0x66, 0x58, 0x51, 0x56, 0x84, 0x7d, 0x53, 0xc4, 0x2f, 0x6b, 0xba, 0x3c, 0xb1, 0xfc, + 0xa7, 0x9e, 0x7e, 0xc6, 0x49, 0x96, 0x0c, 0x8a, 0x6b, 0x56, 0x14, 0x41, 0xbf, 0x60, 0x29, 0xa7, + 0x0b, 0xa6, 0x52, 0xc1, 0x98, 0x0a, 0xb7, 0xcd, 0x1b, 0x75, 0x0b, 0x76, 0xaa, 0x6d, 0x09, 0x63, + 0x0a, 0x8d, 0x61, 0x90, 0x93, 0x57, 0xb8, 0x9c, 0xa9, 0x94, 0xd3, 0x3c, 0x9d, 0xb3, 0x9c, 0x84, + 0x3b, 0xa6, 0x34, 0xdb, 0xce, 0x7e, 0x4a, 0xf3, 0x67, 0x2c, 0x27, 0xcb, 0x48, 0xca, 0x33, 0x8b, + 0x1c, 0xac, 0x20, 0x9f, 0xf2, 0xcc, 0x20, 0x3f, 0x86, 0x7e, 0xc6, 0x4b, 0x49, 0x94, 0xaf, 0xcd, + 0x4d, 0x03, 0xeb, 0x59, 0xa3, 0xab, 0xca, 0x87, 0x00, 0x78, 0x36, 0x63, 0x17, 0x69, 0x86, 0xb9, + 0x0c, 0x91, 0x69, 0x9c, 0x8e, 0xb1, 0x1c, 0x62, 0x2e, 0xa3, 0x9f, 0x61, 0xdb, 0x4f, 0x86, 0xe4, + 0xac, 0x90, 0x04, 0x9d, 0x40, 0xcb, 0x95, 0xdc, 0x8c, 0x47, 0xf7, 0xe0, 0x7e, 0x5c, 0x6f, 0x56, + 0x63, 0xd7, 0x0e, 0x67, 0x0a, 0x2b, 0x92, 0x78, 0x27, 0x51, 0x1f, 0xba, 0x2f, 0x30, 0x55, 0x6e, + 0xf2, 0xa2, 0x9f, 0xa0, 0x67, 0x8f, 0xff, 0x51, 0xb8, 0x63, 0xd8, 0x39, 0x9b, 0x96, 0x2a, 0x67, + 0x17, 0x85, 0x1f, 0xf6, 0x5d, 0x68, 0x4a, 0x3a, 0x29, 0xf0, 0xcc, 0xcd, 0xbb, 0x3b, 0xa1, 0x8f, + 0xa0, 0x37, 0x11, 0x38, 0x23, 0x29, 0x27, 0x82, 0xb2, 0x3c, 0xdc, 0x18, 0x05, 0xe3, 0x46, 0xd2, + 0x35, 0xb6, 0x53, 0x63, 0x8a, 0x10, 0x0c, 0xae, 0xbc, 0xd9, 0x8c, 0xa3, 0x29, 0xec, 0x7e, 0xcf, + 0x73, 0x1d, 0xb4, 0x9a, 0x71, 0x17, 0x68, 0x45, 0x2f, 0x82, 0x7f, 0xad, 0x17, 0xd1, 0x2d, 0xf8, + 0xff, 0x1b, 0x91, 0x5c, 0x12, 0x03, 0xd8, 0xfe, 0x81, 0x08, 0x49, 0x99, 0xbf, 0x65, 0xf4, 0x19, + 0xec, 0x54, 0x16, 0xf7, 0xb6, 0x21, 0xb4, 0x16, 0xd6, 0xe4, 0x6e, 0xee, 0x8f, 0xd1, 0xa7, 0xd0, + 0xd3, 0xef, 0x56, 0x65, 0x3e, 0x84, 0x36, 0x2d, 0x14, 0x11, 0x0b, 0xf7, 0x48, 0x8d, 0xa4, 0x3a, + 0x47, 0x2f, 0xa0, 0xef, 0xb0, 0xce, 0xed, 0xb7, 0xb0, 0x25, 0xb5, 0x61, 0xcd, 0x2b, 0x3e, 0xc7, + 0xf2, 0xdc, 0x3a, 0xb2, 0xf4, 0xe8, 0x0e, 0xf4, 0xcf, 0x4c, 0x25, 0xde, 0x5e, 0xa8, 0x2d, 0x5f, + 0x28, 0x7d, 0x59, 0x0f, 0x74, 0xd7, 0x3f, 0x87, 0xee, 0x93, 0x4b, 0x92, 0x79, 0xe2, 0x43, 0x68, + 0xe7, 0x04, 0xe7, 0x33, 0x5a, 0x10, 0x97, 0xd4, 0x30, 0xb6, 0x8b, 0x23, 0xf6, 0x8b, 0x23, 0x7e, + 0xee, 0x17, 0x47, 0x52, 0x61, 0xfd, 0x1a, 0xd8, 0x78, 0x73, 0x0d, 0x34, 0xae, 0xd6, 0x40, 0x74, + 0x08, 0x3d, 0x1b, 0xcc, 0xdd, 0x7f, 0x17, 0x9a, 0xac, 0x54, 0xbc, 0x54, 0x26, 0x56, 0x2f, 0x71, + 0x27, 0xf4, 0x01, 0x74, 0xc8, 0x25, 0x55, 0x69, 0xa6, 0x47, 0x76, 0xc3, 0xdc, 0xa0, 0xad, 0x0d, + 0x87, 0x2c, 0x27, 0xd1, 0x1f, 0x01, 0xf4, 0x96, 0x3b, 0x56, 0xc7, 0xe6, 0x34, 0x77, 0x37, 0xd5, + 0x9f, 0xef, 0xe4, 0x2f, 0xbd, 0x4d, 0x63, 0xf9, 0x6d, 0x50, 0x0c, 0x9b, 0x7a, 0x25, 0x9a, 0x65, + 0xf2, 0xee, 0x6b, 0x1b, 0xdc, 0xc1, 0x5f, 0x1d, 0x68, 0x3f, 0x71, 0x83, 0x84, 0x7e, 0x85, 0xa6, + 0x9d, 0x7e, 0xf4, 0xa0, 0xee, 0xd4, 0xad, 0xec, 0xd1, 0xe1, 0xc3, 0x75, 0x69, 0xae, 0x7e, 0x37, + 0x90, 0x84, 0x4d, 0xad, 0x03, 0xe8, 0x5e, 0x5d, 0x0f, 0x4b, 0x22, 0x32, 0xbc, 0xbf, 0x1e, 0xa9, + 0x0a, 0xfa, 0x3b, 0xb4, 0xfd, 0x38, 0xa3, 0x47, 0x75, 0x7d, 0x5c, 0x93, 0x93, 0xe1, 0x17, 0xeb, + 0x13, 0xab, 0x04, 0xfe, 0x0c, 0x60, 0xe7, 0xda, 0x48, 0xa3, 0xaf, 0xea, 0xfa, 0x7b, 0xbb, 0xea, + 0x0c, 0x1f, 0xbf, 0x37, 0xbf, 0x4a, 0xeb, 0x37, 0x68, 0x39, 0xed, 0x40, 0xb5, 0x2b, 0xba, 0x2a, + 0x3f, 0xc3, 0x47, 0x6b, 0xf3, 0xaa, 0xe8, 0x97, 0xb0, 0x65, 0x74, 0x01, 0xd5, 0x2e, 0xeb, 0xb2, + 0x76, 0x0d, 0x1f, 0xac, 0xc9, 0xf2, 0x71, 0xf7, 0x03, 0xdd, 0xff, 0x56, 0x58, 0xea, 0xf7, 0xff, + 0x8a, 0x62, 0xd5, 0xef, 0xff, 0x6b, 0xfa, 0x65, 0xfa, 0x5f, 0x8f, 0x61, 0xfd, 0xfe, 0x5f, 0xd2, + 0xbb, 0xfa, 0xfd, 0xbf, 0xac, 0x5b, 0xd1, 0x0d, 0xf4, 0x3a, 0x80, 0xbe, 0x36, 0x9d, 0x29, 0x41, + 0xf0, 0x9c, 0x16, 0x13, 0xf4, 0xb8, 0xa6, 0x78, 0x6b, 0x96, 0x15, 0x70, 0xc7, 0xf4, 0xa9, 0x7c, + 0xfd, 0xfe, 0x0e, 0x7c, 0x5a, 0xe3, 0x60, 0x3f, 0xf8, 0xa6, 0xf5, 0xe3, 0x96, 0xd5, 0xac, 0xa6, + 0xf9, 0xb9, 0xf7, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x41, 0x13, 0xe3, 0x8e, 0x50, 0x0c, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/drivers/shared/executor/proto/executor.proto b/drivers/shared/executor/proto/executor.proto index db0a7b3cbfd..9ac241209fc 100644 --- a/drivers/shared/executor/proto/executor.proto +++ b/drivers/shared/executor/proto/executor.proto @@ -45,6 +45,7 @@ message LaunchRequest { string default_pid_mode = 15; string default_ipc_mode = 16; string cpuset_cgroup = 17; + repeated string allow_caps = 18; } message LaunchResponse {