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", "ImportPath": "github.com/syndtr/gocapability/capability",
"Rev": "2c00daeb6c3b45114c80ac44119e7b8801fdd852" "Rev": "e7cb7fa329f456b3855136a2642b197bad7366ba"
}, },
{ {
"ImportPath": "github.com/vishvananda/netlink", "ImportPath": "github.com/vishvananda/netlink",

View File

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

View File

@ -235,9 +235,10 @@ func (c *capsV1) Apply(kind CapType) error {
} }
type capsV3 struct { type capsV3 struct {
hdr capHeader hdr capHeader
data [2]capData data [2]capData
bounds [2]uint32 bounds [2]uint32
ambient [2]uint32
} }
func (c *capsV3) Get(which CapType, what Cap) bool { 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 return (1<<uint(what))&c.data[i].inheritable != 0
case BOUNDING: case BOUNDING:
return (1<<uint(what))&c.bounds[i] != 0 return (1<<uint(what))&c.bounds[i] != 0
case AMBIENT:
return (1<<uint(what))&c.ambient[i] != 0
} }
return false return false
@ -275,6 +278,9 @@ func (c *capsV3) getData(which CapType, dest []uint32) {
case BOUNDING: case BOUNDING:
dest[0] = c.bounds[0] dest[0] = c.bounds[0]
dest[1] = c.bounds[1] 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 { if which&BOUNDING != 0 {
c.bounds[i] |= 1 << uint(what) 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 { if which&BOUNDING != 0 {
c.bounds[i] &= ^(1 << uint(what)) 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[0] = 0xffffffff
c.bounds[1] = 0xffffffff c.bounds[1] = 0xffffffff
} }
if kind&AMBS == AMBS {
c.ambient[0] = 0xffffffff
c.ambient[1] = 0xffffffff
}
} }
func (c *capsV3) Clear(kind CapType) { func (c *capsV3) Clear(kind CapType) {
@ -369,6 +385,10 @@ func (c *capsV3) Clear(kind CapType) {
c.bounds[0] = 0 c.bounds[0] = 0
c.bounds[1] = 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) { 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]) fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0])
break break
} }
if strings.HasPrefix(line, "CapA") {
fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0])
break
}
} }
f.Close() f.Close()
@ -442,7 +466,25 @@ func (c *capsV3) Apply(kind CapType) (err error) {
} }
if kind&CAPS == CAPS { 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 return

View File

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

View File

@ -38,6 +38,15 @@ func capset(hdr *capHeader, data *capData) (err error) {
return 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) { func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0) _, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
if e1 != 0 { if e1 != 0 {

View File

@ -10,7 +10,7 @@ import (
"github.com/syndtr/gocapability/capability" "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 var capabilityMap map[string]capability.Cap