I am writing an HTTP server daemon in C (there are reasons why), managing it with systemd unit file.
I am rewriting an application designed 20 years ago, around 1995. And the system they use is that they chroot and then setuid, and the standard procedure.
Now in my previous work, the usual policy was that you never ever run any process as root. You create a user/group for it and run from there. Of course, the system did run some things as root, but we could achieve all business logic processing without being root.
Now for the HTTP daemon, I can run it without root if I don't chroot inside the application. So isn't it more secure for the application to never ever run as root?
Isn't it more secure to run it as mydaemon-user from the beginning? Instead of starting it with root, chrooting, then setuid to mydaemon-user?
Best Answer
It seems that others have missed your point, which was not reasons why to use changed roots, which of course you clearly already know, nor what else you can do to place limits on dæmons, when you also clearly know about running under the aegides of unprivileged user accounts; but why to do this stuff inside the application. There's actually a fairly on point example of why.
Consider the design of the
httpd
dæmon program in Daniel J. Bernstein's publicfile package. The first thing that it does is change root to the root directory that it was told to use with a command argument, then drop privileges to the unprivileged user ID and group ID that are passed in two environment variables.Dæmon management toolsets have dedicated tools for things like changing root directory and dropping to unprivileged user and group IDs. Gerrit Pape's runit has
chpst
. My nosh toolset haschroot
andsetuidgid-fromenv
. Laurent Bercot's s6 hass6-chroot
ands6-setuidgid
. Wayne Marshall's Perp hasruntool
andrunuid
. And so forth. Indeed, they all have M. Bernstein's own daemontools toolset withsetuidgid
as an antecedent.One would think that one could extract the functionality from
httpd
and use such dedicated tools. Then, as you envision, no part of the server program ever runs with superuser privileges.The problem is that one as a direct consequence has to do significantly more work to set up the changed root, and this exposes new problems.
With Bernstein
httpd
as it stands, the only files and directories that are in the root directory tree are ones that are to be published to the world. There is nothing else in the tree at all. Moreover, there is no reason for any executable program image file to exist in that tree.But move the root directory change out into a chain-loading program (or systemd), and suddenly the program image file for
httpd
, any shared libraries that it loads, and any special files in/etc
,/run
, and/dev
that the program loader or C runtime library access during program initialization (which you might find quite surprising if youtruss
/strace
a C or C++ program), also have to be present in the changed root. Otherwisehttpd
cannot be chained to and won't load/run.Remember that this is a HTTP(S) content server. It can potentially serve up any (world-readable) file in the changed root. This now includes things like your shared libraries, your program loader, and copies of various loader/CRTL configuration files for your operating system. And if by some (accidental) means the content server has access to write stuff, a compromised server can possibly gain write access to the program image for
httpd
itself, or even your system's program loader. (Remember that you now have two parallel sets of/usr
,/lib
,/etc
,/run
, and/dev
directories to keep secure.)None of this is the case where
httpd
changes root and drops privileges itself.So you have traded having a small amount of privileged code, that is fairly easy to audit and that runs right at the start of the
httpd
program, running with superuser privileges; for having a greatly expanded attack surface of files and directories within the changed root.This is why it is not as simple as doing everything externally to the service program.
Notice that this is nonetheless a bare minimum of functionality within
httpd
itself. All of the code that does things such as look in the operating system's account database for the user ID and group ID to put into those environment variables in the first place is external to thehttpd
program, in simple standalone auditable commands such asenvuidgid
. (And of course it is a UCSPI tool, so it contains none of the code to listen on the relevant TCP port(s) or to accept connections, those being the domain of commands such astcpserver
,tcp-socket-listen
,tcp-socket-accept
,s6-tcpserver4-socketbinder
,s6-tcpserver4d
, and so on.)Further reading
httpd
. publicfile. cr.yp.to.httpd
. Daniel J. Bernstein's softwares all in one. Softwares. Jonathan de Boyne Pollard. 2016.gopherd
. Daniel J. Bernstein's softwares all in one. Softwares. Jonathan de Boyne Pollard. 2017.