Set ambient capabilities where supported

Since Linux 4.3 ambient capabilities are available. If set these allow unprivileged child
processes to inherit capabilities, while at present there is no means to set capabilities
on non root processes, other than via filesystem capabilities which are not usually
supported in image formats.

With ambient capabilities non root processes can be given capabilities as well, and so
the main reason to use root in containers goes away, and capabilities work as expected.

The code falls back to the existing behaviour if ambient capabilities are not supported.

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack 2016-09-28 09:13:56 +01:00
parent 1359131f4a
commit 4e179bddca
6 changed files with 71 additions and 16 deletions

2
Godeps/Godeps.json generated
View File

@ -67,7 +67,7 @@
},
{
"ImportPath": "github.com/syndtr/gocapability/capability",
"Rev": "2c00daeb6c3b45114c80ac44119e7b8801fdd852"
"Rev": "e7cb7fa329f456b3855136a2642b197bad7366ba"
},
{
"ImportPath": "github.com/vishvananda/netlink",

View File

@ -10,42 +10,42 @@ package capability
type Capabilities interface {
// Get check whether a capability present in the given
// capabilities set. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE or BOUNDING.
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Get(which CapType, what Cap) bool
// Empty check whether all capability bits of the given capabilities
// set are zero. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE or BOUNDING.
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Empty(which CapType) bool
// Full check whether all capability bits of the given capabilities
// set are one. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE or BOUNDING.
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Full(which CapType) bool
// Set sets capabilities of the given capabilities sets. The
// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
// PERMITTED, INHERITABLE or BOUNDING.
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Set(which CapType, caps ...Cap)
// Unset unsets capabilities of the given capabilities sets. The
// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
// PERMITTED, INHERITABLE or BOUNDING.
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Unset(which CapType, caps ...Cap)
// Fill sets all bits of the given capabilities kind to one. The
// 'kind' value should be one or combination (OR'ed) of CAPS or
// BOUNDS.
// 'kind' value should be one or combination (OR'ed) of CAPS,
// BOUNDS or AMBS.
Fill(kind CapType)
// Clear sets all bits of the given capabilities kind to zero. The
// 'kind' value should be one or combination (OR'ed) of CAPS or
// BOUNDS.
// 'kind' value should be one or combination (OR'ed) of CAPS,
// BOUNDS or AMBS.
Clear(kind CapType)
// String return current capabilities state of the given capabilities
// set as string. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE or BOUNDING.
// PERMITTED, INHERITABLE BOUNDING or AMBIENT
StringCap(which CapType) string
// String return current capabilities state as string.

View File

@ -235,9 +235,10 @@ func (c *capsV1) Apply(kind CapType) error {
}
type capsV3 struct {
hdr capHeader
data [2]capData
bounds [2]uint32
hdr capHeader
data [2]capData
bounds [2]uint32
ambient [2]uint32
}
func (c *capsV3) Get(which CapType, what Cap) bool {
@ -256,6 +257,8 @@ func (c *capsV3) Get(which CapType, what Cap) bool {
return (1<<uint(what))&c.data[i].inheritable != 0
case BOUNDING:
return (1<<uint(what))&c.bounds[i] != 0
case AMBIENT:
return (1<<uint(what))&c.ambient[i] != 0
}
return false
@ -275,6 +278,9 @@ func (c *capsV3) getData(which CapType, dest []uint32) {
case BOUNDING:
dest[0] = c.bounds[0]
dest[1] = c.bounds[1]
case AMBIENT:
dest[0] = c.ambient[0]
dest[1] = c.ambient[1]
}
}
@ -313,6 +319,9 @@ func (c *capsV3) Set(which CapType, caps ...Cap) {
if which&BOUNDING != 0 {
c.bounds[i] |= 1 << uint(what)
}
if which&AMBIENT != 0 {
c.ambient[i] |= 1 << uint(what)
}
}
}
@ -336,6 +345,9 @@ func (c *capsV3) Unset(which CapType, caps ...Cap) {
if which&BOUNDING != 0 {
c.bounds[i] &= ^(1 << uint(what))
}
if which&AMBIENT != 0 {
c.ambient[i] &= ^(1 << uint(what))
}
}
}
@ -353,6 +365,10 @@ func (c *capsV3) Fill(kind CapType) {
c.bounds[0] = 0xffffffff
c.bounds[1] = 0xffffffff
}
if kind&AMBS == AMBS {
c.ambient[0] = 0xffffffff
c.ambient[1] = 0xffffffff
}
}
func (c *capsV3) Clear(kind CapType) {
@ -369,6 +385,10 @@ func (c *capsV3) Clear(kind CapType) {
c.bounds[0] = 0
c.bounds[1] = 0
}
if kind&AMBS == AMBS {
c.ambient[0] = 0
c.ambient[1] = 0
}
}
func (c *capsV3) StringCap(which CapType) (ret string) {
@ -410,6 +430,10 @@ func (c *capsV3) Load() (err error) {
fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0])
break
}
if strings.HasPrefix(line, "CapA") {
fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0])
break
}
}
f.Close()
@ -442,7 +466,25 @@ func (c *capsV3) Apply(kind CapType) (err error) {
}
if kind&CAPS == CAPS {
return capset(&c.hdr, &c.data[0])
err = capset(&c.hdr, &c.data[0])
if err != nil {
return
}
}
if kind&AMBS == AMBS {
for i := Cap(0); i <= CAP_LAST_CAP; i++ {
action := pr_CAP_AMBIENT_LOWER
if c.Get(AMBIENT, i) {
action = pr_CAP_AMBIENT_RAISE
}
err := prctl(pr_CAP_AMBIENT, action, uintptr(i), 0, 0)
// Ignore EINVAL as not supported on kernels before 4.3
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINVAL {
err = nil
continue
}
}
}
return

View File

@ -20,6 +20,8 @@ func (c CapType) String() string {
return "bounding"
case CAPS:
return "caps"
case AMBIENT:
return "ambient"
}
return "unknown"
}
@ -29,9 +31,11 @@ const (
PERMITTED
INHERITABLE
BOUNDING
AMBIENT
CAPS = EFFECTIVE | PERMITTED | INHERITABLE
BOUNDS = BOUNDING
AMBS = AMBIENT
)
//go:generate go run enumgen/gen.go

View File

@ -38,6 +38,15 @@ func capset(hdr *capHeader, data *capData) (err error) {
return
}
// not yet in syscall
const (
pr_CAP_AMBIENT = 47
pr_CAP_AMBIENT_IS_SET = uintptr(1)
pr_CAP_AMBIENT_RAISE = uintptr(2)
pr_CAP_AMBIENT_LOWER = uintptr(3)
pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4)
)
func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
if e1 != 0 {

View File

@ -10,7 +10,7 @@ import (
"github.com/syndtr/gocapability/capability"
)
const allCapabilityTypes = capability.CAPS | capability.BOUNDS
const allCapabilityTypes = capability.CAPS | capability.BOUNDS | capability.AMBS
var capabilityMap map[string]capability.Cap