diff options
author | Lennart Poettering <lennart@poettering.net> | 2025-01-14 11:50:52 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2025-01-15 17:03:21 +0100 |
commit | ef5f72437e661b603e4b05ad89446883f80a29c3 (patch) | |
tree | 8ece39d8807e3a2133f29c5bc3ece0681d55301b | |
parent | run: fire sd_notify("READY=1") when in service mode and the unit is properly ... (diff) | |
download | systemd-ef5f72437e661b603e4b05ad89446883f80a29c3.tar.xz systemd-ef5f72437e661b603e4b05ad89446883f80a29c3.zip |
run0: allow explicit control of service manager activation for run0 sessions
This adds a new --lightweight=yes/no switch which allows controlling
whether the invoked service will have the service manager around or not.
Moreover, this changes that if the target user is root it will now
support to the lightweight mode, i.e. run0 towards root will no longer
pull in the service manager (a real tty login via getty still will
though!).
My thinking here is that quickly raising privileges via run0 probably
shouldn't be considered a proper login but just something short lived,
temporary for a single command or similar.
-rw-r--r-- | man/run0.xml | 16 | ||||
-rw-r--r-- | src/run/run.c | 35 | ||||
-rw-r--r-- | src/shared/parse-argument.c | 20 | ||||
-rw-r--r-- | src/shared/parse-argument.h | 1 | ||||
-rwxr-xr-x | test/units/TEST-35-LOGIN.sh | 21 |
5 files changed, 93 insertions, 0 deletions
diff --git a/man/run0.xml b/man/run0.xml index 16774813d4..59aa6d05d9 100644 --- a/man/run0.xml +++ b/man/run0.xml @@ -225,6 +225,21 @@ </varlistentry> <varlistentry> + <term><option>--lightweight=<replaceable>BOOLEAN</replaceable></option></term> + + <listitem><para>Controls whether to activate the per-user service manager for the target user. By + default if the target user is <literal>root</literal> or a system user the per-user service manager + is not activated as effect of the <command>run0</command> invocation, otherwise it is.</para> + + <para>This ultimately controls the <varname>$XDG_SESSION_CLASS</varname> variable + <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry> + respects.</para> + + <xi:include href="version-info.xml" xpointer="v258"/> + </listitem> + </varlistentry> + + <varlistentry> <term><option>--machine=</option></term> <listitem> @@ -321,6 +336,7 @@ <member><citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry></member> <member><citerefentry project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member> + <member><citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> </simplelist></para> </refsect1> diff --git a/src/run/run.c b/src/run/run.c index 1f2507c10e..7538029548 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -88,6 +88,7 @@ static bool arg_ignore_failure = false; static char *arg_background = NULL; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static char *arg_shell_prompt_prefix = NULL; +static int arg_lightweight = -1; STATIC_DESTRUCTOR_REGISTER(arg_description, freep); STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep); @@ -199,6 +200,8 @@ static int help_sudo_mode(void) { " --pty Request allocation of a pseudo TTY for stdio\n" " --pipe Request direct pipe for stdio\n" " --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n" + " --lightweight=BOOLEAN Control whether to register a session with service manager\n" + " or without\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -778,6 +781,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { ARG_PTY, ARG_PIPE, ARG_SHELL_PROMPT_PREFIX, + ARG_LIGHTWEIGHT, }; /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions @@ -802,6 +806,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { { "pty", no_argument, NULL, ARG_PTY }, { "pipe", no_argument, NULL, ARG_PIPE }, { "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX }, + { "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT }, {}, }; @@ -914,6 +919,12 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { return r; break; + case ARG_LIGHTWEIGHT: + r = parse_tristate_argument("--lightweight=", optarg, &arg_lightweight); + if (r < 0) + return r; + break; + case '?': return -EINVAL; @@ -947,6 +958,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO)) arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT; + log_debug("Using %s stdio mode.", arg_stdio == ARG_STDIO_PTY ? "pty" : "direct"); + arg_expand_environment = false; arg_send_sighup = true; @@ -1045,6 +1058,28 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m"); } + /* When using run0 to acquire privileges temporarily, let's not pull in session manager by + * default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege + * escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session + * manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do + * this for root though, under the assumption that if a regular user temporarily transitions into + * another regular user it's a better default that the full user environment is uniformly + * available. */ + if (arg_lightweight < 0 && !strv_env_get(arg_environment, "XDG_SESSION_CLASS") && privileged_execution()) + arg_lightweight = true; + + if (arg_lightweight >= 0) { + const char *class = + arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") : + (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background"); + + log_debug("Setting XDG_SESSION_CLASS to '%s'.", class); + + r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class); + if (r < 0) + return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m"); + } + return 1; } diff --git a/src/shared/parse-argument.c b/src/shared/parse-argument.c index e6159c1482..177028e424 100644 --- a/src/shared/parse-argument.c +++ b/src/shared/parse-argument.c @@ -31,6 +31,26 @@ int parse_boolean_argument(const char *optname, const char *s, bool *ret) { } } +int parse_tristate_argument(const char *optname, const char *s, int *ret) { + int r; + + if (s) { + r = parse_boolean(s); + if (r < 0) + return log_error_errno(r, "Failed to parse boolean argument to %s: %s.", optname, s); + + if (ret) + *ret = r; + + return r; + } else { + if (ret) + *ret = -1; + + return 0; + } +} + int parse_json_argument(const char *s, sd_json_format_flags_t *ret) { assert(s); assert(ret); diff --git a/src/shared/parse-argument.h b/src/shared/parse-argument.h index 6a1a67aef7..bd577033b8 100644 --- a/src/shared/parse-argument.h +++ b/src/shared/parse-argument.h @@ -6,6 +6,7 @@ #include "sd-json.h" int parse_boolean_argument(const char *optname, const char *s, bool *ret); +int parse_tristate_argument(const char *optname, const char *s, int *ret); int parse_json_argument(const char *s, sd_json_format_flags_t *ret); int parse_path_argument(const char *path, bool suppress_root, char **arg); int parse_signal_argument(const char *s, int *ret); diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh index 9a5c781001..5a83b5847a 100755 --- a/test/units/TEST-35-LOGIN.sh +++ b/test/units/TEST-35-LOGIN.sh @@ -713,6 +713,10 @@ testcase_background() { TRANSIENTUNIT2="bgg$RANDOM.service" TRANSIENTUNIT3="bggg$RANDOM.service" TRANSIENTUNIT4="bgggg$RANDOM.service" + RUN0UNIT0="run0$RANDOM.service" + RUN0UNIT1="runn0$RANDOM.service" + RUN0UNIT2="runnn0$RANDOM.service" + RUN0UNIT3="runnnn0$RANDOM.service" trap background_at_return RETURN @@ -758,6 +762,23 @@ EOF systemd-run -u "$TRANSIENTUNIT4" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_TYPE=tty" -p Type=exec -p User=lightuser sleep infinity loginctl | grep lightuser | grep -q user-light systemctl stop "$TRANSIENTUNIT4" + + # Now check that run0's session class control works + systemd-run --service-type=notify run0 -u lightuser --unit="$RUN0UNIT0" sleep infinity + loginctl | grep lightuser | grep -q "background-light " + systemctl stop "$RUN0UNIT0" + + systemd-run --service-type=notify run0 -u lightuser --unit="$RUN0UNIT1" --lightweight=yes sleep infinity + loginctl | grep lightuser | grep -q "background-light " + systemctl stop "$RUN0UNIT1" + + systemd-run --service-type=notify run0 -u lightuser --unit="$RUN0UNIT2" --lightweight=no sleep infinity + loginctl | grep lightuser | grep -q "background " + systemctl stop "$RUN0UNIT2" + + systemd-run --service-type=notify run0 -u root --unit="$RUN0UNIT3" sleep infinity + loginctl | grep root | grep -q "background-light " + systemctl stop "$RUN0UNIT3" } testcase_varlink() { |