加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
CVE-2021-20188.patch 10.15 KB
一键复制 编辑 原始数据 按行查看 历史
wangxiao65 提交于 2021-03-04 09:31 . fix CVE-2021-20188
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
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化