pim6sd icon indicating copy to clipboard operation
pim6sd copied to clipboard

Change so that the program can start without root privileges

Open marek22k opened this issue 3 months ago • 8 comments

Background: A process, especially a long-running process that has contact with the outside world, should not have to start with privileged access. With the help of the capabilities system, it is possible to assign individual required permissions to the process and thus also dispense with root privileges (example CapabilityBoundingSet/AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW in systemd).

Closes https://github.com/troglobit/pim6sd/pull/40

marek22k avatar Sep 11 '25 19:09 marek22k

I also like the idea of running pim6sd with reduced privileges, especially as the code has gone through many hands, over many years.

The risk/downside of removing the check would be increased maintenance / support overhead as users will be confronted with less helpful, low-level error messages.

Could cap_get_proc() / cap_get_ambient() (man capgetp / libcap) maybe be used to exit with a descriptive error message if neither root nor the specific capabilities are available? (depending on if @troglobit would like that approach)

T-X avatar Sep 17 '25 23:09 T-X

[..] The risk/downside of removing the check would be increased maintenance / support overhead as users will be confronted with less helpful, low-level error messages.

Yes, that is a concern to me since it usually then ends up as a bug report. Also, I want all the all the other multicast routing daemons to behave the same in this manner, so any solution we arrive at here is what I'll have to port over to the others.

Could cap_get_proc() / cap_get_ambient() (man capgetp / libcap) maybe be used to exit with a descriptive error message if neither root nor the specific capabilities are available? (depending on if @troglobit would like that approach)

Yeah, this is something I was considering too actually. We already use libcap in SMCRoute, so that would be a nice and portable solution. There we use it to drop root capabilities though, so not exactly the same as here:

https://github.com/troglobit/smcroute/blob/93bd6c4098f69cc6609883ef301581381d8ef336/src/smcrouted.c#L226-L229

troglobit avatar Sep 21 '25 08:09 troglobit

What is the current solution to the problem? I still think that not allowing startup as root makes sense.

marek22k avatar Nov 11 '25 21:11 marek22k

What is the current solution to the problem? I still think that not allowing startup as root makes sense.

I believe I've outlined my concerns in my previous reply and I've seen no interaction regarding that since.

But yeah, not exiting when euid != 0 makes sense. I just don't like the log message and still believe we should look at capabilities and signal useful log messages back to users. I also want to continue supporting *BSD. So any change should take that into concern as well.

troglobit avatar Nov 13 '25 19:11 troglobit

So the current solution is to not have one and to have to compile the program yourself?

Unfortunately, I am not familiar with libcap or operating system queries (whether Linux or BSD is running). (Wouldn't you also have to do that partly in the build system, since libcap does not exist on BSD? I have never worked with autoconf myself, only with meson and cmake).

Do you have any ideas for a better log message?

marek22k avatar Nov 13 '25 20:11 marek22k

So the current solution is to not have one and to have to compile the program yourself?

Not sure how to respond to this, I thought this was the thread where we discuss how to change the current state?

Unfortunately, I am not familiar with libcap or operating system queries (whether Linux or BSD is running). (Wouldn't you also have to do that partly in the build system, since libcap does not exist on BSD? I have never worked with autoconf myself, only with meson and cmake).

That's why I mentioned SMCRoute previously, it uses libcap, so you could have a look at how it works and is integrated.

Do you have any ideas for a better log message?

I think it would be useful for a non-root user on Linux to know which capabilities they've not given the program. E.g., "Running as non-root requires CAP_NET_RAW, exiting.", or similar.

troglobit avatar Nov 14 '25 05:11 troglobit

So the current solution is to not have one and to have to compile the program yourself?

Not sure how to respond to this, I thought this was the thread where we discuss how to change the current state?

I may have expressed myself somewhat poorly.

Unfortunately, I am not familiar with libcap or operating system queries (whether Linux or BSD is running). (Wouldn't you also have to do that partly in the build system, since libcap does not exist on BSD? I have never worked with autoconf myself, only with meson and cmake).

That's why I mentioned SMCRoute previously, it uses libcap, so you could have a look at how it works and is integrated.

Ahh, I see. I just noticed that the function in cap.c goes even further (I have to admit, I didn't look at it closely enough).

Do you have any ideas for a better log message?

I think it would be useful for a non-root user on Linux to know which capabilities they've not given the program. E.g., "Running as non-root requires CAP_NET_RAW, exiting.", or similar.

But CAP_NET_ADMIN is also required to install routes, correct? And CAP_NET_ADMIN also implies CAP_NET_RAW, right? (I'm not sure about the last one.)

So, logically, the plan would be as follows:

if (! running_as_root) {
    if via macro (has_libpcap) {
        if (! has_cap(CAP_NET_ADMIN)) {
            error "Running as non-root requires CAP_NET_ADMIN, exiting."
        }
    } else {
        error "Need root privileges to start."
    }
}

marek22k avatar Nov 14 '25 05:11 marek22k

Do you have any ideas for a better log message? I think it would be useful for a non-root user on Linux to know which capabilities they've not given the program. E.g., "Running as non-root requires CAP_NET_RAW, exiting.", or similar. But CAP_NET_ADMIN is also required to install routes, correct? And CAP_NET_ADMIN also implies CAP_NET_RAW, right? (I'm not sure about the last one.)

It was just an example, but yes I think it needs CAP_NET_ADMIN as well.

So, logically, the plan would be as follows:

if (! running_as_root) {
    if via macro (has_libpcap) {
        if (! has_cap(CAP_NET_ADMIN)) {
            error "Running as non-root requires CAP_NET_ADMIN, exiting."
        }
    } else {
        error "Need root privileges to start."
    }
}

Yup pretty much, the "if via macro" could even be an #ifdef tucked away in a header file so it doesn't clutter up the C code.

troglobit avatar Nov 14 '25 08:11 troglobit