package caps // import "github.com/docker/docker/oci/caps" import ( "fmt" "strings" "github.com/docker/docker/errdefs" ) var ( allCaps []string // knownCapabilities is a map of all known capabilities, using capability // name as index. Nil values indicate that the capability is known, but either // not supported by the Kernel, or not available in the current environment, // for example, when running Docker-in-Docker with restricted capabilities. // // Capabilities are 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 knownCaps map[string]*struct{} ) // GetAllCapabilities returns all capabilities that are availeble in the current // environment. func GetAllCapabilities() []string { initCaps() return allCaps } // knownCapabilities returns a map of all known capabilities, using capability // name as index. Nil values indicate that the capability is known, but either // not supported by the Kernel, or not available in the current environment, for // example, when running Docker-in-Docker with restricted capabilities. func knownCapabilities() map[string]*struct{} { initCaps() return knownCaps } // inSlice tests whether a string is contained in a slice of strings or not. func inSlice(slice []string, s string) bool { for _, ss := range slice { if s == ss { return true } } return false } const allCapabilities = "ALL" // NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities // by upper-casing them, and adding a CAP_ prefix (if not yet present). // // This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop. func NormalizeLegacyCapabilities(caps []string) ([]string, error) { var ( normalized []string capabilityList = knownCapabilities() ) for _, c := range caps { c = strings.ToUpper(c) if c == allCapabilities { normalized = append(normalized, c) continue } if !strings.HasPrefix(c, "CAP_") { c = "CAP_" + c } if v, ok := capabilityList[c]; !ok { return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c)) } else if v == nil { return nil, errdefs.InvalidParameter(fmt.Errorf("capability not supported by your kernel or not available in the current environment: %q", c)) } normalized = append(normalized, c) } return normalized, nil } // TweakCapabilities tweaks capabilities by adding, dropping, or overriding // capabilities in the basics capabilities list. func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) { switch { case privileged: // Privileged containers get all capabilities return GetAllCapabilities(), nil case len(adds) == 0 && len(drops) == 0: // Nothing to tweak; we're done return basics, nil } capDrop, err := NormalizeLegacyCapabilities(drops) if err != nil { return nil, err } capAdd, err := NormalizeLegacyCapabilities(adds) if err != nil { return nil, err } var caps []string switch { case inSlice(capAdd, allCapabilities): // Add all capabilities except ones on capDrop for _, c := range GetAllCapabilities() { if !inSlice(capDrop, c) { caps = append(caps, c) } } case inSlice(capDrop, allCapabilities): // "Drop" all capabilities; use what's in capAdd instead caps = capAdd default: // First drop some capabilities for _, c := range basics { if !inSlice(capDrop, c) { caps = append(caps, c) } } // Then add the list of capabilities from capAdd caps = append(caps, capAdd...) } return caps, nil }