skopeo/vendor/github.com/containers/common/pkg/capabilities/capabilities.go
tomsweeneyredhat 34f0644177 Bump c/common to v0.60.0
As the title says.  In preparation of Skopeo v1.17.0 to go
out with Podman v5.3, this gets the c/* projects to:

containers/storage:  v1.56.0
containers/image:    v5.33.0
containers/common:   v0.61.0

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2024-11-12 17:58:45 -05:00

198 lines
4.7 KiB
Go

package capabilities
// Copyright 2013-2018 Docker, Inc.
// NOTE: this package has been copied from github.com/docker/docker but been
// changed significantly to fit the needs of libpod.
import (
"errors"
"fmt"
"slices"
"strings"
"sync"
"github.com/moby/sys/capability"
)
var (
// ErrUnknownCapability is thrown when an unknown capability is processed.
ErrUnknownCapability = errors.New("unknown capability")
// ContainerImageLabels - label can indicate the required
// capabilities required by containers to run the container image.
ContainerImageLabels = []string{"io.containers.capabilities"}
)
// All is a special value used to add/drop all known capabilities.
// Useful on the CLI for `--cap-add=all` etc.
const All = "ALL"
func capName(c capability.Cap) string {
return "CAP_" + strings.ToUpper(c.String())
}
// capStrList returns all capabilities supported by the currently running kernel,
// or an error if the list can not be obtained.
var capStrList = sync.OnceValues(func() ([]string, error) {
list, err := capability.ListSupported()
if err != nil {
return nil, err
}
caps := make([]string, len(list))
for i, c := range list {
caps[i] = capName(c)
}
slices.Sort(caps)
return caps, nil
})
// BoundingSet returns the capabilities in the current bounding set.
func BoundingSet() ([]string, error) {
return boundingSet()
}
var boundingSet = sync.OnceValues(func() ([]string, error) {
currentCaps, err := capability.NewPid2(0)
if err != nil {
return nil, err
}
err = currentCaps.Load()
if err != nil {
return nil, err
}
list, err := capability.ListSupported()
if err != nil {
return nil, err
}
var r []string
for _, c := range list {
if !currentCaps.Get(capability.BOUNDING, c) {
continue
}
r = append(r, capName(c))
}
slices.Sort(r)
return r, nil
})
// AllCapabilities returns all capabilities supported by the running kernel.
func AllCapabilities() []string {
list, _ := capStrList()
return list
}
// NormalizeCapabilities normalizes caps by adding a "CAP_" prefix (if not yet
// present).
func NormalizeCapabilities(caps []string) ([]string, error) {
all, err := capStrList()
if err != nil {
return nil, err
}
normalized := make([]string, 0, len(caps))
for _, c := range caps {
c = strings.ToUpper(c)
if c == All {
normalized = append(normalized, c)
continue
}
if !strings.HasPrefix(c, "CAP_") {
c = "CAP_" + c
}
if !slices.Contains(all, c) {
return nil, fmt.Errorf("%q: %w", c, ErrUnknownCapability)
}
normalized = append(normalized, c)
}
slices.Sort(normalized)
return normalized, nil
}
// ValidateCapabilities validates if caps only contains valid capabilities.
func ValidateCapabilities(caps []string) error {
all, err := capStrList()
if err != nil {
return err
}
for _, c := range caps {
if !slices.Contains(all, c) {
return fmt.Errorf("%q: %w", c, ErrUnknownCapability)
}
}
return nil
}
// MergeCapabilities computes a set of capabilities by adding capabilities
// to or dropping them from base.
//
// Note that:
// "ALL" in capAdd adds returns known capabilities
// "All" in capDrop returns only the capabilities specified in capAdd
func MergeCapabilities(base, adds, drops []string) ([]string, error) {
// Normalize the base capabilities
base, err := NormalizeCapabilities(base)
if err != nil {
return nil, err
}
if len(adds) == 0 && len(drops) == 0 {
// Nothing to tweak; we're done
return base, nil
}
capDrop, err := NormalizeCapabilities(drops)
if err != nil {
return nil, err
}
capAdd, err := NormalizeCapabilities(adds)
if err != nil {
return nil, err
}
if slices.Contains(capDrop, All) {
if slices.Contains(capAdd, All) {
return nil, errors.New("adding all caps and removing all caps not allowed")
}
// "Drop" all capabilities; return what's in capAdd instead
slices.Sort(capAdd)
return capAdd, nil
}
if slices.Contains(capAdd, All) {
base, err = BoundingSet()
if err != nil {
return nil, err
}
capAdd = []string{}
} else {
for _, add := range capAdd {
if slices.Contains(capDrop, add) {
return nil, fmt.Errorf("capability %q cannot be dropped and added", add)
}
}
}
for _, drop := range capDrop {
if slices.Contains(capAdd, drop) {
return nil, fmt.Errorf("capability %q cannot be dropped and added", drop)
}
}
caps := make([]string, 0, len(base)+len(capAdd))
// Drop any capabilities in capDrop that are in base
for _, cap := range base {
if slices.Contains(capDrop, cap) {
continue
}
caps = append(caps, cap)
}
// Add any capabilities in capAdd that are not in base
for _, cap := range capAdd {
if slices.Contains(base, cap) {
continue
}
caps = append(caps, cap)
}
slices.Sort(caps)
return caps, nil
}