Privilege dropping under Unix

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))
}

Pages linking here