Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running mox unprivileged with CAP_NET_BIND_SERVICE #194

Open
tzvetkoff opened this issue Jul 26, 2024 · 4 comments
Open

Running mox unprivileged with CAP_NET_BIND_SERVICE #194

tzvetkoff opened this issue Jul 26, 2024 · 4 comments

Comments

@tzvetkoff
Copy link

tzvetkoff commented Jul 26, 2024

Basically, by adding

AmbientCapabilities=CAP_NET_BIND_SERVICE

to mox.service one can bind privileged ports on Linux (and FreeBSD with capsh?)

My understanding is that the only thing that really requires running as the privileged user is binding ports <1024.
File access is not (and should not be) of any concern.

This is more of a question than an actual issue, since I'd like to hear your input on a proper way to move this forward.
Here's my attempt to get rid of the angel process - https://github.com/mjl-/mox/compare/main...tzvetkoff:mox:unpriv?expand=1.
It's working totally fine on my server.

@mjl-
Copy link
Owner

mjl- commented Aug 2, 2024

Hi @tzvetkoff, that diff indeed seems like a good approach. You're right that starting as root is currently only for being to bind to the privileged ports.
Perhaps in the future mox may want to do more sandboxing. When that happens, it will probably be done with OS-specific mechanisms/APIs. We'll have to see when/if we get there.

A few remarks:

  • The code has a few checks for Getuid() == 0, to guard printing that we're listening on sockets. One example is at https://github.com/mjl-/mox/blob/v0.0.11/imapserver/server.go#L347. I think you're losing those prints now. Would be good to still print them, but still not in localserve mode.
  • The name of flag "-skip-forkexec" sounds a bit low-level. A name that explains its purpose at a higher level may be better, e.g "-no-root" or "-unprivileged" (just thinking out loud). With a bit more explanation in the flag or the help text of the command.

@tzvetkoff
Copy link
Author

Well, this issue was more like a question initially, and most of it can be disregarded when I convert it to a PR (in the next few days, I hope.)

Thing is, I just did a very quick hack just to show we don't need UID-0 to go forward.

Moreover, one can easily syscall.Setuid(uid) on UNIX, so the whole lot of shenanigans currently done to circumvent unprivileged execution are just needless.

I totally agree with you that my diff is substandard, it was just to prove the point that mox can (and, IMHO, should) use OS-based isolation, and just that if needs be - do proper syscall.Setuid after opening the listening sockets.

I will, in a matter of a few days, convert this into a fully-featured pull request, and I'll close this as the conversation will continue there.

Once again, thanks for the great project, and I hope I meet you next year at FOSDEM (I kinda missed it this year.)

@mjl-
Copy link
Owner

mjl- commented Aug 3, 2024

Thing is, I just did a very quick hack just to show we don't need UID-0 to go forward.

Mox also needs to stay working on BSDs. They don't all have the same mechanisms for binding to unprivileged ports. Some have sysctl's to disable/change what unprivileged ports are, but they are global (allow any application to bind to such a port). I actually don't like the per-binary capability flags much: In my experience, it's easy to forget setting it when replacing/upgrading binaries (i.e. a source of trouble). If anything, I would expect systems to provide a per-uid mechanism to allow binding to some ports (or just disable the concept entirely).

Moreover, one can easily syscall.Setuid(uid) on UNIX, so the whole lot of shenanigans currently done to circumvent unprivileged execution are just needless.

Well, with Go there are complications: syscall is a low-level package, and its Setuid only changes the uid of the "proc" (thread/process) that's making the call. The Go runtime can schedule a system call on any of the available procs. I.e. you don't know which proc Setuid will run on, and you don't know which proc your other calls are going to run on. AFAIK, you would have to call syscall.Setuid before anything gets a chance to create additional procs in the runtime. But Go package init code can run early, and create procs. For details, see golang/go#1435.

I didn't mean to imply your diff was substandard, was just dumping my thoughts. It's great to see code along in issues.
I sure plan to be at FOSDEM again, we'll meet there!

@lmeunier
Copy link
Contributor

I'm also interested by the feature. If you need help to test a PR or some code review, do no hesitate to ask.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants