mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #42276 from thaJeztah/apparmor_detect_fix
Use containerd's apparmor package to detect if apparmor can be used
This commit is contained in:
commit
68bec0fcf7
13 changed files with 87 additions and 364 deletions
|
@ -5,8 +5,8 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/pkg/apparmor"
|
||||||
aaprofile "github.com/docker/docker/profiles/apparmor"
|
aaprofile "github.com/docker/docker/profiles/apparmor"
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define constants for native driver
|
// Define constants for native driver
|
||||||
|
@ -17,14 +17,14 @@ const (
|
||||||
|
|
||||||
// DefaultApparmorProfile returns the name of the default apparmor profile
|
// DefaultApparmorProfile returns the name of the default apparmor profile
|
||||||
func DefaultApparmorProfile() string {
|
func DefaultApparmorProfile() string {
|
||||||
if apparmor.IsEnabled() {
|
if apparmor.HostSupports() {
|
||||||
return defaultAppArmorProfile
|
return defaultAppArmorProfile
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureDefaultAppArmorProfile() error {
|
func ensureDefaultAppArmorProfile() error {
|
||||||
if apparmor.IsEnabled() {
|
if apparmor.HostSupports() {
|
||||||
loaded, err := aaprofile.IsLoaded(defaultAppArmorProfile)
|
loaded, err := aaprofile.IsLoaded(defaultAppArmorProfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultAppArmorProfile, err)
|
return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultAppArmorProfile, err)
|
||||||
|
|
|
@ -3,10 +3,10 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/pkg/apparmor"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/docker/docker/oci/caps"
|
"github.com/docker/docker/oci/caps"
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
|
||||||
p.Capabilities.Inheritable = p.Capabilities.Bounding
|
p.Capabilities.Inheritable = p.Capabilities.Bounding
|
||||||
p.Capabilities.Effective = p.Capabilities.Bounding
|
p.Capabilities.Effective = p.Capabilities.Bounding
|
||||||
}
|
}
|
||||||
if apparmor.IsEnabled() {
|
if apparmor.HostSupports() {
|
||||||
var appArmorProfile string
|
var appArmorProfile string
|
||||||
if c.AppArmorProfile != "" {
|
if c.AppArmorProfile != "" {
|
||||||
appArmorProfile = c.AppArmorProfile
|
appArmorProfile = c.AppArmorProfile
|
||||||
|
|
|
@ -5,16 +5,16 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/pkg/apparmor"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/exec"
|
"github.com/docker/docker/daemon/exec"
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExecSetPlatformOpt(t *testing.T) {
|
func TestExecSetPlatformOpt(t *testing.T) {
|
||||||
if !apparmor.IsEnabled() {
|
if !apparmor.HostSupports() {
|
||||||
t.Skip("requires AppArmor to be enabled")
|
t.Skip("requires AppArmor to be enabled")
|
||||||
}
|
}
|
||||||
d := &Daemon{}
|
d := &Daemon{}
|
||||||
|
@ -34,7 +34,7 @@ func TestExecSetPlatformOpt(t *testing.T) {
|
||||||
// This behavior may change in future, but test for the behavior to prevent it
|
// This behavior may change in future, but test for the behavior to prevent it
|
||||||
// from being changed accidentally.
|
// from being changed accidentally.
|
||||||
func TestExecSetPlatformOptPrivileged(t *testing.T) {
|
func TestExecSetPlatformOptPrivileged(t *testing.T) {
|
||||||
if !apparmor.IsEnabled() {
|
if !apparmor.HostSupports() {
|
||||||
t.Skip("requires AppArmor to be enabled")
|
t.Skip("requires AppArmor to be enabled")
|
||||||
}
|
}
|
||||||
d := &Daemon{}
|
d := &Daemon{}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
cdcgroups "github.com/containerd/cgroups"
|
cdcgroups "github.com/containerd/cgroups"
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
coci "github.com/containerd/containerd/oci"
|
coci "github.com/containerd/containerd/oci"
|
||||||
|
"github.com/containerd/containerd/pkg/apparmor"
|
||||||
"github.com/containerd/containerd/sys"
|
"github.com/containerd/containerd/sys"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
|
@ -26,7 +27,6 @@ import (
|
||||||
volumemounts "github.com/docker/docker/volume/mounts"
|
volumemounts "github.com/docker/docker/volume/mounts"
|
||||||
"github.com/moby/sys/mount"
|
"github.com/moby/sys/mount"
|
||||||
"github.com/moby/sys/mountinfo"
|
"github.com/moby/sys/mountinfo"
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
"github.com/opencontainers/runc/libcontainer/devices"
|
"github.com/opencontainers/runc/libcontainer/devices"
|
||||||
"github.com/opencontainers/runc/libcontainer/user"
|
"github.com/opencontainers/runc/libcontainer/user"
|
||||||
|
@ -128,7 +128,7 @@ func WithSelinux(c *container.Container) coci.SpecOpts {
|
||||||
// WithApparmor sets the apparmor profile
|
// WithApparmor sets the apparmor profile
|
||||||
func WithApparmor(c *container.Container) coci.SpecOpts {
|
func WithApparmor(c *container.Container) coci.SpecOpts {
|
||||||
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
|
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
|
||||||
if apparmor.IsEnabled() {
|
if apparmor.HostSupports() {
|
||||||
var appArmorProfile string
|
var appArmorProfile string
|
||||||
if c.AppArmorProfile != "" {
|
if c.AppArmorProfile != "" {
|
||||||
appArmorProfile = c.AppArmorProfile
|
appArmorProfile = c.AppArmorProfile
|
||||||
|
|
|
@ -129,7 +129,7 @@ github.com/googleapis/gax-go bd5b16380fd03dc758d11cef74ba
|
||||||
google.golang.org/genproto 3f1135a288c9a07e340ae8ba4cc6c7065a3160e8
|
google.golang.org/genproto 3f1135a288c9a07e340ae8ba4cc6c7065a3160e8
|
||||||
|
|
||||||
# containerd
|
# containerd
|
||||||
github.com/containerd/containerd fbf1a72de7da110187b7d3dace433914b9beca10 # master (v1.5.0-dev)
|
github.com/containerd/containerd 55eda46b22f985cde99b599e469ff9c13994bf68 # master (v1.5.0-dev)
|
||||||
github.com/containerd/fifo 0724c46b320cf96bb172a0550c19a4b1fca4dacb
|
github.com/containerd/fifo 0724c46b320cf96bb172a0550c19a4b1fca4dacb
|
||||||
github.com/containerd/continuity efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
|
github.com/containerd/continuity efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
|
||||||
github.com/containerd/cgroups 0b889c03f102012f1d93a97ddd3ef71cd6f4f510
|
github.com/containerd/cgroups 0b889c03f102012f1d93a97ddd3ef71cd6f4f510
|
||||||
|
|
48
vendor/github.com/containerd/containerd/pkg/apparmor/apparmor.go
generated
vendored
Normal file
48
vendor/github.com/containerd/containerd/pkg/apparmor/apparmor.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// +build apparmor,linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 apparmor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
appArmorSupported bool
|
||||||
|
checkAppArmor sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostSupports returns true if apparmor is enabled for the host, if
|
||||||
|
// apparmor_parser is enabled, and if we are not running docker-in-docker.
|
||||||
|
//
|
||||||
|
// It is a modified version of libcontainer/apparmor.IsEnabled(), which does not
|
||||||
|
// check for apparmor_parser to be present, or if we're running docker-in-docker.
|
||||||
|
func HostSupports() bool {
|
||||||
|
checkAppArmor.Do(func() {
|
||||||
|
// see https://github.com/docker/docker/commit/de191e86321f7d3136ff42ff75826b8107399497
|
||||||
|
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" {
|
||||||
|
if _, err = os.Stat("/sbin/apparmor_parser"); err == nil {
|
||||||
|
buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
|
||||||
|
appArmorSupported = err == nil && len(buf) > 1 && buf[0] == 'Y'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return appArmorSupported
|
||||||
|
}
|
24
vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_unsupported.go
generated
vendored
Normal file
24
vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// +build !apparmor !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 apparmor
|
||||||
|
|
||||||
|
//nolint: deadcode, unused
|
||||||
|
func HostSupports() bool {
|
||||||
|
return false
|
||||||
|
}
|
4
vendor/github.com/containerd/containerd/reference/reference.go
generated
vendored
4
vendor/github.com/containerd/containerd/reference/reference.go
generated
vendored
|
@ -85,6 +85,10 @@ var splitRe = regexp.MustCompile(`[:@]`)
|
||||||
|
|
||||||
// Parse parses the string into a structured ref.
|
// Parse parses the string into a structured ref.
|
||||||
func Parse(s string) (Spec, error) {
|
func Parse(s string) (Spec, error) {
|
||||||
|
if strings.Contains(s, "://") {
|
||||||
|
return Spec{}, ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
u, err := url.Parse("dummy://" + s)
|
u, err := url.Parse("dummy://" + s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Spec{}, err
|
return Spec{}, err
|
||||||
|
|
60
vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go
generated
vendored
60
vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go
generated
vendored
|
@ -1,60 +0,0 @@
|
||||||
// +build apparmor,linux
|
|
||||||
|
|
||||||
package apparmor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsEnabled returns true if apparmor is enabled for the host.
|
|
||||||
func IsEnabled() bool {
|
|
||||||
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" {
|
|
||||||
if _, err = os.Stat("/sbin/apparmor_parser"); err == nil {
|
|
||||||
buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
|
|
||||||
return err == nil && len(buf) > 1 && buf[0] == 'Y'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func setProcAttr(attr, value string) error {
|
|
||||||
// Under AppArmor you can only change your own attr, so use /proc/self/
|
|
||||||
// instead of /proc/<tid>/ like libapparmor does
|
|
||||||
path := fmt.Sprintf("/proc/self/attr/%s", attr)
|
|
||||||
|
|
||||||
f, err := os.OpenFile(path, os.O_WRONLY, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if err := utils.EnsureProcHandle(f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintf(f, "%s", value)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// changeOnExec reimplements aa_change_onexec from libapparmor in Go
|
|
||||||
func changeOnExec(name string) error {
|
|
||||||
value := "exec " + name
|
|
||||||
if err := setProcAttr("exec", value); err != nil {
|
|
||||||
return fmt.Errorf("apparmor failed to apply profile: %s", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyProfile will apply the profile with the specified name to the process after
|
|
||||||
// the next exec.
|
|
||||||
func ApplyProfile(name string) error {
|
|
||||||
if name == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return changeOnExec(name)
|
|
||||||
}
|
|
20
vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go
generated
vendored
20
vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
// +build !apparmor !linux
|
|
||||||
|
|
||||||
package apparmor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrApparmorNotEnabled = errors.New("apparmor: config provided but apparmor not supported")
|
|
||||||
|
|
||||||
func IsEnabled() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ApplyProfile(name string) error {
|
|
||||||
if name != "" {
|
|
||||||
return ErrApparmorNotEnabled
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
93
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
93
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
|
@ -1,93 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2016, 2017 SUSE LLC
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MaxSendfdLen is the maximum length of the name of a file descriptor being
|
|
||||||
// sent using SendFd. The name of the file handle returned by RecvFd will never
|
|
||||||
// be larger than this value.
|
|
||||||
const MaxNameLen = 4096
|
|
||||||
|
|
||||||
// oobSpace is the size of the oob slice required to store a single FD. Note
|
|
||||||
// that unix.UnixRights appears to make the assumption that fd is always int32,
|
|
||||||
// so sizeof(fd) = 4.
|
|
||||||
var oobSpace = unix.CmsgSpace(4)
|
|
||||||
|
|
||||||
// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
|
|
||||||
// socket. The file name of the remote file descriptor will be recreated
|
|
||||||
// locally (it is sent as non-auxiliary data in the same payload).
|
|
||||||
func RecvFd(socket *os.File) (*os.File, error) {
|
|
||||||
// For some reason, unix.Recvmsg uses the length rather than the capacity
|
|
||||||
// when passing the msg_controllen and other attributes to recvmsg. So we
|
|
||||||
// have to actually set the length.
|
|
||||||
name := make([]byte, MaxNameLen)
|
|
||||||
oob := make([]byte, oobSpace)
|
|
||||||
|
|
||||||
sockfd := socket.Fd()
|
|
||||||
n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if n >= MaxNameLen || oobn != oobSpace {
|
|
||||||
return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Truncate.
|
|
||||||
name = name[:n]
|
|
||||||
oob = oob[:oobn]
|
|
||||||
|
|
||||||
scms, err := unix.ParseSocketControlMessage(oob)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(scms) != 1 {
|
|
||||||
return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
|
|
||||||
}
|
|
||||||
scm := scms[0]
|
|
||||||
|
|
||||||
fds, err := unix.ParseUnixRights(&scm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(fds) != 1 {
|
|
||||||
return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
|
|
||||||
}
|
|
||||||
fd := uintptr(fds[0])
|
|
||||||
|
|
||||||
return os.NewFile(fd, string(name)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendFd sends a file descriptor over the given AF_UNIX socket. In
|
|
||||||
// addition, the file.Name() of the given file will also be sent as
|
|
||||||
// non-auxiliary data in the same payload (allowing to send contextual
|
|
||||||
// information for a file descriptor).
|
|
||||||
func SendFd(socket *os.File, name string, fd uintptr) error {
|
|
||||||
if len(name) >= MaxNameLen {
|
|
||||||
return fmt.Errorf("sendfd: filename too long: %s", name)
|
|
||||||
}
|
|
||||||
oob := unix.UnixRights(int(fd))
|
|
||||||
return unix.Sendmsg(int(socket.Fd()), []byte(name), oob, nil, 0)
|
|
||||||
}
|
|
112
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
112
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
|
@ -1,112 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
exitSignalOffset = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
// ResolveRootfs ensures that the current working directory is
|
|
||||||
// not a symlink and returns the absolute path to the rootfs
|
|
||||||
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
|
||||||
rootfs, err := filepath.Abs(uncleanRootfs)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath.EvalSymlinks(rootfs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitStatus returns the correct exit status for a process based on if it
|
|
||||||
// was signaled or exited cleanly
|
|
||||||
func ExitStatus(status unix.WaitStatus) int {
|
|
||||||
if status.Signaled() {
|
|
||||||
return exitSignalOffset + int(status.Signal())
|
|
||||||
}
|
|
||||||
return status.ExitStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteJSON writes the provided struct v to w using standard json marshaling
|
|
||||||
func WriteJSON(w io.Writer, v interface{}) error {
|
|
||||||
data, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = w.Write(data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanPath makes a path safe for use with filepath.Join. This is done by not
|
|
||||||
// only cleaning the path, but also (if the path is relative) adding a leading
|
|
||||||
// '/' and cleaning it (then removing the leading '/'). This ensures that a
|
|
||||||
// path resulting from prepending another path will always resolve to lexically
|
|
||||||
// be a subdirectory of the prefixed path. This is all done lexically, so paths
|
|
||||||
// that include symlinks won't be safe as a result of using CleanPath.
|
|
||||||
func CleanPath(path string) string {
|
|
||||||
// Deal with empty strings nicely.
|
|
||||||
if path == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that all paths are cleaned (especially problematic ones like
|
|
||||||
// "/../../../../../" which can cause lots of issues).
|
|
||||||
path = filepath.Clean(path)
|
|
||||||
|
|
||||||
// If the path isn't absolute, we need to do more processing to fix paths
|
|
||||||
// such as "../../../../<etc>/some/path". We also shouldn't convert absolute
|
|
||||||
// paths to relative ones.
|
|
||||||
if !filepath.IsAbs(path) {
|
|
||||||
path = filepath.Clean(string(os.PathSeparator) + path)
|
|
||||||
// This can't fail, as (by definition) all paths are relative to root.
|
|
||||||
path, _ = filepath.Rel(string(os.PathSeparator), path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean the path again for good measure.
|
|
||||||
return filepath.Clean(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchLabels searches a list of key-value pairs for the provided key and
|
|
||||||
// returns the corresponding value. The pairs must be separated with '='.
|
|
||||||
func SearchLabels(labels []string, query string) string {
|
|
||||||
for _, l := range labels {
|
|
||||||
parts := strings.SplitN(l, "=", 2)
|
|
||||||
if len(parts) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if parts[0] == query {
|
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annotations returns the bundle path and user defined annotations from the
|
|
||||||
// libcontainer state. We need to remove the bundle because that is a label
|
|
||||||
// added by libcontainer.
|
|
||||||
func Annotations(labels []string) (bundle string, userAnnotations map[string]string) {
|
|
||||||
userAnnotations = make(map[string]string)
|
|
||||||
for _, l := range labels {
|
|
||||||
parts := strings.SplitN(l, "=", 2)
|
|
||||||
if len(parts) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if parts[0] == "bundle" {
|
|
||||||
bundle = parts[1]
|
|
||||||
} else {
|
|
||||||
userAnnotations[parts[0]] = parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetIntSize() int {
|
|
||||||
return int(unsafe.Sizeof(1))
|
|
||||||
}
|
|
68
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
68
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
|
@ -1,68 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EnsureProcHandle returns whether or not the given file handle is on procfs.
|
|
||||||
func EnsureProcHandle(fh *os.File) error {
|
|
||||||
var buf unix.Statfs_t
|
|
||||||
if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil {
|
|
||||||
return fmt.Errorf("ensure %s is on procfs: %v", fh.Name(), err)
|
|
||||||
}
|
|
||||||
if buf.Type != unix.PROC_SUPER_MAGIC {
|
|
||||||
return fmt.Errorf("%s is not on procfs", fh.Name())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseExecFrom applies O_CLOEXEC to all file descriptors currently open for
|
|
||||||
// the process (except for those below the given fd value).
|
|
||||||
func CloseExecFrom(minFd int) error {
|
|
||||||
fdDir, err := os.Open("/proc/self/fd")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fdDir.Close()
|
|
||||||
|
|
||||||
if err := EnsureProcHandle(fdDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fdList, err := fdDir.Readdirnames(-1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, fdStr := range fdList {
|
|
||||||
fd, err := strconv.Atoi(fdStr)
|
|
||||||
// Ignore non-numeric file names.
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Ignore descriptors lower than our specified minimum.
|
|
||||||
if fd < minFd {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Intentionally ignore errors from unix.CloseOnExec -- the cases where
|
|
||||||
// this might fail are basically file descriptors that have already
|
|
||||||
// been closed (including and especially the one that was created when
|
|
||||||
// ioutil.ReadDir did the "opendir" syscall).
|
|
||||||
unix.CloseOnExec(fd)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSockPair returns a new unix socket pair
|
|
||||||
func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
|
|
||||||
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
|
|
||||||
}
|
|
Loading…
Reference in a new issue