-
Notifications
You must be signed in to change notification settings - Fork 616
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
Drop privileges after start #195
Comments
Good point. I have to think about that. |
If I look at golang/go#1435 then there might be a way as long as I don't need to chroot. Open socket, re-exec and pass FD to child process might work. This will become interesting for dynamic listeners :) |
First attempt of dropping privileges after starting fabio. This implementation should work only on OSX and Linux. The root process opens up the listeners and then spawns a child process as a different user passing on the listeners. This still needs work and testing. The command line parameter name will most likely change.
@killcity consider this a first attempt of tackling this. The code is based on the idea from @bgilmore in https://play.golang.org/p/dXBizm4xl3. The only thing I've added is the forwarding of stdout and stderr to make logging work. I find the code still quite ugly and the whole listener setup could use a makeover but it should work. Would be great if you could test it. |
I've tested this only on OSX so far with |
You are too quick :) I will give this a go in the morning (here in the US northeast). Thank so much for working this in. Super awesome! |
Some tests are failing. I'll fix that tomorrow. |
Wasn't sure if this was one of the failing tests? TBH, I haven't compiled much in Go. --> linux/amd64 error: exit status 2
Stderr: # _/root/gopath/fabio-drop-privs
./main.go:50: cfg.Username undefined (type *config.Config has no field or method Username)
./main.go:100: cfg.Username undefined (type *config.Config has no field or method Username)
./main.go:102: cfg.Username undefined (type *config.Config has no field or method Username)
./main.go:145: cfg.Username undefined (type *config.Config has no field or method Username)
./main.go:147: cfg.Username undefined (type *config.Config has no field or method Username)
... |
First attempt of dropping privileges after starting fabio. This implementation should work only on OSX and Linux. The root process opens up the listeners and then spawns a child process as a different user passing on the listeners. This still needs work and testing. The command line parameter name will most likely change.
Yes. I've rebased the code and fixed the failing test. One effect is that the cmd line parameter is now |
Thanks. Going to beat her up a bit! :)
|
Update: No issues. Been running with this since Dec 1. |
@killcity awesome. I'll spend some time on incorporating this into the master branch then. |
An alternative approach: Cheers |
This only works on Linux, correct? I would think it would still be nice to have the functionality regardless of operating system, although it does add some slight overhead.
|
It works on linux. Solaris/Illumos have privileges that are similar and can be applied to the user running fabio
and check with:
or you can apply a privilege set to the process you are going to run (can't remember the exact syntax for this usage):
FreeBSD have a sysctl to define the reserved port range :
You can add that to /etc/sysctl.conf so it's applied on boot. I don't know what the equivalent is for the other BSDs and OS X. I don't think there's anything similar on Windows though, but since you mention setuid I assume you're talking about some sort of unix system. |
Cool. I figured most modern unix variants have something that can accommodate.
Thoughts on whether this should or shouldn't be built into the app?
|
With my security hat on, I'd say that fabio should refuse to start as root. Maybe add an explicit option to allow it, so the user have to make a conscious decision to run as root.
|
What about windows and macOS? I'm not a fan of building per-os code in. OTOH, the proposed patch might also not work on other platforms. This could be optional though. You can choose which approach you want to take with forking as the default. This would also leave the door open if Go makes this easier at a later stage. I like the idea of the |
@magiconair macOS have no root and I don't think Windows even has a concept of root. Allowing a user to bind to a low port (I'm guessing all this discussion is related to port 80 and 443, please correct me if I'm wrong) is an administrator's activity and different OSs will have different mechanisms to achieve that, as I've shown in my messages. A system administrator that is deploying internet facing services should be familiar with those mechanisms. I don't think that logic belongs with the fabio code at all, so no os specific tricks for you to code and maintain. What could be added to fabio is a warning message in case it couldn't bind to a port (maybe it already exists, I haven't run into it). I agree with giving ample warning about the implementation of the insecure switch as you don't want to break deployments though I'd argue that most people running fabio as root have not weighed in the pros and cons and run it like that "cause it works". |
First attempt of dropping privileges after starting fabio. This implementation should work only on OSX and Linux. The root process opens up the listeners and then spawns a child process as a different user passing on the listeners. This still needs work and testing. The command line parameter name will most likely change.
Just keeping the patch in sync with master but I'm leaning towards @mterron. This sounds like the better approach. |
Well, in OSX you define a service with launchd, and launchd takes care of binding to the low port. On Linux you can use capabilities to allow the low port binding for a non privileged user (and you can even do that in the systemd service definition nowadays). On OpenBSD you just don't bind to a low port (instead use PF to port forward from a low port to a "high" port). So on *NIX at least i would not expect a web proxy to take care of security stuff that are quite clearly OS responsibility... |
I agree with @mterron and @stephane-martin that this is not the responsibility of
@mterron and @stephane-martin would you be willing to provide some documentation snippets for the various OSes you seem to have experience with? Not sure if what is already provided in this ticket is sufficient for the documentation. |
For instance on Linux with systemd.
|
On *BSD with Packet Filter, if fabio is directly facing internet, the best practice is only listen on a non-privileged port (4343), and use PF to forward trafic:
Running fabio in a jail (FreeBSD) or chroot (OpenBSD) doesn't harm too. You just need to copy the relevant C libraries to the jail (eg. libc.so, ld.so and libpthread.so). |
I think that between my original messages and @stephane-martin ones the basics are covered.
Is there other OS that's missing? |
Even if Fabio could do it I personally would never rely / trust any daemon to drop it's privileges after it's done with them. I remember from the "old" days (say 10 years ago) that a good number of root exploits in Linux used processes which relied on privilege dropping (Apache HTTP server for example). Because even though they might poses root rights for only a couple of milliseconds; At some point in time they will have them; It's an opening. If there is a vulnerability in Fabio the exploiter will only have to wait until the daemon is restarted at some point to obtain root rights. |
@siepkes so how do you manage Apache or Nginx services now? |
@kostyrev On SmartOS deployments I use SMF to grant the NGINX, Apache, etc. process the So the process never has root rights. It only has the rights to bind to the ports below 1024. |
You grant read perms on private certificates to apache/nginx user? |
@kostyrev Yes only the NGINX user has read rights on the private key. The certificate is world readable since it is obviously public anyway. Since the worker processes have the private key loaded in their memory when they are spawned there is not much point in shielding them from the actual private key file. |
I've compiled the best practices here in a wiki page: https://github.com/fabiolb/fabio/wiki/Binding-to-Low-Ports Is anybody up for reviewing this? If yes, then please comment here. I'm going to create another ticket for not allowing to run fabio as root. |
Thanks everybody for the suggestions! |
It seams that after upgrade to the MacOS Mojave it is no more required to be root for binding privileged ports. Reference. I'm running:
as non-root user, and it works.
|
Will Fabio eventually get support to use setuid (to change to a non-root user once it's up and running)? I'd rather not have to run another proxy in front of it to alleviate security concerns if at all possible. Is the current model putting that burden on something other than Fabio?
Cheers
The text was updated successfully, but these errors were encountered: