mirror of
https://github.com/kemko/nomad.git
synced 2026-01-07 02:45:42 +03:00
Merge pull request #5728 from hashicorp/restore-08-caps
drivers/exec: Restore 0.8 capabilities
This commit is contained in:
@@ -475,6 +475,7 @@ func TestExecDriver_DevicesAndMounts(t *testing.T) {
|
||||
task := &drivers.TaskConfig{
|
||||
ID: uuid.Generate(),
|
||||
Name: "test",
|
||||
User: "root", // need permission to read mounts paths
|
||||
Resources: testResources,
|
||||
StdoutPath: filepath.Join(tmpDir, "task-stdout"),
|
||||
StderrPath: filepath.Join(tmpDir, "task-stderr"),
|
||||
|
||||
@@ -45,26 +45,8 @@ var (
|
||||
|
||||
// ExecutorCgroupMeasuredCpuStats is the list of CPU stats captures by the executor
|
||||
ExecutorCgroupMeasuredCpuStats = []string{"System Mode", "User Mode", "Throttled Periods", "Throttled Time", "Percent"}
|
||||
|
||||
// allCaps is all linux capabilities which is used to configure libcontainer
|
||||
allCaps []string
|
||||
)
|
||||
|
||||
// initialize the allCaps var with all capabilities available on the system
|
||||
func init() {
|
||||
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
|
||||
}
|
||||
for _, cap := range capability.List() {
|
||||
if cap > last {
|
||||
continue
|
||||
}
|
||||
allCaps = append(allCaps, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())))
|
||||
}
|
||||
}
|
||||
|
||||
// LibcontainerExecutor implements an Executor with the runc/libcontainer api
|
||||
type LibcontainerExecutor struct {
|
||||
id string
|
||||
@@ -569,17 +551,44 @@ func (l *LibcontainerExecutor) handleExecWait(ch chan *waitResult, process *libc
|
||||
|
||||
func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) error {
|
||||
// TODO: allow better control of these
|
||||
cfg.Capabilities = &lconfigs.Capabilities{
|
||||
Bounding: allCaps,
|
||||
Permitted: allCaps,
|
||||
Inheritable: allCaps,
|
||||
Ambient: allCaps,
|
||||
Effective: allCaps,
|
||||
// use capabilities list as prior to adopting libcontainer in 0.9
|
||||
allCaps := supportedCaps()
|
||||
|
||||
// match capabilities used in Nomad 0.8
|
||||
if command.User == "root" {
|
||||
cfg.Capabilities = &lconfigs.Capabilities{
|
||||
Bounding: allCaps,
|
||||
Permitted: allCaps,
|
||||
Effective: allCaps,
|
||||
Ambient: nil,
|
||||
Inheritable: nil,
|
||||
}
|
||||
} else {
|
||||
cfg.Capabilities = &lconfigs.Capabilities{
|
||||
Bounding: allCaps,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// supportedCaps returns a list of all supported capabilities in kernel
|
||||
func supportedCaps() []string {
|
||||
allCaps := []string{}
|
||||
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
|
||||
}
|
||||
for _, cap := range capability.List() {
|
||||
if cap > last {
|
||||
continue
|
||||
}
|
||||
allCaps = append(allCaps, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())))
|
||||
}
|
||||
return allCaps
|
||||
}
|
||||
|
||||
// configureIsolation prepares the isolation primitives of the container.
|
||||
// The process runs in a container configured with the following:
|
||||
//
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -44,6 +45,7 @@ func testExecutorCommandWithChroot(t *testing.T) *testExecCmd {
|
||||
"/etc/ld.so.cache": "/etc/ld.so.cache",
|
||||
"/etc/ld.so.conf": "/etc/ld.so.conf",
|
||||
"/etc/ld.so.conf.d": "/etc/ld.so.conf.d",
|
||||
"/etc/passwd": "/etc/passwd",
|
||||
"/lib": "/lib",
|
||||
"/lib64": "/lib64",
|
||||
"/usr/lib": "/usr/lib",
|
||||
@@ -150,7 +152,8 @@ usr/
|
||||
/etc/:
|
||||
ld.so.cache
|
||||
ld.so.conf
|
||||
ld.so.conf.d/`
|
||||
ld.so.conf.d/
|
||||
passwd`
|
||||
tu.WaitForResult(func() (bool, error) {
|
||||
output := testExecCmd.stdout.String()
|
||||
act := strings.TrimSpace(string(output))
|
||||
@@ -239,6 +242,86 @@ func TestExecutor_EscapeContainer(t *testing.T) {
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func TestExecutor_Capabilities(t *testing.T) {
|
||||
t.Parallel()
|
||||
testutil.ExecCompatible(t)
|
||||
|
||||
cases := []struct {
|
||||
user string
|
||||
caps string
|
||||
}{
|
||||
{
|
||||
user: "nobody",
|
||||
caps: `
|
||||
CapInh: 0000000000000000
|
||||
CapPrm: 0000000000000000
|
||||
CapEff: 0000000000000000
|
||||
CapBnd: 0000003fffffffff
|
||||
CapAmb: 0000000000000000`,
|
||||
},
|
||||
{
|
||||
user: "root",
|
||||
caps: `
|
||||
CapInh: 0000000000000000
|
||||
CapPrm: 0000003fffffffff
|
||||
CapEff: 0000003fffffffff
|
||||
CapBnd: 0000003fffffffff
|
||||
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
|
||||
defer allocDir.Destroy()
|
||||
|
||||
execCmd.User = c.user
|
||||
execCmd.ResourceLimits = true
|
||||
execCmd.Cmd = "/bin/bash"
|
||||
execCmd.Args = []string{"-c", "cat /proc/$$/status"}
|
||||
|
||||
executor := NewExecutorWithIsolation(testlog.HCLogger(t))
|
||||
defer executor.Shutdown("SIGKILL", 0)
|
||||
|
||||
_, err := executor.Launch(execCmd)
|
||||
require.NoError(err)
|
||||
|
||||
ch := make(chan interface{})
|
||||
go func() {
|
||||
executor.Wait(context.Background())
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
// all good
|
||||
case <-time.After(5 * time.Second):
|
||||
require.Fail("timeout waiting for exec to shutdown")
|
||||
}
|
||||
|
||||
canonical := func(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
s = regexp.MustCompile("[ \t]+").ReplaceAllString(s, " ")
|
||||
s = regexp.MustCompile("[\n\r]+").ReplaceAllString(s, "\n")
|
||||
return s
|
||||
}
|
||||
|
||||
expected := canonical(c.caps)
|
||||
tu.WaitForResult(func() (bool, error) {
|
||||
output := canonical(testExecCmd.stdout.String())
|
||||
if !strings.Contains(output, expected) {
|
||||
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 TestExecutor_ClientCleanup(t *testing.T) {
|
||||
t.Parallel()
|
||||
testutil.ExecCompatible(t)
|
||||
|
||||
@@ -473,7 +473,7 @@ func setupRootfsBinary(t *testing.T, rootfs, path string) {
|
||||
t.Helper()
|
||||
|
||||
dst := filepath.Join(rootfs, path)
|
||||
err := os.MkdirAll(filepath.Dir(dst), 666)
|
||||
err := os.MkdirAll(filepath.Dir(dst), 0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
src := filepath.Join(
|
||||
|
||||
Reference in New Issue
Block a user