Update April 2025. In his post about self-hosting a GoToSocial instance, Phil Hagelberg points out :
Unix was designed as a multi-user operating system. By default it will not allow normal users to listen on port numbers below 1024, such as the HTTPS port 443. Nowadays this is pretty silly; rather than multiple users per computer we have multiple computers per user. I don’t want my program to have to be root just to respond to HTTPS requests, so I ran
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf && sysctl --system
and now any user can do it. Someone will undoubtedly tell you that’s unsafe. It’s fine.
This is a shockingly good insight. I fully intend to follow his example in future. Nevertheless, my remarks below essentially remain valid.
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))
}