Better capability/namespace management
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume.charmes@docker.com> (github: creack)
This commit is contained in:
parent
6daf56799f
commit
357ca32831
4 changed files with 119 additions and 87 deletions
|
@ -6,24 +6,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var capMap = map[libcontainer.Capability]capability.Cap{
|
|
||||||
libcontainer.CAP_SETPCAP: capability.CAP_SETPCAP,
|
|
||||||
libcontainer.CAP_SYS_MODULE: capability.CAP_SYS_MODULE,
|
|
||||||
libcontainer.CAP_SYS_RAWIO: capability.CAP_SYS_RAWIO,
|
|
||||||
libcontainer.CAP_SYS_PACCT: capability.CAP_SYS_PACCT,
|
|
||||||
libcontainer.CAP_SYS_ADMIN: capability.CAP_SYS_ADMIN,
|
|
||||||
libcontainer.CAP_SYS_NICE: capability.CAP_SYS_NICE,
|
|
||||||
libcontainer.CAP_SYS_RESOURCE: capability.CAP_SYS_RESOURCE,
|
|
||||||
libcontainer.CAP_SYS_TIME: capability.CAP_SYS_TIME,
|
|
||||||
libcontainer.CAP_SYS_TTY_CONFIG: capability.CAP_SYS_TTY_CONFIG,
|
|
||||||
libcontainer.CAP_MKNOD: capability.CAP_MKNOD,
|
|
||||||
libcontainer.CAP_AUDIT_WRITE: capability.CAP_AUDIT_WRITE,
|
|
||||||
libcontainer.CAP_AUDIT_CONTROL: capability.CAP_AUDIT_CONTROL,
|
|
||||||
libcontainer.CAP_MAC_OVERRIDE: capability.CAP_MAC_OVERRIDE,
|
|
||||||
libcontainer.CAP_MAC_ADMIN: capability.CAP_MAC_ADMIN,
|
|
||||||
libcontainer.CAP_NET_ADMIN: capability.CAP_NET_ADMIN,
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropCapabilities drops capabilities for the current process based
|
// DropCapabilities drops capabilities for the current process based
|
||||||
// on the container's configuration.
|
// on the container's configuration.
|
||||||
func DropCapabilities(container *libcontainer.Container) error {
|
func DropCapabilities(container *libcontainer.Container) error {
|
||||||
|
@ -45,7 +27,7 @@ func DropCapabilities(container *libcontainer.Container) error {
|
||||||
func getCapabilities(container *libcontainer.Container) []capability.Cap {
|
func getCapabilities(container *libcontainer.Container) []capability.Cap {
|
||||||
drop := []capability.Cap{}
|
drop := []capability.Cap{}
|
||||||
for _, c := range container.Capabilities {
|
for _, c := range container.Capabilities {
|
||||||
drop = append(drop, capMap[c])
|
drop = append(drop, c.Value)
|
||||||
}
|
}
|
||||||
return drop
|
return drop
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
|
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
|
||||||
func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
|
func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
|
||||||
for _, ns := range container.Namespaces {
|
for _, ns := range container.Namespaces {
|
||||||
if err := system.Unshare(namespaceMap[ns]); err != nil {
|
if err := system.Unshare(ns.Value); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,7 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s
|
||||||
|
|
||||||
// if the container has a new pid and mount namespace we need to
|
// if the container has a new pid and mount namespace we need to
|
||||||
// remount proc and sys to pick up the changes
|
// remount proc and sys to pick up the changes
|
||||||
if container.Namespaces.Contains(libcontainer.CLONE_NEWNS) &&
|
if container.Namespaces.Contains("CLONE_NEWNS") && container.Namespaces.Contains("CLONE_NEWPID") {
|
||||||
container.Namespaces.Contains(libcontainer.CLONE_NEWPID) {
|
|
||||||
pid, err := system.Fork()
|
pid, err := system.Fork()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -84,7 +83,7 @@ dropAndExec:
|
||||||
func (ns *linuxNs) getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
|
func (ns *linuxNs) getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
|
||||||
fds := make([]uintptr, len(container.Namespaces))
|
fds := make([]uintptr, len(container.Namespaces))
|
||||||
for i, ns := range container.Namespaces {
|
for i, ns := range container.Namespaces {
|
||||||
f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", namespaceFileMap[ns]), os.O_RDONLY, 0)
|
f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", ns.File), os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fds, err
|
return fds, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,35 +2,13 @@ package nsinit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var namespaceMap = map[libcontainer.Namespace]int{
|
|
||||||
libcontainer.CLONE_NEWNS: syscall.CLONE_NEWNS,
|
|
||||||
libcontainer.CLONE_NEWUTS: syscall.CLONE_NEWUTS,
|
|
||||||
libcontainer.CLONE_NEWIPC: syscall.CLONE_NEWIPC,
|
|
||||||
libcontainer.CLONE_NEWUSER: syscall.CLONE_NEWUSER,
|
|
||||||
libcontainer.CLONE_NEWPID: syscall.CLONE_NEWPID,
|
|
||||||
libcontainer.CLONE_NEWNET: syscall.CLONE_NEWNET,
|
|
||||||
}
|
|
||||||
|
|
||||||
// namespaceFileMap is used to convert the libcontainer types
|
|
||||||
// into the names of the files located in /proc/<pid>/ns/* for
|
|
||||||
// each namespace
|
|
||||||
var namespaceFileMap = map[libcontainer.Namespace]string{
|
|
||||||
libcontainer.CLONE_NEWNS: "mnt",
|
|
||||||
libcontainer.CLONE_NEWUTS: "uts",
|
|
||||||
libcontainer.CLONE_NEWIPC: "ipc",
|
|
||||||
libcontainer.CLONE_NEWUSER: "user",
|
|
||||||
libcontainer.CLONE_NEWPID: "pid",
|
|
||||||
libcontainer.CLONE_NEWNET: "net",
|
|
||||||
}
|
|
||||||
|
|
||||||
// getNamespaceFlags parses the container's Namespaces options to set the correct
|
// getNamespaceFlags parses the container's Namespaces options to set the correct
|
||||||
// flags on clone, unshare, and setns
|
// flags on clone, unshare, and setns
|
||||||
func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
|
func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
flag |= namespaceMap[ns]
|
flag |= ns.Value
|
||||||
}
|
}
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,131 @@
|
||||||
package libcontainer
|
package libcontainer
|
||||||
|
|
||||||
// These constants are defined as string types so that
|
import (
|
||||||
// it is clear when adding the configuration in config files
|
"encoding/json"
|
||||||
// instead of using ints or other types
|
"errors"
|
||||||
const (
|
"github.com/syndtr/gocapability/capability"
|
||||||
CAP_SETPCAP Capability = "SETPCAP"
|
"os"
|
||||||
CAP_SYS_MODULE Capability = "SYS_MODULE"
|
"syscall"
|
||||||
CAP_SYS_RAWIO Capability = "SYS_RAWIO"
|
)
|
||||||
CAP_SYS_PACCT Capability = "SYS_PACCT"
|
|
||||||
CAP_SYS_ADMIN Capability = "SYS_ADMIN"
|
|
||||||
CAP_SYS_NICE Capability = "SYS_NICE"
|
|
||||||
CAP_SYS_RESOURCE Capability = "SYS_RESOURCE"
|
|
||||||
CAP_SYS_TIME Capability = "SYS_TIME"
|
|
||||||
CAP_SYS_TTY_CONFIG Capability = "SYS_TTY_CONFIG"
|
|
||||||
CAP_MKNOD Capability = "MKNOD"
|
|
||||||
CAP_AUDIT_WRITE Capability = "AUDIT_WRITE"
|
|
||||||
CAP_AUDIT_CONTROL Capability = "AUDIT_CONTROL"
|
|
||||||
CAP_MAC_OVERRIDE Capability = "MAC_OVERRIDE"
|
|
||||||
CAP_MAC_ADMIN Capability = "MAC_ADMIN"
|
|
||||||
CAP_NET_ADMIN Capability = "NET_ADMIN"
|
|
||||||
|
|
||||||
CLONE_NEWNS Namespace = "NEWNS" // mount
|
var (
|
||||||
CLONE_NEWUTS Namespace = "NEWUTS" // utsname
|
ErrUnkownNamespace error = errors.New("Unkown namespace")
|
||||||
CLONE_NEWIPC Namespace = "NEWIPC" // ipc
|
)
|
||||||
CLONE_NEWUSER Namespace = "NEWUSER" // user
|
|
||||||
CLONE_NEWPID Namespace = "NEWPID" // pid
|
// namespaceList is used to convert the libcontainer types
|
||||||
CLONE_NEWNET Namespace = "NEWNET" // network
|
// into the names of the files located in /proc/<pid>/ns/* for
|
||||||
|
// each namespace
|
||||||
|
var (
|
||||||
|
namespaceList = Namespaces{
|
||||||
|
{Key: "NEWNS", Value: syscall.CLONE_NEWNS, File: "mnt"},
|
||||||
|
{Key: "NEWUTS", Value: syscall.CLONE_NEWUTS, File: "uts"},
|
||||||
|
{Key: "NEWIPC", Value: syscall.CLONE_NEWIPC, File: "ipc"},
|
||||||
|
{Key: "NEWUSER", Value: syscall.CLONE_NEWUSER, File: "user"},
|
||||||
|
{Key: "NEWPID", Value: syscall.CLONE_NEWPID, File: "pid"},
|
||||||
|
{Key: "NEWNET", Value: syscall.CLONE_NEWNET, File: "net"},
|
||||||
|
}
|
||||||
|
capabilityList = Capabilities{
|
||||||
|
{Key: "SETPCAP", Value: capability.CAP_SETPCAP},
|
||||||
|
{Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE},
|
||||||
|
{Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO},
|
||||||
|
{Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT},
|
||||||
|
{Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN},
|
||||||
|
{Key: "SYS_NICE", Value: capability.CAP_SYS_NICE},
|
||||||
|
{Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE},
|
||||||
|
{Key: "SYS_TIME", Value: capability.CAP_SYS_TIME},
|
||||||
|
{Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG},
|
||||||
|
{Key: "MKNOD", Value: capability.CAP_MKNOD},
|
||||||
|
{Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE},
|
||||||
|
{Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL},
|
||||||
|
{Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE},
|
||||||
|
{Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN},
|
||||||
|
{Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Namespace string
|
Namespace struct {
|
||||||
Namespaces []Namespace
|
Key string
|
||||||
Capability string
|
Value int
|
||||||
Capabilities []Capability
|
File string
|
||||||
|
}
|
||||||
|
Namespaces []*Namespace
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (ns *Namespace) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(ns.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *Namespace) UnmarshalJSON(src []byte) error {
|
||||||
|
var nsName string
|
||||||
|
if err := json.Unmarshal(src, &nsName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ret := GetNamespace(nsName)
|
||||||
|
if ret == nil {
|
||||||
|
return ErrUnkownNamespace
|
||||||
|
}
|
||||||
|
*ns = *ret
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNamespace(key string) *Namespace {
|
||||||
|
for _, ns := range namespaceList {
|
||||||
|
if ns.Key == key {
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if os.Getenv("DEBUG") != "" {
|
||||||
|
panic("Unreachable: Namespace not found")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Contains returns true if the specified Namespace is
|
// Contains returns true if the specified Namespace is
|
||||||
// in the slice
|
// in the slice
|
||||||
func (n Namespaces) Contains(ns Namespace) bool {
|
func (n Namespaces) Contains(ns string) bool {
|
||||||
for _, nns := range n {
|
return GetNamespace(ns) != nil
|
||||||
if nns == ns {
|
}
|
||||||
return true
|
|
||||||
|
type (
|
||||||
|
Capability struct {
|
||||||
|
Key string
|
||||||
|
Value capability.Cap
|
||||||
|
}
|
||||||
|
Capabilities []*Capability
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ns *Capability) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(ns.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *Capability) UnmarshalJSON(src []byte) error {
|
||||||
|
var capName string
|
||||||
|
if err := json.Unmarshal(src, &capName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ret := GetCapability(capName)
|
||||||
|
if ret == nil {
|
||||||
|
return ErrUnkownNamespace
|
||||||
|
}
|
||||||
|
*ns = *ret
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCapability(key string) *Capability {
|
||||||
|
for _, capp := range capabilityList {
|
||||||
|
if capp.Key == key {
|
||||||
|
return capp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
if os.Getenv("DEBUG") != "" {
|
||||||
|
panic("Unreachable: Namespace not found")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains returns true if the specified Capability is
|
// Contains returns true if the specified Capability is
|
||||||
// in the slice
|
// in the slice
|
||||||
func (c Capabilities) Contains(capp Capability) bool {
|
func (c Capabilities) Contains(capp string) bool {
|
||||||
for _, cc := range c {
|
return GetCapability(capp) != nil
|
||||||
if cc == capp {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue