浏览代码

Merge pull request #42276 from thaJeztah/apparmor_detect_fix

Use containerd's apparmor package to detect if apparmor can be used
Tibor Vass 4 年之前
父节点
当前提交
68bec0fcf7

+ 3 - 3
daemon/apparmor_default.go

@@ -5,8 +5,8 @@ package daemon // import "github.com/docker/docker/daemon"
 import (
 	"fmt"
 
+	"github.com/containerd/containerd/pkg/apparmor"
 	aaprofile "github.com/docker/docker/profiles/apparmor"
-	"github.com/opencontainers/runc/libcontainer/apparmor"
 )
 
 // Define constants for native driver
@@ -17,14 +17,14 @@ const (
 
 // DefaultApparmorProfile returns the name of the default apparmor profile
 func DefaultApparmorProfile() string {
-	if apparmor.IsEnabled() {
+	if apparmor.HostSupports() {
 		return defaultAppArmorProfile
 	}
 	return ""
 }
 
 func ensureDefaultAppArmorProfile() error {
-	if apparmor.IsEnabled() {
+	if apparmor.HostSupports() {
 		loaded, err := aaprofile.IsLoaded(defaultAppArmorProfile)
 		if err != nil {
 			return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultAppArmorProfile, err)

+ 2 - 2
daemon/exec_linux.go

@@ -3,10 +3,10 @@ package daemon // import "github.com/docker/docker/daemon"
 import (
 	"context"
 
+	"github.com/containerd/containerd/pkg/apparmor"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/exec"
 	"github.com/docker/docker/oci/caps"
-	"github.com/opencontainers/runc/libcontainer/apparmor"
 	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.Effective = p.Capabilities.Bounding
 	}
-	if apparmor.IsEnabled() {
+	if apparmor.HostSupports() {
 		var appArmorProfile string
 		if c.AppArmorProfile != "" {
 			appArmorProfile = c.AppArmorProfile

+ 3 - 3
daemon/exec_linux_test.go

@@ -5,16 +5,16 @@ package daemon
 import (
 	"testing"
 
+	"github.com/containerd/containerd/pkg/apparmor"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/exec"
-	"github.com/opencontainers/runc/libcontainer/apparmor"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"gotest.tools/v3/assert"
 )
 
 func TestExecSetPlatformOpt(t *testing.T) {
-	if !apparmor.IsEnabled() {
+	if !apparmor.HostSupports() {
 		t.Skip("requires AppArmor to be enabled")
 	}
 	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
 // from being changed accidentally.
 func TestExecSetPlatformOptPrivileged(t *testing.T) {
-	if !apparmor.IsEnabled() {
+	if !apparmor.HostSupports() {
 		t.Skip("requires AppArmor to be enabled")
 	}
 	d := &Daemon{}

+ 2 - 2
daemon/oci_linux.go

@@ -14,6 +14,7 @@ import (
 	cdcgroups "github.com/containerd/cgroups"
 	"github.com/containerd/containerd/containers"
 	coci "github.com/containerd/containerd/oci"
+	"github.com/containerd/containerd/pkg/apparmor"
 	"github.com/containerd/containerd/sys"
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
@@ -26,7 +27,6 @@ import (
 	volumemounts "github.com/docker/docker/volume/mounts"
 	"github.com/moby/sys/mount"
 	"github.com/moby/sys/mountinfo"
-	"github.com/opencontainers/runc/libcontainer/apparmor"
 	"github.com/opencontainers/runc/libcontainer/cgroups"
 	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/user"
@@ -128,7 +128,7 @@ func WithSelinux(c *container.Container) coci.SpecOpts {
 // WithApparmor sets the apparmor profile
 func WithApparmor(c *container.Container) coci.SpecOpts {
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
-		if apparmor.IsEnabled() {
+		if apparmor.HostSupports() {
 			var appArmorProfile string
 			if c.AppArmorProfile != "" {
 				appArmorProfile = c.AppArmorProfile

+ 1 - 1
vendor.conf

@@ -129,7 +129,7 @@ github.com/googleapis/gax-go                        bd5b16380fd03dc758d11cef74ba
 google.golang.org/genproto                          3f1135a288c9a07e340ae8ba4cc6c7065a3160e8
 
 # 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/continuity                    efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
 github.com/containerd/cgroups                       0b889c03f102012f1d93a97ddd3ef71cd6f4f510

+ 48 - 0
vendor/github.com/containerd/containerd/pkg/apparmor/apparmor.go

@@ -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 - 0
vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_unsupported.go

@@ -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 - 0
vendor/github.com/containerd/containerd/reference/reference.go

@@ -85,6 +85,10 @@ var splitRe = regexp.MustCompile(`[:@]`)
 
 // Parse parses the string into a structured ref.
 func Parse(s string) (Spec, error) {
+	if strings.Contains(s, "://") {
+		return Spec{}, ErrInvalid
+	}
+
 	u, err := url.Parse("dummy://" + s)
 	if err != nil {
 		return Spec{}, err

+ 0 - 60
vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go

@@ -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)
-}

+ 0 - 20
vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go

@@ -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
-}

+ 0 - 93
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go

@@ -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)
-}

+ 0 - 112
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go

@@ -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))
-}

+ 0 - 68
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go

@@ -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
-}