cortesi / modd

A flexible developer tool that runs processes and responds to filesystem changes
MIT License
2.8k stars 128 forks source link

Support for running daemons under `sudo` #61

Open ti-mo opened 5 years ago

ti-mo commented 5 years ago

Hi @cortesi, thanks for the cool project! This is a feature/documentation request for running privileged daemons using modd.

I'm having some issues notifying the daemon I'm working on, because it's run as root using sudo. Posting a complete description here with the hope that it helps some users along. Workaround is at the end of the post.

Consider the following example:

**/*.go {
  prep: make
  daemon +sigterm: sudo godoc -http=:80
}

Running the daemon without sudo (and on an unprivileged port) yields the following output:

10:39:35: daemon: godoc -http=:6060
>> sending signal terminated
exited: signal: terminated
>> starting...

Wrapping it with sudo:

10:40:20: daemon: sudo godoc -http=:80
>> sending signal terminated
^C>> stopping

The daemon isn't stopped when stopping modd either.

This is normal behaviour, because the sudo binary has the setuid bit set. Sending signals to privileged processes (eg. pkill sudo) as a normal user is not allowed:

➜  ~  pkill sudo
pkill: killing pid 16666 failed: Operation not permitted

However, unlike pkill, modd doesn't notify the user that sending the signal failed. I presume this is because it signals the pgroup as a whole, not processes individually. This caused me some frustration before I started digging into why this happened.

This workaround worked:

**/*.go {
  prep: make
  prep: sudo pkill godoc || true
  daemon +sigterm: sudo godoc -http=:80
}

Would it be possible to support privileged notification through extending the syntax? eg.:

**/*.go {
  daemon: sudo godoc -http=:80
  signal: sudo kill -TERM @daemon-pid
}

The PIDs to notify could be presented as an internal variable. A signal specification would have to be written right after a daemon statement, so multiple daemons with multiple different custom signal handlers could be supported.

WDYT?

cortesi commented 5 years ago

Hi Timo. Thanks for the careful exposition of this problem. I must say, my knee-jerk reaction is "don't do that" - instead you could use capabilities on Linux to allow binding to a privileged port, or have a persistent privileged program that binds to the port and proxies traffic to a non-privileged program that is managed by modd, or even use iptables/pf to forward a port to a local unprivileged port. I'll assume that you've thought of all of these possibilities, and for whatever reason, they don't work for you.

As your proposal hints, I feel modd should remain agnostic about privilege and instead to let the user specify a custom restart command. I'd accept a patch that implements this, as long as we can get the syntax right and behaviour right. I'd call the restart command restart, since it might presumably do something entirely unrelated to signals to restart a daemon. We could then say that if a restart command is defined in a block, it is used to restart all daemons, unless they have an explicit +signal modifier. It's an error to define more than one restart command per block, but I think that's a manageable limitation.

ti-mo commented 5 years ago

@cortesi Thanks for the reply! I'm working on an eBPF program, and my initial attempts at getting my tool to run with a minimal capability set were unsuccessful, so I just switched to running with sudo to get up and running. Your response made me look into it again, and I've finally figured out the problem. (https://stackoverflow.com/questions/40837181/how-to-raise-ulimit-hard-limit-for-real-time-priority-programmatically-with-setu) My LUKS-encrypted homedir mount has nosuid set, turns out that flag prohibits processes assuming capabilities set using file system attributes as well.

Still, I think a custom restart handler would be nice to have, your proposal sounds on point! restart sounds good, including the limitations you mentioned. I guess the user can always define multiple blocks with a similar watch pattern if they need multiple daemons with custom restart handlers.