Open NyaomiDEV opened 3 years ago
Better solution
While I agree, user services is something I am looking for, I have some notes:
What I've done on my machine is to create a runtime for my user, and add all the services i want for my user into it (+default), and manually setting command_user
to my user.group for the services, and hardcoding some paths. I don't know of a current way to make this cross-user compatible.
Then the only command I need to run is doas openrc mazunki
, and it will run the services for me (including PipeWire).
Suggestion
The way I can see this working is if users had their own runtime (not completely sure about the path or naming conventions here, although username == runtime
wouldn't be the worst idea, although this could create some naming conflicts if the username is something like boot
or default
), and be able to add/remove scripts to it without escalating permissions.
Then, in scripts, we'd need to have access to the $USER
and maybe $USERGROUP
and maybe some of their XDG variables. With that, it would be trivial to run ${XDG_CONFIG_HOME}/environment
to get other variables.
What I've done on my machine is to create a runtime for my user, and add all the services i want for my user into it (+default), and manually setting
command_user
to my user.group for the services, and hardcoding some paths. I don't know of a current way to make this cross-user compatible.Then the only command I need to run is
doas openrc mazunki
, and it will run the services for me (including PipeWire).
This still poses a security risk as you could omit command_user
and have stuff run as root; that should be defaulted for and enforced by OpenRC itself; also, unless you love to put passwords in twice, I definitely see how that doas
could be inconvenient (unless you plug in a special rule for it, but again, security vulnerability)
The way I can see this working is if users had their own runtime (not completely sure about the path or naming conventions here, although
username == runtime
wouldn't be the worst idea, although this could create some naming conflicts if the username is something likeboot
ordefault
), and be able to add/remove scripts to it without escalating permissions.
The idea of runlevels for users is not a bad one, though the best thing would be to prefix it in some way: bob
=> runtime user.bob
Then, in scripts, we'd need to have access to the
$USER
and maybe$USERGROUP
and maybe some of their XDG variables. With that, it would be trivial to run${XDG_CONFIG_HOME}/environment
to get other variables.
Correction, the variables to be exposed would be at least $USER
, $UID
, $GROUP
(not usergroup, that does not exist), $GID
and $HOME
. I am saying at least; you'd want to have some more variables or your entire environment available, though even that is a security risk; I'd propose a flag like inherit_user_env
to inherit the environment in some way. Also I do not have ${XDG_CONFIG_HOME}/environment
in my system and therefore I am not sure if you are suggesting something new to be added for users.
This still poses a security risk as you could omit command_user and have stuff run as root; that should be defaulted for and enforced by OpenRC itself; also, unless you love to put passwords in twice, I definitely see how that doas could be inconvenient (unless you plug in a special rule for it, but again, security vulnerability)
I haven't looked into the security concerns regarding OpenRC. I am a single-user on my desktop, so it hasn't been a concern for me.
It would be better to run openrc user.mazunki
, without root, and let it run up the missing services for user.mazunki
, automatically setting command_user
as needed.
I think this would be a better solution, instead of needing to hard-code command_user
on all user services.
The idea of runlevels for users is not a bad one, though the best thing would be to prefix it in some way: bob => runtime user.bob
I like it, I changed it on my system.
Correction, the variables to be exposed would be at least $USER, $UID, $GROUP (not usergroup, that does not exist), $GID and $HOME. I am saying at least; you'd want to have some more variables or your entire environment available, though even that is a security risk; I'd propose a flag like inherit_user_env to inherit the environment in some way. Also I do not have ${XDG_CONFIG_HOME}/environment in my system and therefore I am not sure if you are suggesting something new to be added for users.
I just named it $USERGROUP
as an example, but I think maybe $RC_USER
and $RC_GROUP
would be better options. I'm not sure we need the UID, GID, and HOME path, but perhaps.
Maybe setting a list of environment variables to be inherited would be okay. Having XDG_* by default would sure be nice.
Being able to inherit_user_env
would be an appendix to those, I think.
You don't have ${XDG_CONFIG_HOME:-$HOME/.config}/environment
on your system, because it's not a default thing to be there. I think the analogous "default" one would be ~/.profile
, but personally I hate having dotfiles in $HOME, so I've cleaned up my system a bit. Similarly, I also have ${XDG_CONFIG_HOME}/{aliases,dircolors,x11-environment,wl-environment}
. It makes sense to have user environment in the config path, since we also have /etc/environment
for system-wide settings.
I have posted a "working" example for how I update my CloudFlare DNS records on my scripts repository as a user. It seems to work well, except for the fact that I still need to use root to change runtimes.
I have also tried to boot up my window manager through it, but I am struggling with running dbus manually, without dbus-run-session
. PipeWire does work fine, though (on one session, but not for my secondary tty running an Xorg session).
I can't find a clean and proper way to pass around variables from a single node, down to the rest of the environment, though. It would be nice if it were possible to export the entire environment at once.
@mazunki I just had some time to check your scripts folder, and unless I am mistaken you are not really solving the main issue: OpenRC is still running those services as system services and you still need root to manage them.
Sadly I didn't come up with a better or more dynamic idea as of yet (also I think there'd be another problem -- how can we tell the exact moment when users log in and out to start and stop their services?) and I also resorted to system services with command_user
.
Hope this issue gets some traction.
@mazunki I just had some time to check your scripts folder, and unless I am mistaken you are not really solving the main issue: OpenRC is still running those services as system services and you still need root to manage them.
Sadly I didn't come up with a better or more dynamic idea as of yet (also I think there'd be another problem -- how can we tell the exact moment when users log in and out to start and stop their services?) and I also resorted to system services with
command_user
.Hope this issue gets some traction.
You are entirely correct.
My "solution" involves initial escalation, although this is only a concern on multiuser scenarios. For my home usage I don't really mind, since I'm not giving pipewire, for instance, any escalation, meaning there's no threat of the apps posing a security risk. The only threat is me abusing OpenRC to take over my own computer.
It's a compromise since there's no better solution yet.
Also, not only that, I also need to hardcode a configuration file + init script for each user, with the appropriate RC_USER
variable. That's fine if only one user needs the user service, but under most scenarios I would like to be able to add the user service to multipe running user instances at once. Currently, my solution would require copies of the same user-pipewire init script to solve this, instead of simply having config differences.
how can we tell the exact moment when users log in and out to start and stop their services?
Replying to myself: OpenRC's PAM integration. a PAM module can be indeed used to know when an user logs in and out of the system.
By the way, for now Dinit in userspace works just fine on my Artix install, until user mode OpenRC gets some more attention.
how can we tell the exact moment when users log in and out to start and stop their services?
Replying to myself: OpenRC's PAM integration. a PAM module can be indeed used to know when an user logs in and out of the system.
By the way, for now Dinit in userspace works just fine on my Artix install, until user mode OpenRC gets some more attention.
Yeah, this looks really interesting. For others, see https://github.com/chimera-linux/dinit-userservd too.
For what it worths, i use this initscript:
#!/sbin/openrc-run
# Copyright 2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
name="userd daemon"
description="user specific init and shutdown spawner"
user_name=${user:-${RC_SVCNAME#userd.}}
user_homedir="$(getent passwd ${user_name} | cut -d: -f6)"
user_userd="${user_homedir}/.userd/"
shutdown_timeout=${shutdown_timeout:-"5m"}
pidfile="/var/run/${RC_SVCNAME}.pid"
SSD_IONICELEVEL="${ionice}"
SSD_NICELEVEL="${nice}"
start() {
start-stop-daemon --exec "${user_userd}/start" --pidfile "${pidfile}" --user="${user_name}" --background --chdir="${user_userd}" --make-pidfile --stdout "${user_userd}/stdout.log" --stderr "${user_userd}/stderr.log"
}
stop() {
timeout "${shutdown_timeout}" su --login -c "cd ${user_userd} ; ${user_userd}/shutdown" - "${user_name}"
}
Then, administrator usually only have to cd /etc/init.d; ln -s userd userd.username, add it to wanted runlevel, and this uid will have ~/.userd/start and shutdown called appropriately (those can be used to daisy chain a specific init-style program, like svscan)
This is an issue for me, I have a service for syncthing, but it has to run as root (it literally gives you a warning for doing this, as it could be problematic) because openrc doesn't support a user runlevel.. pretty big issue imo.
I launch dinit through emptty. Emptty closes dinit automatically when a login session is closed.
~/.config/emptty
#!/bin/bash
Selection=true
xrdb -merge ~/.Xresources
. /etc/profile
. ~/.env
dinit &
"$@"
dinitctl shutdown
When a login session starts, sway executes this command to make dinit aware of GUI environment it is in.
dinitctl setenv DISPLAY XAUTHORITY WAYLAND_DISPLAY
~/.config/dinit.d can contain user services.
There's two distinct (but very similar) ideas being discussed here:
root
OpenRC manage use services too: This seems to work for single-user systems, but is quite involved in terms of setup, has too many risks of accidental escalation, and requires using (or tweaking) sudo for managing user services.sudo
.For the second approach, OpenRC would need to essential do something like:
if getuid() == 0:
INIT_DIR = /etc/init.d
else:
INIT_DIR = ~/.config/init.d/
And a few similar changes for the other paths. I did something rather similar to OpenBSD's rc about a decade ago and the result was a bit of a hack but worked.
Being able to manage user services like this would be very convenient to manage services like pulseaudio, pipewire, xdg-portals, offlineimap, etc.
Maybe, we should dinit instead of OpenRC for user services?
Adding our support here for use-cases like rootless containerd (and friends). You can currently get them into a service by using command_user
, but as mentioned, there's risk of escalation, managing the scripts gets annoying (you need to bug the admin), and so on. This would make everything a whole lot easier.
For the second approach, OpenRC would need to essential do something like:
if getuid() == 0: INIT_DIR = /etc/init.d else: INIT_DIR = ~/.config/init.d/
And a few similar changes for the other paths. I did something rather similar to OpenBSD's rc about a decade ago and the result was a bit of a hack but worked.
Being able to manage user services like this would be very convenient to manage services like pulseaudio, pipewire, xdg-portals, offlineimap, etc.
I am in favour of this solution if it can be implemented good.
Some extra thoughts
It would be nice if users could have and handle their own services and runlevels.
Nowadays a lot of applications rely on services run as the current user (for example sound servers like PipeWire and PulseAudio are required to run multiple applications that play audio in a tradiitonal desktop configuration).
For comparison, Systemd provides users with a mechanism to have their services (units) run and supervised upon login. This is, in turn, used by many desktop environments and also daemons (PulseAudio, PipeWire, etc.)
Some other init systems, like Runit, have no concept of user services, but they can be easily "tricked" to effectively handle them (Example).
OpenRC does not have any concept of user services and it doesn't seem to be easily adaptable to handle them in a tricky way; the best an user can do is effectively modify some system services so that they are run under their user and group and then they can write a bunch of
sudo rc-service <service> start
lines on their.profile
so that the services actually get started and supervised. This works but it is tricky and it requires the user to set aNOPASSWD
rule forrc-service
on their sudoers file, which makes for a security problem.Hence, it is probably wiser to let OpenRC have a concept of user services, in a way or another; the proposed solution would be:
user_service
(0 or 1, false or true) variable; and that variable, in turn, would conflict with thecommand_user
one;~/.config/init.d/
, where there can be user services only;/run/user/<uid>/openrc
that would more or less be of the same function and scope as the current/run/openrc
directory;rc-service
andrc-update
understand if they are being run as root or as user, to check whether there is a 'user' instance of OpenRC running, and to handle that one instead if they are indeed run as user.I know this may be sloppy as a proposed solution, but I hope to find more people willing to discuss this further.