mirror of https://github.com/containers/podman.git
podman top: join the container userns
When we execute ps(1) in the container and the container uses a userns with a different id mapping the user id field will be wrong. To fix this we must join the userns in such case. Fixes #22293 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
parent
5e27243935
commit
65ed96585d
|
@ -3,6 +3,7 @@
|
|||
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mount.h>
|
||||
|
@ -11,6 +12,7 @@
|
|||
|
||||
/* keep special_exit_code in sync with container_top_linux.go */
|
||||
int special_exit_code = 255;
|
||||
int join_userns = 0;
|
||||
char **argv = NULL;
|
||||
|
||||
void
|
||||
|
@ -33,6 +35,12 @@ set_argv (int pos, char *arg)
|
|||
argv[pos] = arg;
|
||||
}
|
||||
|
||||
void
|
||||
set_userns ()
|
||||
{
|
||||
join_userns = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
We use cgo code here so we can fork then exec separately,
|
||||
this is done so we can mount proc after the fork because the pid namespace is
|
||||
|
@ -64,6 +72,23 @@ fork_exec_ps ()
|
|||
fprintf (stderr, "mount proc: %m");
|
||||
exit (special_exit_code);
|
||||
}
|
||||
if (join_userns)
|
||||
{
|
||||
// join the userns to make sure uid mapping match
|
||||
// we are already part of the pidns so so pid 1 is the main container process
|
||||
r = open ("/proc/1/ns/user", O_CLOEXEC | O_RDONLY);
|
||||
if (r < 0)
|
||||
{
|
||||
fprintf (stderr, "open /proc/1/ns/user: %m");
|
||||
exit (special_exit_code);
|
||||
}
|
||||
if ((status = setns (r, CLONE_NEWUSER)) < 0)
|
||||
{
|
||||
fprintf (stderr, "setns NEWUSER: %m");
|
||||
exit (special_exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
/* use execve to unset all env vars, we do not want to leak anything into the container */
|
||||
execve (argv[0], argv, NULL);
|
||||
fprintf (stderr, "execve: %m");
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
void fork_exec_ps();
|
||||
void create_argv(int len);
|
||||
void set_argv(int pos, char *arg);
|
||||
void set_userns();
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
@ -56,13 +57,13 @@ func podmanTopMain() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
// podmanTopInner os.Args = {command name} {pid} {psPath} [args...]
|
||||
// podmanTopInner os.Args = {command name} {pid} {userns(1/0)} {psPath} [args...]
|
||||
// We are rexxec'd in a new mountns, then we need to set some security settings in order
|
||||
// to safely execute ps in the container pid namespace. Most notably make sure podman and
|
||||
// ps are read only to prevent a process from overwriting it.
|
||||
func podmanTopInner() error {
|
||||
if len(os.Args) < 3 {
|
||||
return fmt.Errorf("internal error, need at least two arguments")
|
||||
if len(os.Args) < 4 {
|
||||
return fmt.Errorf("internal error, need at least three arguments")
|
||||
}
|
||||
|
||||
// We have to lock the thread as we a) switch namespace below and b) use PR_SET_PDEATHSIG
|
||||
|
@ -84,7 +85,7 @@ func podmanTopInner() error {
|
|||
return fmt.Errorf("make / mount private: %w", err)
|
||||
}
|
||||
|
||||
psPath := os.Args[2]
|
||||
psPath := os.Args[3]
|
||||
|
||||
// try to mount everything read only
|
||||
if err := unix.MountSetattr(0, "/", unix.AT_RECURSIVE, &unix.MountAttr{
|
||||
|
@ -122,8 +123,13 @@ func podmanTopInner() error {
|
|||
}
|
||||
pidFD.Close()
|
||||
|
||||
userns := os.Args[2]
|
||||
if userns == "1" {
|
||||
C.set_userns()
|
||||
}
|
||||
|
||||
args := []string{psPath}
|
||||
args = append(args, os.Args[3:]...)
|
||||
args = append(args, os.Args[4:]...)
|
||||
|
||||
C.create_argv(C.int(len(args)))
|
||||
for i, arg := range args {
|
||||
|
@ -317,7 +323,14 @@ func (c *Container) execPS(psArgs []string) ([]string, bool, error) {
|
|||
wPipe.Close()
|
||||
return nil, true, err
|
||||
}
|
||||
args := append([]string{podmanTopCommand, strconv.Itoa(c.state.PID), psPath}, psArgs...)
|
||||
|
||||
// see podmanTopInner()
|
||||
userns := "0"
|
||||
if len(c.config.IDMappings.UIDMap) > 0 {
|
||||
userns = "1"
|
||||
}
|
||||
|
||||
args := append([]string{podmanTopCommand, strconv.Itoa(c.state.PID), userns, psPath}, psArgs...)
|
||||
|
||||
cmd := reexec.Command(args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
|
|
|
@ -119,6 +119,18 @@ var _ = Describe("Podman top", func() {
|
|||
exec := podmanTest.Podman([]string{"top", session.OutputToString(), "aux"})
|
||||
exec.WaitWithDefaultTimeout()
|
||||
Expect(exec).Should(ExitWithError(125, "OCI runtime attempted to invoke a command that was not found"))
|
||||
|
||||
session = podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1000:1000", "--user", "9", fedoraMinimal, "sleep", "inf"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
|
||||
result = podmanTest.Podman([]string{"top", session.OutputToString(), "-ef", "hn"})
|
||||
result.WaitWithDefaultTimeout()
|
||||
Expect(result).Should(ExitCleanly())
|
||||
output := result.OutputToString()
|
||||
Expect(output).To(ContainSubstring("sleep inf"))
|
||||
// check for https://github.com/containers/podman/issues/22293
|
||||
Expect(output).To(HavePrefix("9 "), "user id of process")
|
||||
})
|
||||
|
||||
It("podman top with comma-separated options", func() {
|
||||
|
|
Loading…
Reference in New Issue