summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/container_internal_linux.go59
-rw-r--r--libpod/container_internal_unsupported.go4
-rw-r--r--libpod/image/image.go4
-rw-r--r--libpod/image/image_test.go8
-rw-r--r--libpod/networking_linux.go62
-rw-r--r--libpod/oci_internal_linux.go33
-rw-r--r--libpod/runtime.go49
-rw-r--r--libpod/runtime_pod_infra_linux.go4
9 files changed, 185 insertions, 40 deletions
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 42028c397..f1456548b 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -622,7 +622,7 @@ func (c *Container) refresh() error {
return err
}
- return nil
+ return c.refreshCNI()
}
// Remove conmon attach socket and terminal resize FIFO
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 4bbbef5db..2636fdb6c 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -78,15 +78,21 @@ func (c *Container) prepare() (Err error) {
// Set up network namespace if not already set up
if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS {
netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c)
+ if createNetNSErr != nil {
+ return
+ }
tmpStateLock.Lock()
defer tmpStateLock.Unlock()
// Assign NetNS attributes to container
- if createNetNSErr == nil {
- c.state.NetNS = netNS
- c.state.NetworkStatus = networkStatus
- }
+ c.state.NetNS = netNS
+ c.state.NetworkStatus = networkStatus
+ }
+
+ // handle rootless network namespace setup
+ if c.state.NetNS != nil && c.config.NetMode == "slirp4netns" && !c.config.PostConfigureNetNS {
+ createNetNSErr = c.runtime.setupRootlessNetNS(c)
}
}()
// Mount storage if not mounted
@@ -181,6 +187,30 @@ func (c *Container) cleanupNetwork() error {
return nil
}
+func (c *Container) getUserOverrides() *lookup.Overrides {
+ var hasPasswdFile, hasGroupFile bool
+ overrides := lookup.Overrides{}
+ for _, m := range c.config.Spec.Mounts {
+ if m.Destination == "/etc/passwd" {
+ overrides.ContainerEtcPasswdPath = m.Source
+ hasPasswdFile = true
+ }
+ if m.Destination == "/etc/group" {
+ overrides.ContainerEtcGroupPath = m.Source
+ hasGroupFile = true
+ }
+ if m.Destination == "/etc" {
+ if !hasPasswdFile {
+ overrides.ContainerEtcPasswdPath = filepath.Join(m.Source, "passwd")
+ }
+ if !hasGroupFile {
+ overrides.ContainerEtcGroupPath = filepath.Join(m.Source, "group")
+ }
+ }
+ }
+ return &overrides
+}
+
// Generate spec for a container
// Accepts a map of the container's dependencies
func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
@@ -188,7 +218,8 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
span.SetTag("type", "container")
defer span.Finish()
- execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, nil)
+ overrides := c.getUserOverrides()
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides)
if err != nil {
return nil, err
}
@@ -279,6 +310,17 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
+ hasHomeSet := false
+ for _, s := range c.config.Spec.Process.Env {
+ if strings.HasPrefix(s, "HOME=") {
+ hasHomeSet = true
+ break
+ }
+ }
+ if !hasHomeSet {
+ c.config.Spec.Process.Env = append(c.config.Spec.Process.Env, fmt.Sprintf("HOME=%s", execUser.Home))
+ }
+
if c.config.User != "" {
// User and Group must go together
g.SetProcessUID(uint32(execUser.Uid))
@@ -1283,3 +1325,10 @@ func (c *Container) copyOwnerAndPerms(source, dest string) error {
}
return nil
}
+
+// Teardown CNI config on refresh
+func (c *Container) refreshCNI() error {
+ // Let's try and delete any lingering network config...
+ podNetwork := c.runtime.getPodNetwork(c.ID(), c.config.Name, "", c.config.Networks, c.config.PortMappings, c.config.StaticIP)
+ return c.runtime.netPlugin.TearDownPod(podNetwork)
+}
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index 6fa19a778..05a587c59 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -40,3 +40,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
func (c *Container) copyOwnerAndPerms(source, dest string) error {
return nil
}
+
+func (c *Container) refreshCNI() error {
+ return define.ErrNotImplemented
+}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 0be6eeeb9..855da8611 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -29,7 +29,6 @@ import (
"github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
- "github.com/containers/storage/pkg/reexec"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -85,9 +84,6 @@ func NewImageRuntimeFromStore(store storage.Store) *Runtime {
// NewImageRuntimeFromOptions creates an Image Runtime including the store given
// store options
func NewImageRuntimeFromOptions(options storage.StoreOptions) (*Runtime, error) {
- if reexec.Init() {
- return nil, errors.Errorf("unable to reexec")
- }
store, err := setStore(options)
if err != nil {
return nil, err
diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go
index 5a6d095f6..ef39d09c3 100644
--- a/libpod/image/image_test.go
+++ b/libpod/image/image_test.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
+ "github.com/containers/storage/pkg/reexec"
"github.com/opencontainers/go-digest"
"github.com/stretchr/testify/assert"
)
@@ -70,6 +71,13 @@ func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) {
}
+func TestMain(m *testing.M) {
+ if reexec.Init() {
+ return
+ }
+ os.Exit(m.Run())
+}
+
// TestImage_NewFromLocal tests finding the image locally by various names,
// tags, and aliases
func TestImage_NewFromLocal(t *testing.T) {
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 67dd0150b..d854a2de6 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -90,9 +90,6 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
// Create and configure a new network namespace for a container
func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, err error) {
- if rootless.IsRootless() {
- return nil, nil, errors.New("cannot configure a new network namespace in rootless mode, only --network=slirp4netns is supported")
- }
ctrNS, err := netns.NewNS()
if err != nil {
return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
@@ -110,7 +107,10 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result,
logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
- networkStatus, err := r.configureNetNS(ctr, ctrNS)
+ networkStatus := []*cnitypes.Result{}
+ if !rootless.IsRootless() {
+ networkStatus, err = r.configureNetNS(ctr, ctrNS)
+ }
return ctrNS, networkStatus, err
}
@@ -138,9 +138,6 @@ func checkSlirpFlags(path string) (bool, bool, bool, error) {
// Configure the network namespace for a rootless container
func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
- defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR)
- defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW)
-
path := r.config.NetworkCmdPath
if path == "" {
@@ -164,7 +161,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
cmdArgs := []string{}
if havePortMapping {
- cmdArgs = append(cmdArgs, "--api-socket", apiSocket, fmt.Sprintf("%d", ctr.state.PID))
+ cmdArgs = append(cmdArgs, "--api-socket", apiSocket)
}
dhp, mtu, sandbox, err := checkSlirpFlags(path)
if err != nil {
@@ -179,13 +176,32 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
if sandbox {
cmdArgs = append(cmdArgs, "--enable-sandbox")
}
- cmdArgs = append(cmdArgs, "-c", "-e", "3", "-r", "4", fmt.Sprintf("%d", ctr.state.PID), "tap0")
- cmd := exec.Command(path, cmdArgs...)
+ // the slirp4netns arguments being passed are describes as follows:
+ // from the slirp4netns documentation: https://github.com/rootless-containers/slirp4netns
+ // -c, --configure Brings up the tap interface
+ // -e, --exit-fd=FD specify the FD for terminating slirp4netns
+ // -r, --ready-fd=FD specify the FD to write to when the initialization steps are finished
+ cmdArgs = append(cmdArgs, "-c", "-e", "3", "-r", "4")
+ if !ctr.config.PostConfigureNetNS {
+ ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe()
+ if err != nil {
+ return errors.Wrapf(err, "failed to create rootless network sync pipe")
+ }
+ cmdArgs = append(cmdArgs, "--netns-type=path", ctr.state.NetNS.Path(), "tap0")
+ } else {
+ defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR)
+ defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW)
+ cmdArgs = append(cmdArgs, fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ }
+ cmd := exec.Command(path, cmdArgs...)
+ logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " "))
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
+
+ // Leak one end of the pipe in slirp4netns, the other will be sent to conmon
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncR, syncW)
if err := cmd.Start(); err != nil {
@@ -388,20 +404,22 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
- var requestedIP net.IP
- if ctr.requestedIP != nil {
- requestedIP = ctr.requestedIP
- // cancel request for a specific IP in case the container is reused later
- ctr.requestedIP = nil
- } else {
- requestedIP = ctr.config.StaticIP
- }
+ // rootless containers do not use the CNI plugin
+ if !rootless.IsRootless() {
+ var requestedIP net.IP
+ if ctr.requestedIP != nil {
+ requestedIP = ctr.requestedIP
+ // cancel request for a specific IP in case the container is reused later
+ ctr.requestedIP = nil
+ } else {
+ requestedIP = ctr.config.StaticIP
+ }
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
- // The network may have already been torn down, so don't fail here, just log
- if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
- return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
+ if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
+ return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
+ }
}
// First unmount the namespace
diff --git a/libpod/oci_internal_linux.go b/libpod/oci_internal_linux.go
index 4df1e4010..a5cce795b 100644
--- a/libpod/oci_internal_linux.go
+++ b/libpod/oci_internal_linux.go
@@ -131,9 +131,14 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Containe
}
if ctr.config.NetMode.IsSlirp4netns() {
- ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe()
- if err != nil {
- return errors.Wrapf(err, "failed to create rootless network sync pipe")
+ if ctr.config.PostConfigureNetNS {
+ ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe()
+ if err != nil {
+ return errors.Wrapf(err, "failed to create rootless network sync pipe")
+ }
+ } else {
+ defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR)
+ defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncW)
}
// Leak one end in conmon, the other one will be leaked into slirp4netns
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncW)
@@ -200,13 +205,16 @@ func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, se
pspec.Cwd = cwd
}
+
+ overrides := c.getUserOverrides()
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, overrides)
+ if err != nil {
+ return nil, err
+ }
+
// If user was set, look it up in the container to get a UID to use on
// the host
if user != "" {
- execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, nil)
- if err != nil {
- return nil, err
- }
sgids := make([]uint32, 0, len(execUser.Sgids))
for _, sgid := range execUser.Sgids {
sgids = append(sgids, uint32(sgid))
@@ -220,6 +228,17 @@ func prepareProcessExec(c *Container, cmd, env []string, tty bool, cwd, user, se
pspec.User = processUser
}
+ 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
diff --git a/libpod/runtime.go b/libpod/runtime.go
index e2b9667be..675c92b7a 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -23,6 +23,7 @@ import (
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/lock"
+ "github.com/containers/libpod/pkg/cgroups"
sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
@@ -254,6 +255,8 @@ type RuntimeConfig struct {
// SDNotify tells Libpod to allow containers to notify the host
// systemd of readiness using the SD_NOTIFY mechanism
SDNotify bool
+ // CgroupCheck verifies if the cgroup check for correct OCI runtime has been done.
+ CgroupCheck bool `toml:"cgroup_check,omitempty"`
}
// runtimeConfiguredFrom is a struct used during early runtime init to help
@@ -575,6 +578,10 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
configPath)
}
+ if err := cgroupV2Check(configPath, tmpConfig); err != nil {
+ return nil, err
+ }
+
if tmpConfig.StaticDir != "" {
runtime.configuredFrom.libpodStaticDirSet = true
}
@@ -664,6 +671,14 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
runtime.config.OCIRuntime = tmpConfig.OCIRuntime
}
+ cgroupsV2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return nil, err
+ }
+ if cgroupsV2 {
+ runtime.config.CgroupCheck = true
+ }
+
break
}
}
@@ -1451,3 +1466,37 @@ func (r *Runtime) ImageRuntime() *image.Runtime {
func (r *Runtime) SystemContext() *types.SystemContext {
return r.imageContext
}
+
+// Since runc does not currently support cgroupV2
+// Change to default crun on first running of libpod.conf
+// TODO Once runc has support for cgroups, this function should be removed.
+func cgroupV2Check(configPath string, tmpConfig *RuntimeConfig) error {
+ if !tmpConfig.CgroupCheck && rootless.IsRootless() {
+ cgroupsV2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil {
+ return err
+ }
+ if cgroupsV2 {
+ path, err := exec.LookPath("crun")
+ if err != nil {
+ logrus.Warnf("Can not find crun package on the host, containers might fail to run on cgroup V2 systems without crun: %q", err)
+ // Can't find crun path so do nothing
+ return nil
+ }
+ tmpConfig.CgroupCheck = true
+ tmpConfig.OCIRuntime = path
+ file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE, 0666)
+ if err != nil {
+ return errors.Wrapf(err, "cannot open file %s", configPath)
+ }
+ defer file.Close()
+ enc := toml.NewEncoder(file)
+ if err := enc.Encode(tmpConfig); err != nil {
+ if removeErr := os.Remove(configPath); removeErr != nil {
+ logrus.Debugf("unable to remove %s: %q", configPath, err)
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index ad6662f03..6a27c2800 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -99,7 +99,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID
if isRootless {
netmode = "slirp4netns"
}
- options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, netmode, networks))
+ // PostConfigureNetNS should not be set since user namespace sharing is not implemented
+ // and rootless networking no longer supports post configuration setup
+ options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, networks))
return r.newContainer(ctx, g.Config, options...)
}