Solved How to run rc.d service as specific user?

I am attempting to create two /usr/local/etc/rc.d services, using rc.subr(8)() after reviewing https://docs.freebsd.org/en/articles/rc-scripting/. Service B's process must always run as root, while service A's process must always run as another less privileged user ( some_user). The other user must be allowed to start and stop both services. The other user has been created and added to the wheel group.

Problem 1:
After reboot, ps -A -j shows actual_process_a running as root isntead of some_user. If I change my service to use su some_user -c "actual_process_a start", then it shows running as some_user, but according to rc.subr(8)(), setting the ${name}_user should do that automatically.
${name}_user
User to run command as, using chroot(8) if
${name}_chroot is set, otherwise uses su(1). Only
supported after /usr is mounted.

What am I doing wrong? The following has been minimally modified from the actual code:
Code:
#!/bin/sh
# PROVIDE: hello_world
# REQUIRE: LOGIN

. /etc/rc.subr

name="hello_world"
procname="hello_world"
desc="Hello World"
rcvar="hello_world_enable"
start_cmd="startit"
stop_cmd="stopit"

load_rc_config "${name}"
: ${hello_world_enable:="YES"}
: ${hello_world_user="some_user"}

# (Sourcing environment variables here...)

startit() 
{
    # (Extra preparation here)
    # The following would work, but should not be necessary according to the man page:
    # su some_user -c "actual_process_a start"
    actual_process_a start
}

stopit()
{
    actual_process_a stop
    # (Cleanup here)
}

run_rc_command "$1"

Problem 2:
(I think this is related, but can create a separate thread if needed.) When I start the service manually using service hello_world_a start, the process always runs as the user I ran that command as. I'm hoping that will be solved with ${name}_user (similar to how a Windows service has Log On configuration), but if not, is there a recommended way to handle this?
  1. Using chmod() to set the setuid bit did not seem to work. I'm guessing that FreeBSD does not allow this for interpreted scripts.
  2. Using su within the service directly works to always run the service as some_user, but won't work for root
  3. As a last resort, I could use sudo() and add some_user to sudoers (), but I think this is giving too many privileges to the restricted user.
 
Code:
start_cmd="startit" 
stop_cmd="stopit"
What am I doing wrong?
You are redefining the start and stop functions. Thus you're not using the builtin start and stop functions. So you will have to fix this in your own code.

Using chmod() to set the setuid bit did not seem to work. I'm guessing that FreeBSD does not allow this for interpreted scripts.
Scripts are never executed SETUID even if the bit has been set. It's a rather big security hole.
 
Remove start_cmd="startit" and stop_cmd="stopit". Then also remove the entire startit and stopit functions to see what I mean.
Removing those functions will end up with a do-nothing service; there is no way to start and stop my actual process without them. The actual logic to start and stop my process is in those functions. The snippet provided was simplified, but I have to call the executable with different (more complex) arguments for start and stop behavior, with a few additional prep and cleanup commands.

I cannot use command due to this, so I must use separate start_cmd and stop_cmd.
 
Removing those functions will end up with a do-nothing service;
No, it won't. It will simply execute whatever was set in the command variable.

Look at this example: https://docs.freebsd.org/en/articles/rc-scripting/#rcng-daemon
See any start and stop functions? Or any definition of start_cmd or stop_cmd? It doesn't need any because it uses the builtin start and stop functions. Those standard, builtin, functions would automatically use ${name}_user if it was set.

Because you are defining the start/stop functions yourself you will also need to take care of any ${name}_user in your own start/stop functions.
 
Thanks SirDice and chrbr; it seems that run_rc_command does NOT run the _cmd commands as the specified user. Presumably, this also affects _precmd and _postcmd. Only if command is specified instead, run_rc_command runs them as the specified user. This was not originally clear from rc.subr(8)(). To test this, I moved my startit logic to /usr/local/etc/rc.d/test/hello_world_start.sh, removing the line start_cmd="startit", and adding command="/usr/local/etc/rc.d/test/hello_world_start.sh". The actual process then ran under the specified user.

Unfortunately, making this change encounters https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235122 in FreeBSD 12.3, preventing service hello_world_a start from being run as a non-root user. It looks like I'll just have to use su and sudo directly for now.
 
I have a munin_script with command:
Code:
command="/usr/home/Services/Runner/start_munin_cron"
Which is
Code:
su - munin -c /usr/local/bin/munin-cron
So this script runs as "munin"

Feel free to have a look at,
 
Alain De Vos thanks for responding, but that does not appear relevant to my specific question, which was regarding the _user feature in an rc.d() script. It turns out that feature does not work with the start_cmd feature.

Using su() within the rc.d() script will allow my start command to run on boot as the specified user, but it looks like I need to use sudo() within the rc.d() script to allow some_user (without knowing the root password) to start a service that needs to run as root.
 
I don't think you want to use su in your RC script. Instead, there's a ${name}_user variable (see rc.subr(8)).

The other user must be allowed to start and stop both services. The other user has been created and added to the wheel group.

For this, I think the simple thing is sudo / doas. You can make sudo locked down to a specific command, so you can ensure they only can run service service_a restart etc.
 
Back
Top