| // +build !windows |
| |
| package 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 <key> 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 |
| } |