package caps // import "github.com/docker/docker/daemon/caps" import ( "fmt" "strings" "github.com/syndtr/gocapability/capability" ) var capabilityList Capabilities func init() { last := capability.CAP_LAST_CAP // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap if last == capability.Cap(63) { last = capability.CAP_BLOCK_SUSPEND } for _, cap := range capability.List() { if cap > last { continue } capabilityList = append(capabilityList, &CapabilityMapping{ Key: "CAP_" + strings.ToUpper(cap.String()), Value: cap, }, ) } } type ( // CapabilityMapping maps linux capability name to its value of capability.Cap type // Capabilities is one of the security systems in Linux Security Module (LSM) // framework provided by the kernel. // For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html CapabilityMapping struct { Key string `json:"key,omitempty"` Value capability.Cap `json:"value,omitempty"` } // Capabilities contains all CapabilityMapping Capabilities []*CapabilityMapping ) // String returns of CapabilityMapping func (c *CapabilityMapping) String() string { return c.Key } // GetCapability returns CapabilityMapping which contains specific key func GetCapability(key string) *CapabilityMapping { for _, capp := range capabilityList { if capp.Key == key { cpy := *capp return &cpy } } return nil } // GetAllCapabilities returns all of the capabilities func GetAllCapabilities() []string { output := make([]string, len(capabilityList)) for i, capability := range capabilityList { output[i] = capability.String() } return output } // inSlice tests whether a string is contained in a slice of strings or not. // Comparison is case insensitive func inSlice(slice []string, s string) bool { for _, ss := range slice { if strings.ToLower(s) == strings.ToLower(ss) { return true } } return false } // TweakCapabilities can tweak capabilities by adding or dropping capabilities // based on the basics capabilities. func TweakCapabilities(basics, adds, drops []string) ([]string, error) { var ( newCaps []string allCaps = GetAllCapabilities() ) // FIXME(tonistiigi): docker format is without CAP_ prefix, oci is with prefix // Currently they are mixed in here. We should do conversion in one place. // look for invalid cap in the drop list for _, cap := range drops { if strings.ToLower(cap) == "all" { continue } if !inSlice(allCaps, "CAP_"+cap) { return nil, fmt.Errorf("Unknown capability drop: %q", cap) } } // handle --cap-add=all if inSlice(adds, "all") { basics = allCaps } if !inSlice(drops, "all") { for _, cap := range basics { // skip `all` already handled above if strings.ToLower(cap) == "all" { continue } // if we don't drop `all`, add back all the non-dropped caps if !inSlice(drops, cap[4:]) { newCaps = append(newCaps, strings.ToUpper(cap)) } } } for _, cap := range adds { // skip `all` already handled above if strings.ToLower(cap) == "all" { continue } cap = "CAP_" + cap if !inSlice(allCaps, cap) { return nil, fmt.Errorf("Unknown capability to add: %q", cap) } // add cap if not already in the list if !inSlice(newCaps, cap) { newCaps = append(newCaps, strings.ToUpper(cap)) } } return newCaps, nil }