mirror of
https://github.com/containers/podman.git
synced 2025-07-24 16:04:50 +08:00
rootless: add cli validator
whenever the podman process is launched, it runs any file found in these directories: - /etc/containers/auth-scripts - /usr/libexec/podman/auth-scripts The current podman command line is passed as arguments to the process. If any of the processes fail, the error is immediately reported back from podman that exits with the same error code. [NO NEW TESTS NEEDED] requires a system-wide configuration. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
@ -8,6 +8,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/wait.h>
|
||||
@ -19,6 +20,7 @@
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define ETC_AUTH_SCRIPTS "/etc/containers/auth-scripts"
|
||||
#define LIBEXECPODMAN "/usr/libexec/podman"
|
||||
|
||||
#ifndef TEMP_FAILURE_RETRY
|
||||
@ -119,6 +121,155 @@ rootless_gid ()
|
||||
return rootless_gid_init;
|
||||
}
|
||||
|
||||
/* exec the specified executable and exit if it fails. */
|
||||
static void
|
||||
exec_binary (const char *path, char **argv, int argc)
|
||||
{
|
||||
int r, status = 0;
|
||||
pid_t pid;
|
||||
|
||||
pid = fork ();
|
||||
if (pid < 0)
|
||||
{
|
||||
fprintf (stderr, "fork: %m\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (pid == 0)
|
||||
{
|
||||
size_t i;
|
||||
char **newargv = malloc ((argc + 2) * sizeof(char *));
|
||||
if (!newargv)
|
||||
{
|
||||
fprintf (stderr, "malloc: %m\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
newargv[0] = (char*) path;
|
||||
for (i = 0; i < argc; i++)
|
||||
newargv[i+1] = argv[i];
|
||||
|
||||
newargv[i+1] = NULL;
|
||||
errno = 0;
|
||||
execv (path, newargv);
|
||||
/* If the file was deleted in the meanwhile, return success. */
|
||||
if (errno == ENOENT)
|
||||
exit (EXIT_SUCCESS);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
r = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
|
||||
if (r < 0)
|
||||
{
|
||||
fprintf (stderr, "waitpid: %m\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (WIFEXITED(status) && WEXITSTATUS (status))
|
||||
{
|
||||
fprintf (stderr, "external auth script %s failed\n", path);
|
||||
exit (WEXITSTATUS(status));
|
||||
}
|
||||
if (WIFSIGNALED (status))
|
||||
{
|
||||
fprintf (stderr, "external auth script %s failed\n", path);
|
||||
exit (127+WTERMSIG (status));
|
||||
}
|
||||
if (WIFSTOPPED (status))
|
||||
{
|
||||
fprintf (stderr, "external auth script %s failed\n", path);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_auth_scripts_dir (const char *dir, char **argv, int argc)
|
||||
{
|
||||
cleanup_free char *buffer = NULL;
|
||||
cleanup_dir DIR *d = NULL;
|
||||
size_t i, nfiles = 0;
|
||||
struct dirent *de;
|
||||
|
||||
/* Store how many FDs were open before the Go runtime kicked in. */
|
||||
d = opendir (dir);
|
||||
if (!d)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
fprintf (stderr, "opendir %s: %m\n", dir);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
for (de = readdir (d); de; de = readdir (d))
|
||||
{
|
||||
buffer = realloc (buffer, (nfiles + 1) * (NAME_MAX + 1));
|
||||
if (buffer == NULL)
|
||||
{
|
||||
fprintf (stderr, "realloc buffer: %m\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (de->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
strncpy (buffer + nfiles * (NAME_MAX + 1), de->d_name, NAME_MAX + 1);
|
||||
nfiles++;
|
||||
buffer[nfiles * (NAME_MAX + 1)] = '\0';
|
||||
}
|
||||
|
||||
qsort (buffer, nfiles, NAME_MAX + 1, (int (*)(const void *, const void *)) strcmp);
|
||||
|
||||
for (i = 0; i < nfiles; i++)
|
||||
{
|
||||
const char *fname = buffer + i * (NAME_MAX + 1);
|
||||
char path[PATH_MAX];
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
ret = snprintf (path, PATH_MAX, "%s/%s", dir, fname);
|
||||
if (ret == PATH_MAX)
|
||||
{
|
||||
fprintf (stderr, "internal error: path too long\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = stat (path, &st);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Ignore the failure if the file was deleted. */
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
fprintf (stderr, "stat %s: %m\n", path);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Not an executable. */
|
||||
if ((st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
|
||||
continue;
|
||||
|
||||
exec_binary (path, argv, argc);
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
if (errno)
|
||||
{
|
||||
fprintf (stderr, "readdir %s: %m\n", dir);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_auth_scripts (char **argv, int argc)
|
||||
{
|
||||
char *auth_scripts = getenv ("PODMAN_AUTH_SCRIPTS_DIR");
|
||||
do_auth_scripts_dir (LIBEXECPODMAN "/auth-scripts", argv, argc);
|
||||
do_auth_scripts_dir (ETC_AUTH_SCRIPTS, argv, argc);
|
||||
if (auth_scripts && auth_scripts[0])
|
||||
do_auth_scripts_dir (auth_scripts, argv, argc);
|
||||
}
|
||||
|
||||
static void
|
||||
do_pause ()
|
||||
{
|
||||
@ -146,7 +297,7 @@ do_pause ()
|
||||
}
|
||||
|
||||
static char **
|
||||
get_cmd_line_args ()
|
||||
get_cmd_line_args (int *argc_out)
|
||||
{
|
||||
cleanup_free char *buffer = NULL;
|
||||
cleanup_close int fd = -1;
|
||||
@ -206,13 +357,15 @@ get_cmd_line_args ()
|
||||
/* Move ownership. */
|
||||
buffer = NULL;
|
||||
|
||||
if (argc_out)
|
||||
*argc_out = argc;
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
static bool
|
||||
can_use_shortcut ()
|
||||
can_use_shortcut (char **argv)
|
||||
{
|
||||
cleanup_free char **argv = NULL;
|
||||
cleanup_free char *argv0 = NULL;
|
||||
bool ret = true;
|
||||
int argc;
|
||||
@ -221,10 +374,6 @@ can_use_shortcut ()
|
||||
return false;
|
||||
#endif
|
||||
|
||||
argv = get_cmd_line_args ();
|
||||
if (argv == NULL)
|
||||
return false;
|
||||
|
||||
argv0 = argv[0];
|
||||
|
||||
if (strstr (argv[0], "podman") == NULL)
|
||||
@ -289,7 +438,9 @@ static void __attribute__((constructor)) init()
|
||||
const char *listen_pid;
|
||||
const char *listen_fds;
|
||||
const char *listen_fdnames;
|
||||
cleanup_free char **argv = NULL;
|
||||
cleanup_dir DIR *d = NULL;
|
||||
int argc;
|
||||
|
||||
pause = getenv ("_PODMAN_PAUSE");
|
||||
if (pause && pause[0])
|
||||
@ -339,30 +490,40 @@ static void __attribute__((constructor)) init()
|
||||
}
|
||||
}
|
||||
|
||||
listen_pid = getenv("LISTEN_PID");
|
||||
listen_fds = getenv("LISTEN_FDS");
|
||||
listen_fdnames = getenv("LISTEN_FDNAMES");
|
||||
argv = get_cmd_line_args (&argc);
|
||||
if (argv == NULL)
|
||||
{
|
||||
fprintf(stderr, "cannot retrieve cmd line");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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);
|
||||
if (listen_fdnames != NULL)
|
||||
saved_systemd_listen_fdnames = strdup(listen_fdnames);
|
||||
if (saved_systemd_listen_pid == NULL
|
||||
|| saved_systemd_listen_fds == NULL)
|
||||
{
|
||||
fprintf (stderr, "save socket listen environments error: %m\n");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (geteuid () != 0 || getenv ("_CONTAINERS_USERNS_CONFIGURED") == NULL)
|
||||
do_auth_scripts(argv, argc);
|
||||
|
||||
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);
|
||||
if (listen_fdnames != NULL)
|
||||
saved_systemd_listen_fdnames = strdup(listen_fdnames);
|
||||
if (saved_systemd_listen_pid == NULL
|
||||
|| saved_systemd_listen_fds == NULL)
|
||||
{
|
||||
fprintf (stderr, "save socket listen environments error: %m\n");
|
||||
_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");
|
||||
if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ())
|
||||
if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut (argv))
|
||||
{
|
||||
cleanup_free char *cwd = NULL;
|
||||
cleanup_close int userns_fd = -1;
|
||||
@ -650,7 +811,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
|
||||
sprintf (uid, "%d", geteuid ());
|
||||
sprintf (gid, "%d", getegid ());
|
||||
|
||||
argv = get_cmd_line_args ();
|
||||
argv = get_cmd_line_args (NULL);
|
||||
if (argv == NULL)
|
||||
{
|
||||
fprintf (stderr, "cannot read argv: %m\n");
|
||||
@ -900,7 +1061,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
argv = get_cmd_line_args ();
|
||||
argv = get_cmd_line_args (NULL);
|
||||
if (argv == NULL)
|
||||
{
|
||||
fprintf (stderr, "cannot read argv: %m\n");
|
||||
|
33
test/system/950-auth-scripts.bats
Normal file
33
test/system/950-auth-scripts.bats
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bats
|
||||
#
|
||||
# Tests for podman auth scripts
|
||||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
function setup() {
|
||||
basic_setup
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
basic_teardown
|
||||
}
|
||||
|
||||
@test "podman auth script" {
|
||||
auth_dir=$PODMAN_TMPDIR/auth
|
||||
mkdir -p $auth_dir
|
||||
auth_script=$auth_dir/pull_check.sh
|
||||
|
||||
cat > $auth_script <<EOF
|
||||
#!/bin/sh
|
||||
if echo \$@ | grep "pull foobar"; then
|
||||
exit 42
|
||||
fi
|
||||
exit 43
|
||||
EOF
|
||||
chmod +x $auth_script
|
||||
|
||||
PODMAN_AUTH_SCRIPTS_DIR=$auth_dir run_podman 42 pull foobar
|
||||
PODMAN_AUTH_SCRIPTS_DIR=$auth_dir run_podman 43 pull barfoo
|
||||
}
|
Reference in New Issue
Block a user