038537d53431312920c8fb9a53c827158861a73c

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
Ayman Bagabas <ayman.bagabas@gmail.com>
Date

Message

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

Diff

 1diff --git a/examples/non-root/main.go b/examples/non-root/main.go
 2new file mode 100644
 3index 0000000000000000000000000000000000000000..352212734962d1fcd0b2fdeba3fcd87d25c6ca2d
 4--- /dev/null
 5+++ b/examples/non-root/main.go
 6@@ -0,0 +1,65 @@
 7+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
 8+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 9+
10+package main
11+
12+import (
13+	"context"
14+	"fmt"
15+	"log"
16+	"net"
17+	"os"
18+	"os/signal"
19+	"syscall"
20+	"time"
21+
22+	"github.com/charmbracelet/soft-serve/config"
23+	"github.com/charmbracelet/soft-serve/server"
24+)
25+
26+const (
27+	port = 22
28+	gid  = 1000
29+	uid  = 1000
30+)
31+
32+var (
33+	addr = fmt.Sprintf(":%d", port)
34+)
35+
36+func main() {
37+	// To listen on port 22 we need root privileges
38+	ls, err := net.Listen("tcp", addr)
39+	if err != nil {
40+		log.Fatalf("Can't listen: %s", err)
41+	}
42+	// We don't need root privileges any more
43+	if err := syscall.Setgid(gid); err != nil {
44+		log.Fatalf("Setgid error: %s", err)
45+	}
46+	if err := syscall.Setuid(uid); err != nil {
47+		log.Fatalf("Setuid error: %s", err)
48+	}
49+	cfg := config.DefaultConfig()
50+	cfg.Port = port
51+	s := server.NewServer(cfg)
52+
53+	done := make(chan os.Signal, 1)
54+	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
55+
56+	log.Printf("Starting SSH server on %s:%d", cfg.BindAddr, cfg.Port)
57+	go func() {
58+		if err := s.Serve(ls); err != nil {
59+			log.Fatalln(err)
60+		}
61+	}()
62+
63+	<-done
64+
65+	log.Printf("Stopping SSH server on %s:%d", cfg.BindAddr, cfg.Port)
66+	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
67+	defer func() { cancel() }()
68+	if err := s.Shutdown(ctx); err != nil {
69+		log.Fatalln(err)
70+	}
71+}
72diff --git a/server/server.go b/server/server.go
73index 9d9d81e7dd6dd81cfc1d9d135ecbd62c161c37bd..708aa3e2fa0c1b261ddc6456f7569e143b5e45eb 100644
74--- a/server/server.go
75+++ b/server/server.go
76@@ -4,6 +4,7 @@ import (
77 	"context"
78 	"fmt"
79 	"log"
80+	"net"
81 
82 	"github.com/charmbracelet/soft-serve/config"
83 	appCfg "github.com/charmbracelet/soft-serve/internal/config"
84@@ -69,6 +70,11 @@ func (srv *Server) Start() error {
85 	return srv.SSHServer.ListenAndServe()
86 }
87 
88+// Serve serves the SSH server using the provided listener.
89+func (srv *Server) Serve(l net.Listener) error {
90+	return srv.SSHServer.Serve(l)
91+}
92+
93 // Shutdown lets the server gracefully shutdown.
94 func (srv *Server) Shutdown(ctx context.Context) error {
95 	return srv.SSHServer.Shutdown(ctx)