drivers/exec: Fix handling of capabilities for unprivileged tasks (#16643)
Currently, the `exec` driver is only setting the Bounding set, which is not sufficient to actually enable the requisite capabilities for the task process. In order for the capabilities to survive `execve` performed by libcontainer, the `Permitted`, `Inheritable`, and `Ambient` sets must also be set. Per CAPABILITIES (7): > Ambient: This is a set of capabilities that are preserved across an > execve(2) of a program that is not privileged. The ambient capability > set obeys the invariant that no capability can ever be ambient if it > is not both permitted and inheritable.
This commit is contained in:
parent
17fd1a2e35
commit
11a9bb6ce7
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
driver/exec: Fixed a bug where `cap_drop` and `cap_add` would not expand capabilities
|
||||
```
|
|
@ -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,
|
||||
Permitted: command.Capabilities,
|
||||
Inheritable: command.Capabilities,
|
||||
Ambient: command.Capabilities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -629,26 +629,39 @@ func TestExecutor_Capabilities(t *testing.T) {
|
|||
|
||||
cases := []struct {
|
||||
user string
|
||||
caps 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) {
|
||||
|
|
Loading…
Reference in New Issue