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

Fix rootless socket activation #9928

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
58 changes: 46 additions & 12 deletions pkg/rootless/rootless_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ static int open_files_max_fd;
static fd_set *open_files_set;
static uid_t rootless_uid_init;
static gid_t rootless_gid_init;
static bool do_socket_activation = false;
static char *saved_systemd_listen_fds;
static char *saved_systemd_listen_pid;
static char *saved_systemd_listen_fdnames;

static int
syscall_setresuid (uid_t ruid, uid_t euid, uid_t suid)
Expand Down Expand Up @@ -242,6 +246,10 @@ static void __attribute__((constructor)) init()
{
const char *xdg_runtime_dir;
const char *pause;
const char *listen_pid;
const char *listen_fds;
const char *listen_fdnames;

DIR *d;

pause = getenv ("_PODMAN_PAUSE");
Expand Down Expand Up @@ -293,6 +301,26 @@ static void __attribute__((constructor)) init()
closedir (d);
}

listen_pid = getenv("LISTEN_PID");
listen_fds = getenv("LISTEN_FDS");
listen_fdnames = getenv("LISTEN_FDNAMES");

if (listen_pid != NULL && listen_fds != NULL && strtol(listen_pid, NULL, 10) == getpid())
{
// save systemd socket environment for rootless child
do_socket_activation = true;
saved_systemd_listen_pid = strdup(listen_pid);
saved_systemd_listen_fds = strdup(listen_fds);
saved_systemd_listen_fdnames = strdup(listen_fdnames);
if (saved_systemd_listen_pid == NULL
|| saved_systemd_listen_fds == NULL
|| saved_systemd_listen_fdnames == NULL)
{
fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno));
_exit (EXIT_FAILURE);
}
}

/* Shortcut. If we are able to join the pause pid file, do it now so we don't
need to re-exec. */
xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
Expand Down Expand Up @@ -635,6 +663,12 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
for (f = 3; f <= open_files_max_fd; f++)
if (is_fd_inherited (f))
close (f);
if (do_socket_activation)
{
unsetenv ("LISTEN_PID");
unsetenv ("LISTEN_FDS");
unsetenv ("LISTEN_FDNAMES");
}

return pid;
}
Expand All @@ -660,6 +694,15 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
_exit (EXIT_FAILURE);
}

if (do_socket_activation)
{
char s[32];
sprintf (s, "%d", getpid());
setenv ("LISTEN_PID", s, true);
setenv ("LISTEN_FDS", saved_systemd_listen_fds, true);
setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true);
}

setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
setenv ("_CONTAINERS_ROOTLESS_UID", uid, 1);
setenv ("_CONTAINERS_ROOTLESS_GID", gid, 1);
Expand Down Expand Up @@ -777,9 +820,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
char **argv;
char uid[16];
char gid[16];
char *listen_fds = NULL;
char *listen_pid = NULL;
bool do_socket_activation = false;
char *cwd = getcwd (NULL, 0);
sigset_t sigset, oldsigset;

Expand All @@ -789,14 +829,6 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
_exit (EXIT_FAILURE);
}

listen_pid = getenv("LISTEN_PID");
listen_fds = getenv("LISTEN_FDS");

if (listen_pid != NULL && listen_fds != NULL)
{
if (strtol(listen_pid, NULL, 10) == getpid())
do_socket_activation = true;
}

sprintf (uid, "%d", geteuid ());
sprintf (gid, "%d", getegid ());
Expand All @@ -814,7 +846,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
{
long num_fds;

num_fds = strtol (listen_fds, NULL, 10);
num_fds = strtol (saved_systemd_listen_fds, NULL, 10);
if (num_fds != LONG_MIN && num_fds != LONG_MAX)
{
int f;
Expand Down Expand Up @@ -863,6 +895,8 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
char s[32];
sprintf (s, "%d", getpid());
setenv ("LISTEN_PID", s, true);
setenv ("LISTEN_FDS", saved_systemd_listen_fds, true);
setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true);
}

setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1);
Expand Down
103 changes: 103 additions & 0 deletions test/system/270-socket-activation.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env bats -*- bats -*-
#
# Tests podman system service under systemd socket activation
#

load helpers

SERVICE_NAME="podman_test_$(random_string)"

SYSTEMCTL="systemctl"
UNIT_DIR="/usr/lib/systemd/system"
SERVICE_SOCK_ADDR="/run/podman/podman.sock"

if is_rootless; then
UNIT_DIR="$HOME/.config/systemd/user"
mkdir -p $UNIT_DIR

SYSTEMCTL="$SYSTEMCTL --user"
if [ -z "$XDG_RUNTIME_DIR" ]; then
export XDG_RUNTIME_DIR=/run/user/$(id -u)
fi
SERVICE_SOCK_ADDR="$XDG_RUNTIME_DIR/podman/podman.sock"
fi

SERVICE_FILE="$UNIT_DIR/$SERVICE_NAME.service"
SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket"


function setup() {
skip_if_remote "systemd tests are meaningless over remote"

basic_setup

cat > $SERVICE_FILE <<EOF
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-system-service(1)
StartLimitIntervalSec=0

[Service]
Type=exec
KillMode=process
Environment=LOGGING="--log-level=info"
ExecStart=$PODMAN $LOGGING system service -t 2
EOF
cat > $SOCKET_FILE <<EOF
[Unit]
Description=Podman API Socket
Documentation=man:podman-system-service(1)

[Socket]
ListenStream=%t/podman/podman.sock
SocketMode=0660

[Install]
WantedBy=sockets.target
EOF

# ensure pause die before each test runs
if is_rootless; then
local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid"
if [ -f $pause_pid ]; then
kill -9 $(cat $pause_pid) 2> /dev/null
rm -f $pause_pid
fi
fi
$SYSTEMCTL start "$SERVICE_NAME.socket"
}

function teardown() {
$SYSTEMCTL stop "$SERVICE_NAME.socket"
rm -f "$SERVICE_FILE" "$SOCKET_FILE"
$SYSTEMCTL daemon-reload
basic_teardown
}

@test "podman system service - socket activation - no container" {
run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping
is "$output" "OK" "podman service responses normally"
}

@test "podman system service - socket activation - exist container " {
run_podman run $IMAGE sleep 90
run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping
is "$output" "OK" "podman service responses normally"
}

@test "podman system service - socket activation - kill rootless pause " {
if ! is_rootless; then
skip "root podman no need pause process"
fi
run_podman run $IMAGE sleep 90
local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid"
if [ -f $pause_pid ]; then
kill -9 $(cat $pause_pid) 2> /dev/null
fi
run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping
is "$output" "OK" "podman service responses normally"
}

# vim: filetype=sh