diff --git a/go.mod b/go.mod index 98b23ab872d..8f61fb27151 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.4 github.com/docker/go-units v0.5.0 github.com/godbus/dbus/v5 v5.1.0 + github.com/kolyshkin/capability v0.1.1 github.com/moby/sys/mountinfo v0.7.1 github.com/moby/sys/user v0.1.0 github.com/mrunalp/fileutils v0.5.1 @@ -17,7 +18,6 @@ require ( github.com/opencontainers/selinux v1.11.0 github.com/seccomp/libseccomp-golang v0.10.0 github.com/sirupsen/logrus v1.9.3 - github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/urfave/cli v1.22.14 github.com/vishvananda/netlink v1.1.0 golang.org/x/net v0.24.0 diff --git a/go.sum b/go.sum index 13b88a4850c..0572f78f2d8 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kolyshkin/capability v0.1.1 h1:th2w1lW+eNsYjiHl5ID/vFbOb3mj8vjterkwzZbvhOs= +github.com/kolyshkin/capability v0.1.1/go.mod h1:LLvqGTUJOPNZaZd47EGYif+S7+CmFocn0v7gt9ue2pg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -60,8 +62,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= diff --git a/libcontainer/capabilities/capabilities.go b/libcontainer/capabilities/capabilities.go index 69884ef992a..ab68a33087d 100644 --- a/libcontainer/capabilities/capabilities.go +++ b/libcontainer/capabilities/capabilities.go @@ -6,16 +6,15 @@ import ( "sort" "strings" + "github.com/kolyshkin/capability" "github.com/opencontainers/runc/libcontainer/configs" "github.com/sirupsen/logrus" - "github.com/syndtr/gocapability/capability" ) const allCapabilityTypes = capability.CAPS | capability.BOUNDING | capability.AMBIENT var ( - capabilityMap map[string]capability.Cap - capTypes = []capability.CapType{ + capTypes = []capability.CapType{ capability.BOUNDING, capability.PERMITTED, capability.INHERITABLE, @@ -24,14 +23,19 @@ var ( } ) -func init() { - capabilityMap = make(map[string]capability.Cap, capability.CAP_LAST_CAP+1) +func getCapMap() (map[string]capability.Cap, error) { + lastcap, err := capability.LastCap() + if err != nil { + return nil, err + } + capabilityMap := make(map[string]capability.Cap, lastcap+1) for _, c := range capability.List() { - if c > capability.CAP_LAST_CAP { + if c > lastcap { continue } capabilityMap["CAP_"+strings.ToUpper(c.String())] = c } + return capabilityMap, nil } // KnownCapabilities returns the list of the known capabilities. @@ -53,14 +57,17 @@ func New(capConfig *configs.Capabilities) (*Caps, error) { err error c Caps ) - + cmap, err := getCapMap() + if err != nil { + return nil, err + } unknownCaps := make(map[string]struct{}) c.caps = map[capability.CapType][]capability.Cap{ - capability.BOUNDING: capSlice(capConfig.Bounding, unknownCaps), - capability.EFFECTIVE: capSlice(capConfig.Effective, unknownCaps), - capability.INHERITABLE: capSlice(capConfig.Inheritable, unknownCaps), - capability.PERMITTED: capSlice(capConfig.Permitted, unknownCaps), - capability.AMBIENT: capSlice(capConfig.Ambient, unknownCaps), + capability.BOUNDING: capSlice(capConfig.Bounding, cmap, unknownCaps), + capability.EFFECTIVE: capSlice(capConfig.Effective, cmap, unknownCaps), + capability.INHERITABLE: capSlice(capConfig.Inheritable, cmap, unknownCaps), + capability.PERMITTED: capSlice(capConfig.Permitted, cmap, unknownCaps), + capability.AMBIENT: capSlice(capConfig.Ambient, cmap, unknownCaps), } if c.pid, err = capability.NewPid2(0); err != nil { return nil, err @@ -77,10 +84,10 @@ func New(capConfig *configs.Capabilities) (*Caps, error) { // capSlice converts the slice of capability names in caps, to their numeric // equivalent, and returns them as a slice. Unknown or unavailable capabilities // are not returned, but appended to unknownCaps. -func capSlice(caps []string, unknownCaps map[string]struct{}) []capability.Cap { +func capSlice(caps []string, cmap map[string]capability.Cap, unknownCaps map[string]struct{}) []capability.Cap { var out []capability.Cap for _, c := range caps { - if v, ok := capabilityMap[c]; !ok { + if v, ok := cmap[c]; !ok { unknownCaps[c] = struct{}{} } else { out = append(out, v) diff --git a/libcontainer/capabilities/capabilities_linux_test.go b/libcontainer/capabilities/capabilities_linux_test.go index dfbb44b4a3f..2966ee23803 100644 --- a/libcontainer/capabilities/capabilities_linux_test.go +++ b/libcontainer/capabilities/capabilities_linux_test.go @@ -8,7 +8,7 @@ import ( "github.com/opencontainers/runc/libcontainer/configs" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" - "github.com/syndtr/gocapability/capability" + "github.com/kolyshkin/capability" ) func TestNew(t *testing.T) { diff --git a/vendor/github.com/kolyshkin/capability/.codespellrc b/vendor/github.com/kolyshkin/capability/.codespellrc new file mode 100644 index 00000000000..e874be5634b --- /dev/null +++ b/vendor/github.com/kolyshkin/capability/.codespellrc @@ -0,0 +1,3 @@ +[codespell] +skip = ./.git +ignore-words-list = nd diff --git a/vendor/github.com/kolyshkin/capability/.golangci.yml b/vendor/github.com/kolyshkin/capability/.golangci.yml new file mode 100644 index 00000000000..d775aadd6fa --- /dev/null +++ b/vendor/github.com/kolyshkin/capability/.golangci.yml @@ -0,0 +1,6 @@ +linters: + enable: + - unconvert + - unparam + - gofumpt + - errorlint diff --git a/vendor/github.com/kolyshkin/capability/CHANGELOG.md b/vendor/github.com/kolyshkin/capability/CHANGELOG.md new file mode 100644 index 00000000000..1b7c10b80df --- /dev/null +++ b/vendor/github.com/kolyshkin/capability/CHANGELOG.md @@ -0,0 +1,50 @@ +# Changelog +This file documents all notable changes made to this project since the initial fork. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.1] - 2024-08-01 + +This is a maintenance release, fixing a few minor issues. + +### Fixed + * Fixed future kernel compatibility, for real this time. (#11) + * Fixed [LastCap] to be a function. (#12) + +## [0.1.0] - 2024-07-31 + +This is an initial release since the fork. + +### Breaking changes + + * The `CAP_LAST_CAP` variable is removed; users need to modify the code to + use [LastCap] to get the value. (#6) + * The code now requires Go >= 1.20. + +### Added + * `go.mod` and `go.sum` files. (#2) + * New [LastCap] function. (#6) + * Basic CI using GHA infra. (#8, #9) + * README and CHANGELOG. (#10) + +### Fixed + * Fixed ambient capabilities error handling in [Apply]. (#3) + * Fixed future kernel compatibility. (#1) + * Fixed various linter warnings. (#4, #7) + +### Changed + * Go build tags changed from old-style (`+build`) to new Go 1.17+ style (`go:build`). (#2) + +### Removed + * Removed support for capabilities v1 and v2. (#1) + * Removed init function so programs that use this package start faster. (#6) + * Removed `CAP_LAST_CAP` (use [LastCap] instead). (#6) + +[Apply]: https://pkg.go.dev/github.com/kolyshkin/capability#Capabilities.Apply +[LastCap]: https://pkg.go.dev/github.com/kolyshkin/capability#LastCap + + +[0.1.1]: https://github.com/kolyshkin/capability/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/kolyshkin/capability/compare/42c35b4376354fd5...v0.1.0 + diff --git a/vendor/github.com/syndtr/gocapability/LICENSE b/vendor/github.com/kolyshkin/capability/LICENSE similarity index 100% rename from vendor/github.com/syndtr/gocapability/LICENSE rename to vendor/github.com/kolyshkin/capability/LICENSE diff --git a/vendor/github.com/kolyshkin/capability/README.md b/vendor/github.com/kolyshkin/capability/README.md new file mode 100644 index 00000000000..074731ade89 --- /dev/null +++ b/vendor/github.com/kolyshkin/capability/README.md @@ -0,0 +1,11 @@ +This is a fork of (apparently no longer maintained) +https://github.com/syndtr/gocapability package. It provides basic primitives to +work with [Linux capabilities][capabilities(7)]. + +[![Go Reference](https://pkg.go.dev/badge/github.com/kolyshkin/capability.svg)](https://pkg.go.dev/github.com/kolyshkin/capability) + +## Alternatives + + * https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/cap + +[capabilities(7)]: https://man7.org/linux/man-pages/man7/capabilities.7.html diff --git a/vendor/github.com/syndtr/gocapability/capability/capability.go b/vendor/github.com/kolyshkin/capability/capability.go similarity index 88% rename from vendor/github.com/syndtr/gocapability/capability/capability.go rename to vendor/github.com/kolyshkin/capability/capability.go index 61a90775e59..ac66c2132c2 100644 --- a/vendor/github.com/syndtr/gocapability/capability/capability.go +++ b/vendor/github.com/kolyshkin/capability/capability.go @@ -65,21 +65,21 @@ type Capabilities interface { // // Deprecated: Replace with NewPid2. For example, replace: // -// c, err := NewPid(0) -// if err != nil { -// return err -// } +// c, err := NewPid(0) +// if err != nil { +// return err +// } // // with: // -// c, err := NewPid2(0) -// if err != nil { -// return err -// } -// err = c.Load() -// if err != nil { -// return err -// } +// c, err := NewPid2(0) +// if err != nil { +// return err +// } +// err = c.Load() +// if err != nil { +// return err +// } func NewPid(pid int) (Capabilities, error) { c, err := newPid(pid) if err != nil { @@ -101,21 +101,21 @@ func NewPid2(pid int) (Capabilities, error) { // // Deprecated: Replace with NewFile2. For example, replace: // -// c, err := NewFile(path) -// if err != nil { -// return err -// } +// c, err := NewFile(path) +// if err != nil { +// return err +// } // // with: // -// c, err := NewFile2(path) -// if err != nil { -// return err -// } -// err = c.Load() -// if err != nil { -// return err -// } +// c, err := NewFile2(path) +// if err != nil { +// return err +// } +// err = c.Load() +// if err != nil { +// return err +// } func NewFile(path string) (Capabilities, error) { c, err := newFile(path) if err != nil { diff --git a/vendor/github.com/syndtr/gocapability/capability/capability_linux.go b/vendor/github.com/kolyshkin/capability/capability_linux.go similarity index 69% rename from vendor/github.com/syndtr/gocapability/capability/capability_linux.go rename to vendor/github.com/kolyshkin/capability/capability_linux.go index 1567dc81040..72dc201f0dd 100644 --- a/vendor/github.com/syndtr/gocapability/capability/capability_linux.go +++ b/vendor/github.com/kolyshkin/capability/capability_linux.go @@ -12,62 +12,58 @@ import ( "fmt" "io" "os" + "strconv" "strings" + "sync" "syscall" ) -var errUnknownVers = errors.New("unknown capability version") - const ( - linuxCapVer1 = 0x19980330 - linuxCapVer2 = 0x20071026 + linuxCapVer1 = 0x19980330 // No longer supported. + linuxCapVer2 = 0x20071026 // No longer supported. linuxCapVer3 = 0x20080522 ) -var ( - capVers uint32 - capLastCap Cap -) - -func init() { - var hdr capHeader - capget(&hdr, nil) - capVers = hdr.version - - if initLastCap() == nil { - CAP_LAST_CAP = capLastCap - if capLastCap > 31 { - capUpperMask = (uint32(1) << (uint(capLastCap) - 31)) - 1 - } else { - capUpperMask = 0 - } - } +// LastCap returns highest valid capability of the running kernel. +func LastCap() (Cap, error) { + return lastCap() } -func initLastCap() error { - if capLastCap != 0 { - return nil - } - +var lastCap = sync.OnceValues(func() (Cap, error) { f, err := os.Open("/proc/sys/kernel/cap_last_cap") if err != nil { - return err + return 0, err } - defer f.Close() - var b []byte = make([]byte, 11) - _, err = f.Read(b) + buf := make([]byte, 11) + l, err := f.Read(buf) + f.Close() if err != nil { - return err + return 0, err } + buf = buf[:l] - fmt.Sscanf(string(b), "%d", &capLastCap) + last, err := strconv.Atoi(strings.TrimSpace(string(buf))) + if err != nil { + return 0, err + } + return Cap(last), nil +}) - return nil +func capUpperMask() uint32 { + last, err := lastCap() + if err != nil || last < 32 { + return 0 + } + return (uint32(1) << (uint(last) - 31)) - 1 } func mkStringCap(c Capabilities, which CapType) (ret string) { - for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ { + last, err := lastCap() + if err != nil { + return "" + } + for i, first := Cap(0), true; i <= last; i++ { if !c.Get(which, i) { continue } @@ -98,138 +94,33 @@ func mkString(c Capabilities, max CapType) (ret string) { return } -func newPid(pid int) (c Capabilities, err error) { - switch capVers { - case linuxCapVer1: - p := new(capsV1) - p.hdr.version = capVers - p.hdr.pid = int32(pid) - c = p - case linuxCapVer2, linuxCapVer3: +var capVersion = sync.OnceValues(func() (uint32, error) { + var hdr capHeader + err := capget(&hdr, nil) + return hdr.version, err +}) + +func newPid(pid int) (c Capabilities, retErr error) { + ver, err := capVersion() + if err != nil { + retErr = fmt.Errorf("unable to get capability version from the kernel: %w", err) + return + } + switch ver { + case linuxCapVer1, linuxCapVer2: + retErr = errors.New("old/unsupported capability version (kernel older than 2.6.26?)") + default: + // Either linuxCapVer3, or an unknown/future version (such as v4). + // In the latter case, we fall back to v3 as the latest version known + // to this package, as kernel should be backward-compatible to v3. p := new(capsV3) - p.hdr.version = capVers + p.hdr.version = linuxCapVer3 p.hdr.pid = int32(pid) c = p - default: - err = errUnknownVers - return } return } -type capsV1 struct { - hdr capHeader - data capData -} - -func (c *capsV1) Get(which CapType, what Cap) bool { - if what > 32 { - return false - } - - switch which { - case EFFECTIVE: - return (1< 32 { - continue - } - - if which&EFFECTIVE != 0 { - c.data.effective |= 1 << uint(what) - } - if which&PERMITTED != 0 { - c.data.permitted |= 1 << uint(what) - } - if which&INHERITABLE != 0 { - c.data.inheritable |= 1 << uint(what) - } - } -} - -func (c *capsV1) Unset(which CapType, caps ...Cap) { - for _, what := range caps { - if what > 32 { - continue - } - - if which&EFFECTIVE != 0 { - c.data.effective &= ^(1 << uint(what)) - } - if which&PERMITTED != 0 { - c.data.permitted &= ^(1 << uint(what)) - } - if which&INHERITABLE != 0 { - c.data.inheritable &= ^(1 << uint(what)) - } - } -} - -func (c *capsV1) Fill(kind CapType) { - if kind&CAPS == CAPS { - c.data.effective = 0x7fffffff - c.data.permitted = 0x7fffffff - c.data.inheritable = 0 - } -} - -func (c *capsV1) Clear(kind CapType) { - if kind&CAPS == CAPS { - c.data.effective = 0 - c.data.permitted = 0 - c.data.inheritable = 0 - } -} - -func (c *capsV1) StringCap(which CapType) (ret string) { - return mkStringCap(c, which) -} - -func (c *capsV1) String() (ret string) { - return mkString(c, BOUNDING) -} - -func (c *capsV1) Load() (err error) { - return capget(&c.hdr, &c.data) -} - -func (c *capsV1) Apply(kind CapType) error { - if kind&CAPS == CAPS { - return capset(&c.hdr, &c.data) - } - return nil -} - type capsV3 struct { hdr capHeader data [2]capData @@ -292,7 +183,8 @@ func (c *capsV3) Full(which CapType) bool { if (data[0] & 0xffffffff) != 0xffffffff { return false } - return (data[1] & capUpperMask) == capUpperMask + mask := capUpperMask() + return (data[1] & mask) == mask } func (c *capsV3) Set(which CapType, caps ...Cap) { @@ -401,15 +293,12 @@ func (c *capsV3) Load() (err error) { return } - var status_path string - - if c.hdr.pid == 0 { - status_path = fmt.Sprintf("/proc/self/status") - } else { - status_path = fmt.Sprintf("/proc/%d/status", c.hdr.pid) + path := "/proc/self/status" + if c.hdr.pid != 0 { + path = fmt.Sprintf("/proc/%d/status", c.hdr.pid) } - f, err := os.Open(status_path) + f, err := os.Open(path) if err != nil { return } @@ -423,11 +312,17 @@ func (c *capsV3) Load() (err error) { break } if strings.HasPrefix(line, "CapB") { - fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0]) + _, err = fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0]) + if err != nil { + break + } continue } if strings.HasPrefix(line, "CapA") { - fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0]) + _, err = fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0]) + if err != nil { + break + } continue } } @@ -437,6 +332,10 @@ func (c *capsV3) Load() (err error) { } func (c *capsV3) Apply(kind CapType) (err error) { + last, err := LastCap() + if err != nil { + return err + } if kind&BOUNDS == BOUNDS { var data [2]capData err = capget(&c.hdr, &data[0]) @@ -444,14 +343,14 @@ func (c *capsV3) Apply(kind CapType) (err error) { return } if (1<