diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal.go | 2 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 59 | ||||
-rw-r--r-- | libpod/container_internal_unsupported.go | 4 | ||||
-rw-r--r-- | libpod/image/image.go | 4 | ||||
-rw-r--r-- | libpod/image/image_test.go | 8 | ||||
-rw-r--r-- | libpod/networking_linux.go | 62 | ||||
-rw-r--r-- | libpod/oci_internal_linux.go | 33 | ||||
-rw-r--r-- | libpod/runtime.go | 49 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 4 |
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...) } |