summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state_internal.go2
-rw-r--r--libpod/container_exec.go27
-rw-r--r--libpod/container_validate.go6
-rw-r--r--libpod/define/errors.go4
-rw-r--r--libpod/in_memory_state.go4
-rw-r--r--libpod/networking_linux.go37
-rw-r--r--libpod/oci_conmon_exec_linux.go124
-rw-r--r--libpod/oci_conmon_linux.go120
8 files changed, 188 insertions, 136 deletions
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index d4994334f..f63876c14 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -919,7 +919,7 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
return err
}
if len(deps) != 0 {
- return errors.Wrapf(define.ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
+ return errors.Wrapf(define.ErrDepExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
}
if err := ctrBucket.DeleteBucket(ctrID); err != nil {
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index bb43287d9..8d8ed14aa 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -696,6 +696,24 @@ func (c *Container) ExecResize(sessionID string, newSize define.TerminalSize) er
return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it is not running", c.ID(), session.ID())
}
+ // The exec session may have exited since we last updated.
+ // Needed to prevent race conditions around short-running exec sessions.
+ running, err := c.ociRuntime.ExecUpdateStatus(c, session.ID())
+ if err != nil {
+ return err
+ }
+ if !running {
+ session.State = define.ExecStateStopped
+
+ if err := c.save(); err != nil {
+ logrus.Errorf("Error saving state of container %s: %v", c.ID(), err)
+ }
+
+ return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot resize container %s exec session %s as it has stopped", c.ID(), session.ID())
+ }
+
+ // Make sure the exec session is still running.
+
return c.ociRuntime.ExecAttachResize(c, sessionID, newSize)
}
@@ -720,8 +738,13 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
logrus.Debugf("Sending resize events to exec session %s", sessionID)
for resizeRequest := range resize {
if err := c.ExecResize(sessionID, resizeRequest); err != nil {
- // Assume the exec session went down.
- logrus.Warnf("Error resizing exec session %s: %v", sessionID, err)
+ if errors.Cause(err) == define.ErrExecSessionStateInvalid {
+ // The exec session stopped
+ // before we could resize.
+ logrus.Infof("Missed resize on exec session %s, already stopped", sessionID)
+ } else {
+ logrus.Warnf("Error resizing exec session %s: %v", sessionID, err)
+ }
return
}
}
diff --git a/libpod/container_validate.go b/libpod/container_validate.go
index 245121a91..aae96ae85 100644
--- a/libpod/container_validate.go
+++ b/libpod/container_validate.go
@@ -126,5 +126,11 @@ func (c *Container) validate() error {
}
}
+ // If User in the OCI spec is set, require that c.config.User is set for
+ // security reasons (a lot of our code relies on c.config.User).
+ if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) {
+ return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly")
+ }
+
return nil
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 2e85454b2..e19ac6a27 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -31,6 +31,10 @@ var (
// not exist.
ErrNoSuchExecSession = errors.New("no such exec session")
+ // ErrDepExists indicates that the current object has dependencies and
+ // cannot be removed before them.
+ ErrDepExists = errors.New("dependency exists")
+
// ErrNoAliases indicates that the container does not have any network
// aliases.
ErrNoAliases = errors.New("no aliases for container")
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 3875878ed..df45f8e73 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -391,7 +391,7 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
deps, ok := s.ctrDepends[ctr.ID()]
if ok && len(deps) != 0 {
depsStr := strings.Join(deps, ", ")
- return errors.Wrapf(define.ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
+ return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
}
// Ensure we don't have active exec sessions
@@ -1497,7 +1497,7 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
deps, ok := s.ctrDepends[ctr.ID()]
if ok && len(deps) != 0 {
depsStr := strings.Join(deps, ", ")
- return errors.Wrapf(define.ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
+ return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
}
// Ensure we don't have active exec sessions
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 157c85431..6e2c2880f 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -105,13 +105,13 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port
return ctrNetwork
}
-type rootlessCNI struct {
+type RootlessCNI struct {
ns ns.NetNS
dir string
lock lockfile.Locker
}
-func (r *rootlessCNI) Do(toRun func() error) error {
+func (r *RootlessCNI) Do(toRun func() error) error {
err := r.ns.Do(func(_ ns.NetNS) error {
// before we can run the given function
// we have to setup all mounts correctly
@@ -174,9 +174,14 @@ func (r *rootlessCNI) Do(toRun func() error) error {
return err
}
-// cleanup the rootless cni namespace if needed
+// Cleanup the rootless cni namespace if needed
// check if we have running containers with the bridge network mode
-func (r *rootlessCNI) cleanup(runtime *Runtime) error {
+func (r *RootlessCNI) Cleanup(runtime *Runtime) error {
+ _, err := os.Stat(r.dir)
+ if os.IsNotExist(err) {
+ // the directory does not exists no need for cleanup
+ return nil
+ }
r.lock.Lock()
defer r.lock.Unlock()
running := func(c *Container) bool {
@@ -234,10 +239,10 @@ func (r *rootlessCNI) cleanup(runtime *Runtime) error {
return nil
}
-// getRootlessCNINetNs returns the rootless cni object. If create is set to true
+// GetRootlessCNINetNs returns the rootless cni object. If create is set to true
// the rootless cni namespace will be created if it does not exists already.
-func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
- var rootlessCNINS *rootlessCNI
+func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
+ var rootlessCNINS *RootlessCNI
if rootless.IsRootless() {
runDir, err := util.GetRuntimeDir()
if err != nil {
@@ -411,7 +416,17 @@ func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
}
}
- rootlessCNINS = &rootlessCNI{
+ // The CNI plugins need access to iptables in $PATH. As it turns out debian doesn't put
+ // /usr/sbin in $PATH for rootless users. This will break rootless cni completely.
+ // We might break existing users and we cannot expect everyone to change their $PATH so
+ // lets add /usr/sbin to $PATH ourselves.
+ path = os.Getenv("PATH")
+ if !strings.Contains(path, "/usr/sbin") {
+ path = path + ":/usr/sbin"
+ os.Setenv("PATH", path)
+ }
+
+ rootlessCNINS = &RootlessCNI{
ns: ns,
dir: cniDir,
lock: lock,
@@ -423,7 +438,7 @@ func (r *Runtime) getRootlessCNINetNs(new bool) (*rootlessCNI, error) {
// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
// networks. If rootless it will join/create the rootless cni namespace.
func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
- rootlessCNINS, err := r.getRootlessCNINetNs(true)
+ rootlessCNINS, err := r.GetRootlessCNINetNs(true)
if err != nil {
return nil, err
}
@@ -641,7 +656,7 @@ func (r *Runtime) closeNetNS(ctr *Container) error {
// Tear down a container's CNI network configuration and joins the
// rootless net ns as rootless user
func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error {
- rootlessCNINS, err := r.getRootlessCNINetNs(false)
+ rootlessCNINS, err := r.GetRootlessCNINetNs(false)
if err != nil {
return err
}
@@ -655,7 +670,7 @@ func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error {
// execute the cni setup in the rootless net ns
err = rootlessCNINS.Do(tearDownPod)
if err == nil {
- err = rootlessCNINS.cleanup(r)
+ err = rootlessCNINS.Cleanup(r)
}
} else {
err = tearDownPod()
diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index 173edba2b..b43316951 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -2,18 +2,23 @@ package libpod
import (
"fmt"
+ "io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
+ "strings"
"syscall"
"time"
+ "github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/errorhandling"
+ "github.com/containers/podman/v3/pkg/lookup"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/podman/v3/utils"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
@@ -654,3 +659,122 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
}
}
}
+
+// prepareProcessExec returns the path of the process.json used in runc exec -p
+// caller is responsible to close the returned *os.File if needed.
+func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessionID string) (*os.File, error) {
+ f, err := ioutil.TempFile(c.execBundlePath(sessionID), "exec-process-")
+ if err != nil {
+ return nil, err
+ }
+ pspec := new(spec.Process)
+ if err := JSONDeepCopy(c.config.Spec.Process, pspec); err != nil {
+ return nil, err
+ }
+ pspec.SelinuxLabel = c.config.ProcessLabel
+ pspec.Args = options.Cmd
+
+ // We need to default this to false else it will inherit terminal as true
+ // from the container.
+ pspec.Terminal = false
+ if options.Terminal {
+ pspec.Terminal = true
+ }
+ if len(env) > 0 {
+ pspec.Env = append(pspec.Env, env...)
+ }
+
+ if options.Cwd != "" {
+ pspec.Cwd = options.Cwd
+ }
+
+ var addGroups []string
+ var sgids []uint32
+
+ // if the user is empty, we should inherit the user that the container is currently running with
+ user := options.User
+ if user == "" {
+ logrus.Debugf("Set user to %s", c.config.User)
+ user = c.config.User
+ addGroups = c.config.Groups
+ }
+
+ overrides := c.getUserOverrides()
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, overrides)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(addGroups) > 0 {
+ sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, overrides)
+ if err != nil {
+ return nil, 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
+ }
+
+ ctrSpec, err := c.specFromState()
+ if err != nil {
+ return nil, err
+ }
+
+ allCaps, err := capabilities.BoundingSet()
+ if err != nil {
+ return nil, err
+ }
+ if options.Privileged {
+ pspec.Capabilities.Bounding = allCaps
+ } else {
+ pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
+ }
+ if execUser.Uid == 0 {
+ pspec.Capabilities.Effective = pspec.Capabilities.Bounding
+ pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
+ pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
+ pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
+ } else {
+ if user == c.config.User {
+ pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
+ pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
+ pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
+ pspec.Capabilities.Ambient = ctrSpec.Process.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 nil, err
+ }
+
+ if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
+ return nil, err
+ }
+ return f, nil
+}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 1c7089e5d..f26ca67ce 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -22,7 +22,6 @@ import (
"text/template"
"time"
- "github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
conmonConfig "github.com/containers/conmon/runner/config"
"github.com/containers/podman/v3/libpod/define"
@@ -30,7 +29,6 @@ import (
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
"github.com/containers/podman/v3/pkg/errorhandling"
- "github.com/containers/podman/v3/pkg/lookup"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/podman/v3/utils"
@@ -1195,124 +1193,6 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
return nil
}
-// prepareProcessExec returns the path of the process.json used in runc exec -p
-// caller is responsible to close the returned *os.File if needed.
-func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessionID string) (*os.File, error) {
- f, err := ioutil.TempFile(c.execBundlePath(sessionID), "exec-process-")
- if err != nil {
- return nil, err
- }
- pspec := new(spec.Process)
- if err := JSONDeepCopy(c.config.Spec.Process, pspec); err != nil {
- return nil, err
- }
- pspec.SelinuxLabel = c.config.ProcessLabel
- pspec.Args = options.Cmd
-
- // We need to default this to false else it will inherit terminal as true
- // from the container.
- pspec.Terminal = false
- if options.Terminal {
- pspec.Terminal = true
- }
- if len(env) > 0 {
- pspec.Env = append(pspec.Env, env...)
- }
-
- if options.Cwd != "" {
- pspec.Cwd = options.Cwd
- }
-
- var addGroups []string
- var sgids []uint32
-
- // if the user is empty, we should inherit the user that the container is currently running with
- user := options.User
- if user == "" {
- user = c.config.User
- addGroups = c.config.Groups
- }
-
- overrides := c.getUserOverrides()
- execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, overrides)
- if err != nil {
- return nil, err
- }
-
- if len(addGroups) > 0 {
- sgids, err = lookup.GetContainerGroups(addGroups, c.state.Mountpoint, overrides)
- if err != nil {
- return nil, 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
- }
-
- ctrSpec, err := c.specFromState()
- if err != nil {
- return nil, err
- }
-
- allCaps, err := capabilities.BoundingSet()
- if err != nil {
- return nil, err
- }
- if options.Privileged {
- pspec.Capabilities.Bounding = allCaps
- } else {
- pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
- }
- if execUser.Uid == 0 {
- pspec.Capabilities.Effective = pspec.Capabilities.Bounding
- pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
- pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
- pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
- } else {
- if user == c.config.User {
- pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
- pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
- pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
- pspec.Capabilities.Ambient = ctrSpec.Process.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 nil, err
- }
-
- if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
- return nil, err
- }
- return f, nil
-}
-
// configureConmonEnv gets the environment values to add to conmon's exec struct
// TODO this may want to be less hardcoded/more configurable in the future
func (r *ConmonOCIRuntime) configureConmonEnv(ctr *Container, runtimeDir string) ([]string, []*os.File) {