diff --git a/libcontainer/capabilities/capabilities.go b/libcontainer/capabilities/capabilities.go index 7e938d3f505..fa1949ac48b 100644 --- a/libcontainer/capabilities/capabilities.go +++ b/libcontainer/capabilities/capabilities.go @@ -5,7 +5,6 @@ package capabilities import ( "sort" - "strings" "github.com/opencontainers/runc/libcontainer/configs" "github.com/sirupsen/logrus" @@ -14,25 +13,12 @@ import ( const allCapabilityTypes = capability.CAPS | capability.BOUNDING | capability.AMBIENT -var ( - capabilityMap map[string]capability.Cap - capTypes = []capability.CapType{ - capability.BOUNDING, - capability.PERMITTED, - capability.INHERITABLE, - capability.EFFECTIVE, - capability.AMBIENT, - } -) - -func init() { - capabilityMap = make(map[string]capability.Cap, capability.CAP_LAST_CAP+1) - for _, c := range capability.List() { - if c > capability.CAP_LAST_CAP { - continue - } - capabilityMap["CAP_"+strings.ToUpper(c.String())] = c - } +var capTypes = []capability.CapType{ + capability.BOUNDING, + capability.PERMITTED, + capability.INHERITABLE, + capability.EFFECTIVE, + capability.AMBIENT, } // New creates a new Caps from the given Capabilities config. Unknown Capabilities @@ -69,6 +55,7 @@ func New(capConfig *configs.Capabilities) (*Caps, error) { // are not returned, but appended to unknownCaps. func capSlice(caps []string, unknownCaps map[string]struct{}) []capability.Cap { var out []capability.Cap + capabilityMap, _ := current() for _, c := range caps { if v, ok := capabilityMap[c]; !ok { unknownCaps[c] = struct{}{} @@ -102,7 +89,7 @@ func (c *Caps) ApplyBoundingSet() error { return c.pid.Apply(capability.BOUNDING) } -// Apply sets all the capabilities for the current process in the config. +// ApplyCaps sets all the capabilities for the current process in the config. func (c *Caps) ApplyCaps() error { c.pid.Clear(allCapabilityTypes) for _, g := range capTypes { diff --git a/libcontainer/capabilities/capabilities_linux.go b/libcontainer/capabilities/capabilities_linux.go new file mode 100644 index 00000000000..3e7fd88cb80 --- /dev/null +++ b/libcontainer/capabilities/capabilities_linux.go @@ -0,0 +1,105 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package capabilities + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/syndtr/gocapability/capability" +) + +// fromBitmap parses an uint64 bitmap into a capability map. Unknown cap numbers +// are ignored. +func fromBitmap(v uint64) map[string]capability.Cap { + res := make(map[string]capability.Cap, 63) + for i := 0; i <= 63; i++ { + if b := (v >> i) & 0x1; b == 0x1 { + c := capability.Cap(i) + if s := c.String(); s != "unknown" { + res["CAP_"+strings.ToUpper(s)] = c + } + } + } + return res +} + +// parseProcPIDStatus returns uint64 bitmap value from /proc//status file +func parseProcPIDStatus(r io.Reader) (map[capability.CapType]uint64, error) { + res := make(map[capability.CapType]uint64) + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + pair := strings.SplitN(line, ":", 2) + if len(pair) != 2 { + continue + } + k := strings.TrimSpace(pair[0]) + v := strings.TrimSpace(pair[1]) + switch k { + case "CapInh", "CapPrm", "CapEff", "CapBnd", "CapAmb": + ui64, err := strconv.ParseUint(v, 16, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse line %q", line) + } + switch k { + case "CapInh": + res[capability.INHERITABLE] = ui64 + case "CapPrm": + res[capability.PERMITTED] = ui64 + case "CapEff": + res[capability.EFFECTIVE] = ui64 + case "CapBnd": + res[capability.BOUNDING] = ui64 + case "CapAmb": + res[capability.AMBIENT] = ui64 + } + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return res, nil +} + +var ( + curCaps map[string]capability.Cap + curCapsErr error + curCapsOnce sync.Once +) + +// current returns a map of the effective known caps of the current process. +func current() (map[string]capability.Cap, error) { + curCapsOnce.Do(func() { + f, curCapsErr := os.Open("/proc/self/status") + if curCapsErr != nil { + return + } + defer f.Close() + caps, curCapsErr := parseProcPIDStatus(f) + if curCapsErr != nil { + return + } + curCaps = fromBitmap(caps[capability.EFFECTIVE]) + }) + return curCaps, curCapsErr +}