Skip to content

Commit 86a6569

Browse files
author
Mahmood Ali
authored
Merge pull request hashicorp#5728 from hashicorp/restore-08-caps
drivers/exec: Restore 0.8 capabilities
2 parents d017b5f + 6217d50 commit 86a6569

File tree

4 files changed

+119
-26
lines changed

4 files changed

+119
-26
lines changed

drivers/exec/driver_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ func TestExecDriver_DevicesAndMounts(t *testing.T) {
475475
task := &drivers.TaskConfig{
476476
ID: uuid.Generate(),
477477
Name: "test",
478+
User: "root", // need permission to read mounts paths
478479
Resources: testResources,
479480
StdoutPath: filepath.Join(tmpDir, "task-stdout"),
480481
StderrPath: filepath.Join(tmpDir, "task-stderr"),

drivers/shared/executor/executor_linux.go

+33-24
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,8 @@ var (
4545

4646
// ExecutorCgroupMeasuredCpuStats is the list of CPU stats captures by the executor
4747
ExecutorCgroupMeasuredCpuStats = []string{"System Mode", "User Mode", "Throttled Periods", "Throttled Time", "Percent"}
48-
49-
// allCaps is all linux capabilities which is used to configure libcontainer
50-
allCaps []string
5148
)
5249

53-
// initialize the allCaps var with all capabilities available on the system
54-
func init() {
55-
last := capability.CAP_LAST_CAP
56-
// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
57-
if last == capability.Cap(63) {
58-
last = capability.CAP_BLOCK_SUSPEND
59-
}
60-
for _, cap := range capability.List() {
61-
if cap > last {
62-
continue
63-
}
64-
allCaps = append(allCaps, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())))
65-
}
66-
}
67-
6850
// LibcontainerExecutor implements an Executor with the runc/libcontainer api
6951
type LibcontainerExecutor struct {
7052
id string
@@ -569,17 +551,44 @@ func (l *LibcontainerExecutor) handleExecWait(ch chan *waitResult, process *libc
569551

570552
func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) error {
571553
// TODO: allow better control of these
572-
cfg.Capabilities = &lconfigs.Capabilities{
573-
Bounding: allCaps,
574-
Permitted: allCaps,
575-
Inheritable: allCaps,
576-
Ambient: allCaps,
577-
Effective: allCaps,
554+
// use capabilities list as prior to adopting libcontainer in 0.9
555+
allCaps := supportedCaps()
556+
557+
// match capabilities used in Nomad 0.8
558+
if command.User == "root" {
559+
cfg.Capabilities = &lconfigs.Capabilities{
560+
Bounding: allCaps,
561+
Permitted: allCaps,
562+
Effective: allCaps,
563+
Ambient: nil,
564+
Inheritable: nil,
565+
}
566+
} else {
567+
cfg.Capabilities = &lconfigs.Capabilities{
568+
Bounding: allCaps,
569+
}
578570
}
579571

580572
return nil
581573
}
582574

575+
// supportedCaps returns a list of all supported capabilities in kernel
576+
func supportedCaps() []string {
577+
allCaps := []string{}
578+
last := capability.CAP_LAST_CAP
579+
// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
580+
if last == capability.Cap(63) {
581+
last = capability.CAP_BLOCK_SUSPEND
582+
}
583+
for _, cap := range capability.List() {
584+
if cap > last {
585+
continue
586+
}
587+
allCaps = append(allCaps, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())))
588+
}
589+
return allCaps
590+
}
591+
583592
// configureIsolation prepares the isolation primitives of the container.
584593
// The process runs in a container configured with the following:
585594
//

drivers/shared/executor/executor_linux_test.go

