Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rootless: automatically create a systemd scope #3959

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 70 additions & 10 deletions cmd/podman/main_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ package main

import (
"context"
"fmt"
"io/ioutil"
"log/syslog"
"os"
"runtime/pprof"
"strconv"
"strings"
"syscall"

Expand All @@ -18,6 +21,7 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/tracing"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -119,7 +123,29 @@ func profileOff(cmd *cobra.Command) error {
return nil
}

func movePauseProcessToScope() error {
pausePidPath, err := util.GetRootlessPauseProcessPidPath()
if err != nil {
return errors.Wrapf(err, "could not get pause process pid file path")
}

data, err := ioutil.ReadFile(pausePidPath)
if err != nil {
return errors.Wrapf(err, "cannot read pause pid file")
}
pid, err := strconv.ParseUint(string(data), 10, 0)
if err != nil {
return errors.Wrapf(err, "cannot parse pid file %s", pausePidPath)
}

return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope")
}

func setupRootless(cmd *cobra.Command, args []string) error {
if !rootless.IsRootless() {
return nil
}

matches, err := rootless.ConfigurationMatches()
if err != nil {
return err
Expand All @@ -128,9 +154,6 @@ func setupRootless(cmd *cobra.Command, args []string) error {
logrus.Warningf("the current user namespace doesn't match the configuration in /etc/subuid or /etc/subgid")
logrus.Warningf("you can use `%s system migrate` to recreate the user namespace and restart the containers", os.Args[0])
}
if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || cmd == _migrateCommand || strings.HasPrefix(cmd.Use, "help") {
return nil
}

podmanCmd := cliconfig.PodmanCommand{
Command: cmd,
Expand All @@ -139,6 +162,39 @@ func setupRootless(cmd *cobra.Command, args []string) error {
Remote: remoteclient,
}

runtime, err := libpodruntime.GetRuntime(getContext(), &podmanCmd)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.DeferredShutdown(false)

// do it only after podman has already re-execed and running with uid==0.
if os.Geteuid() == 0 {
ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
if err != nil {
return err
}

if !ownsCgroup {
unitName := fmt.Sprintf("podman-%d.scope", os.Getpid())
if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil {
conf, err := runtime.GetConfig()
if err != nil {
return err
}
if conf.CgroupManager == libpod.SystemdCgroupsManager {
logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err)
} else {
logrus.Debugf("Failed to add podman to systemd sandbox cgroup: %v", err)
}
}
}
}

if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || cmd == _migrateCommand || strings.HasPrefix(cmd.Use, "help") {
return nil
}

pausePidPath, err := util.GetRootlessPauseProcessPidPath()
if err != nil {
return errors.Wrapf(err, "could not get pause process pid file path")
Expand All @@ -158,13 +214,6 @@ func setupRootless(cmd *cobra.Command, args []string) error {
}

// if there is no pid file, try to join existing containers, and create a pause process.

runtime, err := libpodruntime.GetRuntime(getContext(), &podmanCmd)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.DeferredShutdown(false)

ctrs, err := runtime.GetRunningContainers()
if err != nil {
logrus.Errorf(err.Error())
Expand All @@ -177,6 +226,17 @@ func setupRootless(cmd *cobra.Command, args []string) error {
}

became, ret, err := rootless.TryJoinFromFilePaths(pausePidPath, true, paths)
if err := movePauseProcessToScope(); err != nil {
conf, err := runtime.GetConfig()
if err != nil {
return err
}
if conf.CgroupManager == libpod.SystemdCgroupsManager {
logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err)
} else {
logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err)
}
}
if err != nil {
logrus.Errorf(err.Error())
os.Exit(1)
Expand Down
54 changes: 33 additions & 21 deletions libpod/oci_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/lookup"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/coreos/go-systemd/activation"
Expand Down Expand Up @@ -359,35 +360,46 @@ func startCommandGivenSelinux(cmd *exec.Cmd) error {
// moveConmonToCgroupAndSignal gets a container's cgroupParent and moves the conmon process to that cgroup
// it then signals for conmon to start by sending nonse data down the start fd
func (r *OCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec.Cmd, startFd *os.File, uuid string) error {
mustCreateCgroup := true
// If cgroup creation is disabled - just signal.
if ctr.config.NoCgroups {
return writeConmonPipeData(startFd)
mustCreateCgroup = false
}

cgroupParent := ctr.CgroupParent()
if r.cgroupManager == SystemdCgroupsManager {
unitName := createUnitName("libpod-conmon", ctr.ID())

realCgroupParent := cgroupParent
splitParent := strings.Split(cgroupParent, "/")
if strings.HasSuffix(cgroupParent, ".slice") && len(splitParent) > 1 {
realCgroupParent = splitParent[len(splitParent)-1]
if rootless.IsRootless() {
ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup()
if err != nil {
return err
}
mustCreateCgroup = !ownsCgroup
}

logrus.Infof("Running conmon under slice %s and unitName %s", realCgroupParent, unitName)
if err := utils.RunUnderSystemdScope(cmd.Process.Pid, realCgroupParent, unitName); err != nil {
logrus.Warnf("Failed to add conmon to systemd sandbox cgroup: %v", err)
}
} else {
cgroupPath := filepath.Join(ctr.config.CgroupParent, "conmon")
control, err := cgroups.New(cgroupPath, &spec.LinuxResources{})
if err != nil {
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
if mustCreateCgroup {
cgroupParent := ctr.CgroupParent()
if r.cgroupManager == SystemdCgroupsManager {
unitName := createUnitName("libpod-conmon", ctr.ID())

realCgroupParent := cgroupParent
splitParent := strings.Split(cgroupParent, "/")
if strings.HasSuffix(cgroupParent, ".slice") && len(splitParent) > 1 {
realCgroupParent = splitParent[len(splitParent)-1]
}

logrus.Infof("Running conmon under slice %s and unitName %s", realCgroupParent, unitName)
if err := utils.RunUnderSystemdScope(cmd.Process.Pid, realCgroupParent, unitName); err != nil {
logrus.Warnf("Failed to add conmon to systemd sandbox cgroup: %v", err)
}
} else {
// we need to remove this defer and delete the cgroup once conmon exits
// maybe need a conmon monitor?
if err := control.AddPid(cmd.Process.Pid); err != nil {
cgroupPath := filepath.Join(ctr.config.CgroupParent, "conmon")
control, err := cgroups.New(cgroupPath, &spec.LinuxResources{})
if err != nil {
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
} else {
// we need to remove this defer and delete the cgroup once conmon exits
// maybe need a conmon monitor?
if err := control.AddPid(cmd.Process.Pid); err != nil {
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
}
}
}
}
Expand Down
62 changes: 62 additions & 0 deletions pkg/cgroups/cgroups_supported.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
package cgroups

import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"syscall"

"github.com/pkg/errors"
)

var (
Expand All @@ -25,3 +32,58 @@ func IsCgroup2UnifiedMode() (bool, error) {
})
return isUnified, isUnifiedErr
}

// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
// current cgroup.
func UserOwnsCurrentSystemdCgroup() (bool, error) {
uid := os.Geteuid()

cgroup2, err := IsCgroup2UnifiedMode()
if err != nil {
return false, err
}

f, err := os.Open("/proc/self/cgroup")
if err != nil {
return false, errors.Wrapf(err, "open file /proc/self/cgroup")
}
defer f.Close()

scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
parts := strings.SplitN(line, ":", 3)

if len(parts) < 3 {
continue
}

var cgroupPath string

if cgroup2 {
cgroupPath = filepath.Join(cgroupRoot, parts[2])
} else {
if parts[1] != "name=systemd" {
continue
}
cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
}

st, err := os.Stat(cgroupPath)
if err != nil {
return false, err
}
s := st.Sys()
giuseppe marked this conversation as resolved.
Show resolved Hide resolved
if s == nil {
return false, fmt.Errorf("error stat cgroup path %s", cgroupPath)
}

if int(s.(*syscall.Stat_t).Uid) != uid {
return false, nil
}
}
if err := scanner.Err(); err != nil {
return false, errors.Wrapf(err, "parsing file /proc/self/cgroup")
}
return true, nil
}
6 changes: 6 additions & 0 deletions pkg/cgroups/cgroups_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ package cgroups
func IsCgroup2UnifiedMode() (bool, error) {
return false, nil
}

// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
// current cgroup.
func UserOwnsCurrentSystemdCgroup() (bool, error) {
return false, nil
}
18 changes: 15 additions & 3 deletions utils/utils_supported.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@
package utils

import (
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/rootless"
systemdDbus "github.com/coreos/go-systemd/dbus"
"github.com/godbus/dbus"
)

// RunUnderSystemdScope adds the specified pid to a systemd scope
func RunUnderSystemdScope(pid int, slice string, unitName string) error {
var properties []systemdDbus.Property
conn, err := systemdDbus.New()
if err != nil {
return err
var conn *systemdDbus.Conn
var err error

if rootless.IsRootless() {
conn, err = cgroups.GetUserConnection(rootless.GetRootlessUID())
if err != nil {
return err
}
} else {
conn, err = systemdDbus.New()
if err != nil {
return err
}
}
properties = append(properties, systemdDbus.PropSlice(slice))
properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
Expand Down