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



## Tim Rau (Oct 26, 2022)

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:

```
#!/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?

Using chmod() to set the `setuid` bit did not seem to work. I'm guessing that FreeBSD does not allow this for interpreted scripts.
Using `su` within the service directly works to always run the service as `some_user`, but won't work for `root` 
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.


----------



## Alain De Vos (Oct 26, 2022)

I'm not an expert but is it not

```
su - someuser -c ...
```


----------



## SirDice (Oct 26, 2022)

Tim Rau said:


> ```
> start_cmd="startit"
> stop_cmd="stopit"
> ```





Tim Rau said:


> 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.



Tim Rau said:


> 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.


----------



## Tim Rau (Oct 26, 2022)

SirDice said:


> 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.


I don't know what you mean. Setting `start_cmd` is the technique from https://docs.freebsd.org/en/articles/rc-scripting/ and documented as `argument_cmd` in rc.subr(8)(). Can you provide an example of what you mean by "builtin start and stop functions"?


----------



## SirDice (Oct 26, 2022)

Tim Rau said:


> I don't know what you mean.


Remove `start_cmd="startit"` and `stop_cmd="stopit"`. Then also remove the entire `startit` and `stopit` functions to see what I mean.


----------



## Tim Rau (Oct 26, 2022)

SirDice said:


> 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`.


----------



## SirDice (Oct 26, 2022)

Tim Rau said:


> 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.


----------



## chrbr (Oct 26, 2022)

Dear Tim Rau,
please have a look at https://forums.freebsd.org/threads/how-to-integrate-shell-scripts-into-the-rc-framework.86689/. I have stumbled over the same problems as you. cy@ gave me some very helpful hints which resulted in a better version. The history of the scripts are listed in the link. I hope it is of some help. Additionally it is very helpful to check other files in /usr/local/etc/rc.d/. I have leared from tinyproxy which has a very minimal file in /iusr/local/etc/rc.d/.


----------



## Tim Rau (Oct 26, 2022)

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.


----------



## Alain De Vos (Oct 26, 2022)

I have a munin_script with command:

```
command="/usr/home/Services/Runner/start_munin_cron"
```
Which is

```
su - munin -c /usr/local/bin/munin-cron
```
So this script runs as "munin"

Feel free to have a look at,








						Practical rc scripting very short tutorial
					

First make make daemon which will print "Hello World" each few seconds on the screen. A file /usr/local/etc/myservice2 , chmod 755 with following content:  #!/usr/local/bin/mksh echo "Starting" while true do     echo "Hello World"     sleep 6 done  Then we create the rc-script to stop and start...




					forums.freebsd.org


----------



## Tim Rau (Oct 27, 2022)

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`.


----------



## patmaddox (Oct 27, 2022)

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.


----------

