My treehouse
webserver is designed to bind to the privileged
networking ports 80
and 443
upon startup, to serve HTTP and
HTTPS. This essentially means that treehouse
needs to be started
with root privileges, which it will however drop as soon it has bound
(i.e., secured access) to those ports: The process will use its root
privileges to modify itself to become less privileged, something that
can not be reversed.
This has a number of security benefits. Depending on which user/group
ID (uid/gid) is assumed, the amount of files that the process can
access is significantly reduced. By default, treehouse
will change
its process user and group ID to 65534, aka the "nobody" user which
typically has the least amount of access on a Unix system.
Now treehouse
can in effect only open (and thereby make available to
the outside Internet) files that are "world-readable", i.e., that have
the permission bit set to be publicly readable.
Here is how to do this in Go:
package main
import (
"log"
"net"
"net/http"
"syscall"
)
func main() {
var uid, gid = 65534, 65534
if syscall.Getuid() != 0 {
log.Fatalln("Must be run as root")
}
// listen on port 80, requires root
httplistener, err := net.Listen("tcp", ":http")
if err != nil {
log.Fatalln(err)
}
// drop privileges here
log.Println("Running as root, dropping privileges")
if err = syscall.Setgid(gid); err != nil {
log.Fatalln("Unable to set GID due to error:", err)
}
if err = syscall.Setuid(uid); err != nil {
log.Fatalln("Unable to set UID due to error:", err)
}
// privileges are dropped, we are no longer root
// set up http mux
// ...
// serve HTTP
log.Fatalln(http.Serve(httplistener, mux))
}