mirror of
https://github.com/kemko/nomad.git
synced 2026-01-07 10:55:42 +03:00
drivers/java: enable setting allow_caps on java driver
Enable setting allow_caps on the java task driver plugin, along with the associated cap_add and cap_drop options in java task configuration.
This commit is contained in:
@@ -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"
|
||||
@@ -73,6 +74,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
|
||||
@@ -88,11 +93,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
|
||||
// driverCapabilities is returned by the Capabilities RPC and indicates what
|
||||
// optional features this driver supports
|
||||
capabilities = &drivers.Capabilities{
|
||||
driverCapabilities = &drivers.Capabilities{
|
||||
SendSignals: false,
|
||||
Exec: false,
|
||||
FSIsolation: drivers.FSIsolationNone,
|
||||
@@ -108,8 +115,8 @@ var (
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "linux" {
|
||||
capabilities.FSIsolation = drivers.FSIsolationChroot
|
||||
capabilities.MountConfigs = drivers.MountConfigSupportAll
|
||||
driverCapabilities.FSIsolation = drivers.FSIsolationChroot
|
||||
driverCapabilities.MountConfigs = drivers.MountConfigSupportAll
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +129,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 {
|
||||
@@ -137,18 +148,44 @@ 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
|
||||
}
|
||||
|
||||
// TaskConfig is the driver configuration of a taskConfig within a job
|
||||
type TaskConfig struct {
|
||||
Class string `codec:"class"`
|
||||
ClassPath string `codec:"class_path"`
|
||||
JarPath string `codec:"jar_path"`
|
||||
JvmOpts []string `codec:"jvm_options"`
|
||||
Args []string `codec:"args"` // extra arguments to java executable
|
||||
ModePID string `codec:"pid_mode"`
|
||||
ModeIPC string `codec:"ipc_mode"`
|
||||
// Class indicates which class contains the java entry point.
|
||||
Class string `codec:"class"`
|
||||
|
||||
// ClassPath indicates where class files are found.
|
||||
ClassPath string `codec:"class_path"`
|
||||
|
||||
// JarPath indicates where a jar file is found.
|
||||
JarPath string `codec:"jar_path"`
|
||||
|
||||
// JvmOpts are arguments to pass to the JVM
|
||||
JvmOpts []string `codec:"jvm_options"`
|
||||
|
||||
// Args are extra arguments to java executable
|
||||
Args []string `codec:"args"`
|
||||
|
||||
// ModePID indicates whether PID namespace isolation is enabled for the task.
|
||||
// Must be "private" or "host" if set.
|
||||
ModePID string `codec:"pid_mode"`
|
||||
|
||||
// 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 {
|
||||
@@ -165,6 +202,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
|
||||
}
|
||||
|
||||
@@ -243,7 +290,7 @@ func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) {
|
||||
}
|
||||
|
||||
func (d *Driver) Capabilities() (*drivers.Capabilities, error) {
|
||||
return capabilities, nil
|
||||
return driverCapabilities, nil
|
||||
}
|
||||
|
||||
func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) {
|
||||
@@ -415,7 +462,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
|
||||
executorConfig := &executor.ExecutorConfig{
|
||||
LogFile: pluginLogFile,
|
||||
LogLevel: "debug",
|
||||
FSIsolation: capabilities.FSIsolation == drivers.FSIsolationChroot,
|
||||
FSIsolation: driverCapabilities.FSIsolation == drivers.FSIsolationChroot,
|
||||
}
|
||||
|
||||
exec, pluginClient, err := executor.CreateExecutor(
|
||||
@@ -438,6 +485,11 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
|
||||
cfg.Mounts = append(cfg.Mounts, dnsMount)
|
||||
}
|
||||
|
||||
caps, err := capabilities.Calculate(d.config.AllowCaps, driverConfig.CapAdd, driverConfig.CapDrop)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
execCmd := &executor.ExecCommand{
|
||||
Cmd: absPath,
|
||||
Args: args,
|
||||
@@ -453,6 +505,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)
|
||||
@@ -491,7 +544,8 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive
|
||||
}
|
||||
|
||||
func javaCmdArgs(driverConfig TaskConfig) []string {
|
||||
args := []string{}
|
||||
var args []string
|
||||
|
||||
// Look for jvm options
|
||||
if len(driverConfig.JvmOpts) != 0 {
|
||||
args = append(args, driverConfig.JvmOpts...)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -8,12 +9,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
|
||||
|
||||
"context"
|
||||
"time"
|
||||
|
||||
ctestutil "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
@@ -416,20 +415,99 @@ func Test_dnsConfig(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) {
|
||||
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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user