feat: add running soft-serve as non-root using setgid/setuid

Ayman Bagabas created

Change summary

examples/non-root/main.go | 65 +++++++++++++++++++++++++++++++++++++++++
server/server.go          |  6 +++
2 files changed, 71 insertions(+)

Detailed changes

examples/non-root/main.go 🔗

@@ -0,0 +1,65 @@
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net"
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+
+	"github.com/charmbracelet/soft-serve/config"
+	"github.com/charmbracelet/soft-serve/server"
+)
+
+const (
+	port = 22
+	gid  = 1000
+	uid  = 1000
+)
+
+var (
+	addr = fmt.Sprintf(":%d", port)
+)
+
+func main() {
+	// To listen on port 22 we need root privileges
+	ls, err := net.Listen("tcp", addr)
+	if err != nil {
+		log.Fatalf("Can't listen: %s", err)
+	}
+	// We don't need root privileges any more
+	if err := syscall.Setgid(gid); err != nil {
+		log.Fatalf("Setgid error: %s", err)
+	}
+	if err := syscall.Setuid(uid); err != nil {
+		log.Fatalf("Setuid error: %s", err)
+	}
+	cfg := config.DefaultConfig()
+	cfg.Port = port
+	s := server.NewServer(cfg)
+
+	done := make(chan os.Signal, 1)
+	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+	log.Printf("Starting SSH server on %s:%d", cfg.BindAddr, cfg.Port)
+	go func() {
+		if err := s.Serve(ls); err != nil {
+			log.Fatalln(err)
+		}
+	}()
+
+	<-done
+
+	log.Printf("Stopping SSH server on %s:%d", cfg.BindAddr, cfg.Port)
+	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+	defer func() { cancel() }()
+	if err := s.Shutdown(ctx); err != nil {
+		log.Fatalln(err)
+	}
+}

server/server.go 🔗

@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"log"
+	"net"
 
 	"github.com/charmbracelet/soft-serve/config"
 	appCfg "github.com/charmbracelet/soft-serve/internal/config"
@@ -69,6 +70,11 @@ func (srv *Server) Start() error {
 	return srv.SSHServer.ListenAndServe()
 }
 
+// Serve serves the SSH server using the provided listener.
+func (srv *Server) Serve(l net.Listener) error {
+	return srv.SSHServer.Serve(l)
+}
+
 // Shutdown lets the server gracefully shutdown.
 func (srv *Server) Shutdown(ctx context.Context) error {
 	return srv.SSHServer.Shutdown(ctx)