代码拉取完成,页面将自动刷新
同步操作将从 src-openEuler/podman 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
From 69daa67c436a8fdeb0149aa5cb0112f03fdb699f Mon Sep 17 00:00:00 2001
From: Matthew Heon <mheon@redhat.com>
Date: Mon, 25 Jan 2021 14:18:07 -0500
Subject: [PATCH] Correct handling of capabilities
Ensure that capabilities are properly handled for non-root users
in privileged containers. We do not want to give full caps, but
instead only CapInh and CapEff (others should be all-zeroes).
Fixing `podman run` is easy - the same code as the Podman 1.6 fix
works there. The `podman exec` command is far more challenging.
Exec received a complete rewrite to use Conmon at some point
before Podman 1.6, and gained many capabilities in the process.
One of those was the ability to actually tweak the capabilities
of the exec process - 1.0 did not have that. Since it was needed
to resolve this CVE, I was forced to backport a large bit of the
1.0 -> 1.6 exec changes (passing a Process block to the OCI
runtime, and using `prepareProcessExec()` to prepare said block).
I am honestly uncomfortable with the size and scope of this
change but I don't see another way around this.
Fixes CVE-2021-20188
Signed-off-by: Matthew Heon <mheon@redhat.com>
---
libpod/container_api.go | 24 +------
libpod/oci.go | 148 ++++++++++++++++++++++++++++++++--------
pkg/spec/spec.go | 8 +++
3 files changed, 132 insertions(+), 48 deletions(-)
diff --git a/libpod/container_api.go b/libpod/container_api.go
index fe66abf7a8f..b10596f6228 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -2,7 +2,6 @@ package libpod
import (
"context"
- "fmt"
"io/ioutil"
"os"
"strconv"
@@ -11,9 +10,7 @@ import (
"github.com/containers/libpod/libpod/driver"
"github.com/containers/libpod/pkg/inspect"
- "github.com/containers/libpod/pkg/lookup"
"github.com/containers/storage/pkg/stringid"
- "github.com/docker/docker/daemon/caps"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/wait"
@@ -260,8 +257,6 @@ func (c *Container) Kill(signal uint) er
// TODO allow specifying streams to attach to
// TODO investigate allowing exec without attaching
func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string) error {
- var capList []string
-
locked := false
if !c.batched {
locked = true
@@ -284,22 +279,8 @@ func (c *Container) Exec(tty, privileged
if conState != ContainerStateRunning {
return errors.Errorf("cannot exec into container that is not running")
}
- if privileged || c.config.Privileged {
- capList = caps.GetAllCapabilities()
- }
- // If user was set, look it up in the container to get a UID to use on
- // the host
- hostUser := ""
- if user != "" {
- execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, nil)
- if err != nil {
- return err
- }
-
- // runc expects user formatted as uid:gid
- hostUser = fmt.Sprintf("%d:%d", execUser.Uid, execUser.Gid)
- }
+ isPrivileged := privileged || c.config.Privileged
// Generate exec session ID
// Ensure we don't conflict with an existing session ID
@@ -321,10 +303,11 @@ func (c *Container) Exec(tty, privileged
logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID)
- execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID)
+ execCmd, processFile, err := c.runtime.ociRuntime.execContainer(c, cmd, env, tty, workDir, user, sessionID, isPrivileged)
if err != nil {
return errors.Wrapf(err, "error exec %s", c.ID())
}
+ defer os.Remove(processFile)
pidFile := c.execPidPath(sessionID)
// 1 second seems a reasonable time to wait
diff --git a/libpod/oci.go b/libpod/oci.go
index a1894b52fbe..79217dced5d 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -15,10 +15,12 @@ import (
"syscall"
"time"
+ "github.com/containers/libpod/pkg/lookup"
"github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/rootless"
"github.com/coreos/go-systemd/activation"
"github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/docker/daemon/caps"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux"
"github.com/opencontainers/selinux/go-selinux/label"
@@ -689,18 +691,23 @@ func (r *OCIRuntime) unpauseContainer(ct
// TODO: Add --detach support
// TODO: Convert to use conmon
// TODO: add --pid-file and use that to generate exec session tracking
-func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string) (*exec.Cmd, error) {
+func (r *OCIRuntime) execContainer(c *Container, cmd, env []string, tty bool, cwd, user, sessionID string, privileged bool) (*exec.Cmd, string, error) {
if len(cmd) == 0 {
- return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
+ return nil, "", errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
}
if sessionID == "" {
- return nil, errors.Wrapf(ErrEmptyID, "must provide a session ID for exec")
+ return nil, "", errors.Wrapf(ErrEmptyID, "must provide a session ID for exec")
}
runtimeDir, err := GetRootlessRuntimeDir()
if err != nil {
- return nil, err
+ return nil, "", err
+ }
+
+ processFile, err := prepareProcessExec(c, cmd, env, tty, cwd, user, sessionID, privileged)
+ if err != nil {
+ return nil, "", err
}
args := []string{}
@@ -710,32 +717,14 @@ func (r *OCIRuntime) execContainer(c *Co
args = append(args, "exec")
- if cwd != "" {
- args = append(args, "--cwd", cwd)
- }
+ args = append(args, "--process", processFile)
args = append(args, "--pid-file", c.execPidPath(sessionID))
- if tty {
- args = append(args, "--tty")
- }
-
- if user != "" {
- args = append(args, "--user", user)
- }
-
if c.config.Spec.Process.NoNewPrivileges {
args = append(args, "--no-new-privs")
}
- for _, cap := range capAdd {
- args = append(args, "--cap", cap)
- }
-
- for _, envVar := range env {
- args = append(args, "--env", envVar)
- }
-
// Append container ID and command
args = append(args, c.ID())
args = append(args, cmd...)
@@ -749,10 +738,10 @@ func (r *OCIRuntime) execContainer(c *Co
execCmd.Env = append(execCmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
if err := execCmd.Start(); err != nil {
- return nil, errors.Wrapf(err, "cannot start container %s", c.ID())
+ return nil, "", errors.Wrapf(err, "cannot start container %s", c.ID())
}
- return execCmd, nil
+ return execCmd, processFile, nil
}
// execStopContainer stops all active exec sessions in a container
@@ -831,3 +820,110 @@ func (r *OCIRuntime) checkpointContainer
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "checkpoint",
"--image-path", imagePath, "--work-path", workPath, ctr.ID())
}
+
+// prepareProcessExec returns the path of the process.json used in runc exec -p.
+// Returns path to the created exec process file. This will need to be removed
+// by the caller when they're done, best effort.
+func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, sessionID string, privileged bool) (string, error) {
+ filename := filepath.Join(c.bundlePath(), fmt.Sprintf("exec-process-%s", sessionID))
+ f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0600)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ pspec := c.config.Spec.Process
+ pspec.SelinuxLabel = c.config.ProcessLabel
+ pspec.Args = cmd
+ // We need to default this to false else it will inherit terminal as true
+ // from the container.
+ pspec.Terminal = false
+ if tty {
+ pspec.Terminal = true
+ }
+ if len(env) > 0 {
+ pspec.Env = append(pspec.Env, env...)
+ }
+
+ if cwd != "" {
+ pspec.Cwd = cwd
+
+ }
+
+ var addGroups []string
+ var sgids []uint32
+
+ // if the user is empty, we should inherit the user that the container is currently running with
+ if user == "" {
+ user = c.config.User
+ addGroups = c.config.Groups
+ }
+
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, nil)
+ if err != nil {
+ return "", err
+ }
+
+ if len(addGroups) > 0 {
+ sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, nil)
+ if err != nil {
+ return "", errors.Wrapf(err, "error looking up supplemental groups for container %s exec session %s", c.ID(), sessionID)
+ }
+ }
+
+ // If user was set, look it up in the container to get a UID to use on
+ // the host
+ if user != "" || len(sgids) > 0 {
+ if user != "" {
+ for _, sgid := range execUser.Sgids {
+ sgids = append(sgids, uint32(sgid))
+ }
+ }
+ processUser := spec.User{
+ UID: uint32(execUser.Uid),
+ GID: uint32(execUser.Gid),
+ AdditionalGids: sgids,
+ }
+
+ pspec.User = processUser
+ }
+
+ allCaps := caps.GetAllCapabilities()
+ pspec.Capabilities.Effective = []string{}
+ if privileged {
+ pspec.Capabilities.Bounding = allCaps
+ } else {
+ pspec.Capabilities.Bounding = []string{}
+ }
+ pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
+ if execUser.Uid == 0 {
+ pspec.Capabilities.Effective = pspec.Capabilities.Bounding
+ pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
+ pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
+ } else {
+ pspec.Capabilities.Permitted = pspec.Capabilities.Effective
+ pspec.Capabilities.Ambient = pspec.Capabilities.Effective
+ }
+
+ hasHomeSet := false
+ for _, s := range pspec.Env {
+ if strings.HasPrefix(s, "HOME=") {
+ hasHomeSet = true
+ break
+ }
+ }
+ if !hasHomeSet {
+ pspec.Env = append(pspec.Env, fmt.Sprintf("HOME=%s", execUser.Home))
+ }
+
+ processJSON, err := json.Marshal(pspec)
+ if err != nil {
+ return "", err
+ }
+
+ if err := ioutil.WriteFile(filename, processJSON, 0644); err != nil {
+ return "", err
+ }
+
+ return filename, nil
+}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 46105af4aef..d4f13711859 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -325,6 +325,14 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
}
} else {
g.SetupPrivileged(true)
+ if config.User != "" {
+ user := strings.SplitN(config.User, ":", 2)[0]
+ if user != "root" && user != "0" {
+ g.Spec().Process.Capabilities.Effective = []string{}
+ g.Spec().Process.Capabilities.Permitted = []string{}
+ g.Spec().Process.Capabilities.Ambient = []string{}
+ }
+ }
}
// HANDLE SECCOMP
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。