diff --git a/.changelog/16643.txt b/.changelog/16643.txt new file mode 100644 index 000000000..c01a3e789 --- /dev/null +++ b/.changelog/16643.txt @@ -0,0 +1,3 @@ +```release-note:bug +driver/exec: Fixed a bug where `cap_drop` and `cap_add` would not expand capabilities +``` diff --git a/drivers/shared/executor/executor_linux.go b/drivers/shared/executor/executor_linux.go index 4ab8367be..5516660cd 100644 --- a/drivers/shared/executor/executor_linux.go +++ b/drivers/shared/executor/executor_linux.go @@ -526,8 +526,17 @@ func configureCapabilities(cfg *lconfigs.Config, command *ExecCommand) { } default: // otherwise apply the plugin + task capability configuration + // + // The capabilities must be set in the Ambient set as libcontainer + // performs `execve`` as an unprivileged user. Ambient also requires + // that capabilities are Permitted and Inheritable. Setting Effective + // is unnecessary, because we only need the capabilities to become + // effective _after_ execve, not before. cfg.Capabilities = &lconfigs.Capabilities{ - Bounding: command.Capabilities, + Bounding: command.Capabilities, + Permitted: command.Capabilities, + Inheritable: command.Capabilities, + Ambient: command.Capabilities, } } } diff --git a/drivers/shared/executor/executor_linux_test.go b/drivers/shared/executor/executor_linux_test.go index 102785782..be16435ad 100644 --- a/drivers/shared/executor/executor_linux_test.go +++ b/drivers/shared/executor/executor_linux_test.go @@ -628,27 +628,40 @@ func TestExecutor_Capabilities(t *testing.T) { testutil.ExecCompatible(t) cases := []struct { - user string - caps string + user string + capAdd []string + capDrop []string + capsExpected string }{ { user: "nobody", - caps: ` -CapInh: 0000000000000000 -CapPrm: 0000000000000000 -CapEff: 0000000000000000 + capsExpected: ` +CapInh: 00000000a80405fb +CapPrm: 00000000a80405fb +CapEff: 00000000a80405fb CapBnd: 00000000a80405fb -CapAmb: 0000000000000000`, +CapAmb: 00000000a80405fb`, }, { user: "root", - caps: ` + capsExpected: ` CapInh: 0000000000000000 CapPrm: 0000003fffffffff CapEff: 0000003fffffffff CapBnd: 0000003fffffffff CapAmb: 0000000000000000`, }, + { + user: "nobody", + capDrop: []string{"all"}, + capAdd: []string{"net_bind_service"}, + capsExpected: ` +CapInh: 0000000000000400 +CapPrm: 0000000000000400 +CapEff: 0000000000000400 +CapBnd: 0000000000000400 +CapAmb: 0000000000000400`, + }, } for _, c := range cases { @@ -662,7 +675,17 @@ CapAmb: 0000000000000000`, execCmd.ResourceLimits = true execCmd.Cmd = "/bin/bash" execCmd.Args = []string{"-c", "cat /proc/$$/status"} - execCmd.Capabilities = capabilities.NomadDefaults().Slice(true) + + capsBasis := capabilities.NomadDefaults() + capsAllowed := capsBasis.Slice(true) + if c.capDrop != nil || c.capAdd != nil { + calcCaps, err := capabilities.Calculate( + capsBasis, capsAllowed, c.capAdd, c.capDrop) + require.NoError(t, err) + execCmd.Capabilities = calcCaps + } else { + execCmd.Capabilities = capsAllowed + } executor := NewExecutorWithIsolation(testlog.HCLogger(t)) defer executor.Shutdown("SIGKILL", 0) @@ -690,7 +713,7 @@ CapAmb: 0000000000000000`, return s } - expected := canonical(c.caps) + expected := canonical(c.capsExpected) tu.WaitForResult(func() (bool, error) { output := canonical(testExecCmd.stdout.String()) if !strings.Contains(output, expected) {