+84-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io/ioutil"
77
"os"
88
"path/filepath"
9+
"regexp"
910
"strconv"
1011
"strings"
1112
"testing"
@@ -44,6 +45,7 @@ func testExecutorCommandWithChroot(t *testing.T) *testExecCmd {
4445
"/etc/ld.so.cache": "/etc/ld.so.cache",
4546
"/etc/ld.so.conf": "/etc/ld.so.conf",
4647
"/etc/ld.so.conf.d": "/etc/ld.so.conf.d",
48+
"/etc/passwd": "/etc/passwd",
4749
"/lib": "/lib",
4850
"/lib64": "/lib64",
4951
"/usr/lib": "/usr/lib",
@@ -150,7 +152,8 @@ usr/
150152
/etc/:
151153
ld.so.cache
152154
ld.so.conf
153-
ld.so.conf.d/`
155+
ld.so.conf.d/
156+
passwd`
154157
tu.WaitForResult(func() (bool, error) {
155158
output := testExecCmd.stdout.String()
156159
act := strings.TrimSpace(string(output))
@@ -239,6 +242,86 @@ func TestExecutor_EscapeContainer(t *testing.T) {
239242
require.NoError(err)
240243
}
241244

245+
func TestExecutor_Capabilities(t *testing.T) {
246+
t.Parallel()
247+
testutil.ExecCompatible(t)
248+
249+
cases := []struct {
250+
user string
251+
caps string
252+
}{
253+
{
254+
user: "nobody",
255+
caps: `
256+
CapInh: 0000000000000000
257+
CapPrm: 0000000000000000
258+
CapEff: 0000000000000000
259+
CapBnd: 0000003fffffffff
260+
CapAmb: 0000000000000000`,
261+
},
262+
{
263+
user: "root",
264+
caps: `
265+
CapInh: 0000000000000000
266+
CapPrm: 0000003fffffffff
267+
CapEff: 0000003fffffffff
268+
CapBnd: 0000003fffffffff
269+
CapAmb: 0000000000000000`,
270+
},
271+
}
272+
273+
for _, c := range cases {
274+
t.Run(c.user, func(t *testing.T) {
275+
require := require.New(t)
276+
277+
testExecCmd := testExecutorCommandWithChroot(t)
278+
execCmd, allocDir := testExecCmd.command, testExecCmd.allocDir
279+
defer allocDir.Destroy()
280+
281+
execCmd.User = c.user
282+
execCmd.ResourceLimits = true
283+
execCmd.Cmd = "/bin/bash"
284+
execCmd.Args = []string{"-c", "cat /proc/$$/status"}
285+
286+
executor := NewExecutorWithIsolation(testlog.HCLogger(t))
287+
defer executor.Shutdown("SIGKILL", 0)
288+
289+
_, err := executor.Launch(execCmd)
290+
require.NoError(err)
291+
292+
ch := make(chan interface{})
293+
go func() {
294+
executor.Wait(context.Background())
295+
close(ch)
296+
}()
297+
298+
select {
299+
case <-ch:
300+
// all good
301+
case <-time.After(5 * time.Second):
302+
require.Fail("timeout waiting for exec to shutdown")
303+
}
304+
305+
canonical := func(s string) string {
306+
s = strings.TrimSpace(s)
307+
s = regexp.MustCompile("[ \t]+").ReplaceAllString(s, " ")
308+
s = regexp.MustCompile("[\n\r]+").ReplaceAllString(s, "\n")
309+
return s
310+
}
311+
312+
expected := canonical(c.caps)
313+
tu.WaitForResult(func() (bool, error) {
314+
output := canonical(testExecCmd.stdout.String())
315+
if !strings.Contains(output, expected) {
316+
return false, fmt.Errorf("capabilities didn't match: want\n%v\n; got:\n%v\n", expected, output)
317+
}
318+
return true, nil
319+
}, func(err error) { require.NoError(err) })
320+
})
321+
}
322+
323+
}
324+
242325
func TestExecutor_ClientCleanup(t *testing.T) {
243326
t.Parallel()
244327
testutil.ExecCompatible(t)

drivers/shared/executor/executor_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ func setupRootfsBinary(t *testing.T, rootfs, path string) {
473473
t.Helper()
474474

475475
dst := filepath.Join(rootfs, path)
476-
err := os.MkdirAll(filepath.Dir(dst), 666)
476+
err := os.MkdirAll(filepath.Dir(dst), 0755)
477477
require.NoError(t, err)
478478

479479
src := filepath.Join(

0 commit comments

Comments
 (0)