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)