Unverified Commit 86a65699 authored by Mahmood Ali's avatar Mahmood Ali Committed by GitHub
Browse files

Merge pull request #5728 from hashicorp/restore-08-caps

drivers/exec: Restore 0.8 capabilities
Showing with 119 additions and 26 deletions
+119 -26
...@@ -475,6 +475,7 @@ func TestExecDriver_DevicesAndMounts(t *testing.T) { ...@@ -475,6 +475,7 @@ func TestExecDriver_DevicesAndMounts(t *testing.T) {
task := &drivers.TaskConfig{ task := &drivers.TaskConfig{
ID: uuid.Generate(), ID: uuid.Generate(),
Name: "test", Name: "test",
User: "root", // need permission to read mounts paths
Resources: testResources, Resources: testResources,
StdoutPath: filepath.Join(tmpDir, "task-stdout"), StdoutPath: filepath.Join(tmpDir, "task-stdout"),
StderrPath: filepath.Join(tmpDir, "task-stderr"), StderrPath: filepath.Join(tmpDir, "task-stderr"),
......
...@@ -45,26 +45,8 @@ var ( ...@@ -45,26 +45,8 @@ var (
// ExecutorCgroupMeasuredCpuStats is the list of CPU stats captures by the executor // ExecutorCgroupMeasuredCpuStats is the list of CPU stats captures by the executor
ExecutorCgroupMeasuredCpuStats = []string{"System Mode", "User Mode", "Throttled Periods", "Throttled Time", "Percent"} 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 // LibcontainerExecutor implements an Executor with the runc/libcontainer api
type LibcontainerExecutor struct { type LibcontainerExecutor struct {
id string id string
...@@ -569,17 +551,44 @@ func (l *LibcontainerExecutor) handleExecWait(ch chan *waitResult, process *libc ...@@ -569,17 +551,44 @@ func (l *LibcontainerExecutor) handleExecWait(ch chan *waitResult, process *libc
func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) error { func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) error {
// TODO: allow better control of these // TODO: allow better control of these
cfg.Capabilities = &lconfigs.Capabilities{ // use capabilities list as prior to adopting libcontainer in 0.9
Bounding: allCaps, allCaps := supportedCaps()
Permitted: allCaps,
Inheritable: allCaps, // match capabilities used in Nomad 0.8
Ambient: allCaps, if command.User == "root" {
Effective: allCaps, cfg.Capabilities = &lconfigs.Capabilities{
Bounding: allCaps,
Permitted: allCaps,
Effective: allCaps,
Ambient: nil,
Inheritable: nil,
}
} else {
cfg.Capabilities = &lconfigs.Capabilities{
Bounding: allCaps,
}
} }
return nil 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. // configureIsolation prepares the isolation primitives of the container.
// The process runs in a container configured with the following: // The process runs in a container configured with the following:
// //
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
...@@ -44,6 +45,7 @@ func testExecutorCommandWithChroot(t *testing.T) *testExecCmd { ...@@ -44,6 +45,7 @@ func testExecutorCommandWithChroot(t *testing.T) *testExecCmd {
"/etc/ld.so.cache": "/etc/ld.so.cache", "/etc/ld.so.cache": "/etc/ld.so.cache",
"/etc/ld.so.conf": "/etc/ld.so.conf", "/etc/ld.so.conf": "/etc/ld.so.conf",
"/etc/ld.so.conf.d": "/etc/ld.so.conf.d", "/etc/ld.so.conf.d": "/etc/ld.so.conf.d",
"/etc/passwd": "/etc/passwd",
"/lib": "/lib", "/lib": "/lib",
"/lib64": "/lib64", "/lib64": "/lib64",
"/usr/lib": "/usr/lib", "/usr/lib": "/usr/lib",
...@@ -150,7 +152,8 @@ usr/ ...@@ -150,7 +152,8 @@ usr/
/etc/: /etc/:
ld.so.cache ld.so.cache
ld.so.conf ld.so.conf
ld.so.conf.d/` ld.so.conf.d/
passwd`
tu.WaitForResult(func() (bool, error) { tu.WaitForResult(func() (bool, error) {
output := testExecCmd.stdout.String() output := testExecCmd.stdout.String()
act := strings.TrimSpace(string(output)) act := strings.TrimSpace(string(output))
...@@ -239,6 +242,86 @@ func TestExecutor_EscapeContainer(t *testing.T) { ...@@ -239,6 +242,86 @@ func TestExecutor_EscapeContainer(t *testing.T) {
require.NoError(err) 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) { func TestExecutor_ClientCleanup(t *testing.T) {
t.Parallel() t.Parallel()
testutil.ExecCompatible(t) testutil.ExecCompatible(t)
......
...@@ -473,7 +473,7 @@ func setupRootfsBinary(t *testing.T, rootfs, path string) { ...@@ -473,7 +473,7 @@ func setupRootfsBinary(t *testing.T, rootfs, path string) {
t.Helper() t.Helper()
dst := filepath.Join(rootfs, path) dst := filepath.Join(rootfs, path)
err := os.MkdirAll(filepath.Dir(dst), 666) err := os.MkdirAll(filepath.Dir(dst), 0755)
require.NoError(t, err) require.NoError(t, err)
src := filepath.Join( src := filepath.Join(
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment