From 7517538d2fccd75dbfb4f8b5ac9194bf0bbbda76 Mon Sep 17 00:00:00 2001 From: tjpcc Date: Fri, 8 Sep 2023 14:54:50 -0600 Subject: fixes & improvements - refactored main.go to do everything which requires root *first*, before dropping privs. this includes binding to finger and gopher ports. - added gopher support! - fixed CGI handling - new cgi-bin/private directory handling in gemini which requires specific TLS client certs (can't do this in gopher, no TLS) - sliderule version bump brings in lots of fixes on the sliderule side --- config.go | 8 +++--- go.mod | 2 +- go.sum | 4 +-- main.go | 87 +++++++++++++++++++++++++++++++++------------------------------ routes.go | 46 +++++++++++++++++++++++++++++++-- 5 files changed, 97 insertions(+), 50 deletions(-) diff --git a/config.go b/config.go index 6af9808..a758903 100644 --- a/config.go +++ b/config.go @@ -16,17 +16,18 @@ type config struct { hostname string geminiRoot string + gopherRoot string tlsKeyFile string tlsCertFile string - uploaderFingerprints []string + privilegedUsers []string fingerResponses map[string]string } func configure() config { - fingerprints := strings.Split(os.Getenv("UPLOADER_FINGERPRINTS"), ",") + privileged := strings.Split(os.Getenv("PRIVILEGED_FINGERPRINTS"), ",") fingers := map[string]string{} for _, pair := range os.Environ() { @@ -40,10 +41,11 @@ func configure() config { return config{ hostname: os.Getenv("HOST_NAME"), geminiRoot: os.Getenv("GEMINI_ROOT"), + gopherRoot: os.Getenv("GOPHER_ROOT"), tlsKeyFile: os.Getenv("TLS_KEY_FILE"), tlsCertFile: os.Getenv("TLS_CERT_FILE"), - uploaderFingerprints: fingerprints, + privilegedUsers: privileged, fingerResponses: fingers, } diff --git a/go.mod b/go.mod index 07ea11d..7ac0c84 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module tildegit.org/tjp/sr-71 go 1.19 -require tildegit.org/tjp/sliderule v1.0.1-0.20230504031331-11b693e72fc5 +require tildegit.org/tjp/sliderule v1.0.1-0.20230509182153-a97f6f500f4e require ( github.com/go-kit/log v0.2.1 // indirect diff --git a/go.sum b/go.sum index 71cf7c3..6f9d682 100644 --- a/go.sum +++ b/go.sum @@ -6,5 +6,5 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -tildegit.org/tjp/sliderule v1.0.1-0.20230504031331-11b693e72fc5 h1:psd8MmCrj3/LreDmXGnr1/heRbtyKdzatmx5PsK0Q70= -tildegit.org/tjp/sliderule v1.0.1-0.20230504031331-11b693e72fc5/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik= +tildegit.org/tjp/sliderule v1.0.1-0.20230509182153-a97f6f500f4e h1:FnTEB2M257SRIgj5WgqSc13/4/3eVs2HVfCQpcuqWMg= +tildegit.org/tjp/sliderule v1.0.1-0.20230509182153-a97f6f500f4e/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik= diff --git a/main.go b/main.go index 1d6efc0..69ec021 100644 --- a/main.go +++ b/main.go @@ -4,11 +4,12 @@ import ( "context" "crypto/tls" "log" - "os" "sync" + sr "tildegit.org/tjp/sliderule" "tildegit.org/tjp/sliderule/finger" "tildegit.org/tjp/sliderule/gemini" + "tildegit.org/tjp/sliderule/gopher" "tildegit.org/tjp/sliderule/logging" ) @@ -19,75 +20,77 @@ func main() { log.Fatal(err) } - dropped, err := dropPrivileges() + ctx, _, info, warn, errlog := serverContext() + + gopherSrv, err := buildGopher(ctx, conf, info, errlog) if err != nil { log.Fatal(err) } - ctx, _, info, warn, errlog := serverContext() + fingerSrv, err := buildFinger(ctx, conf, info, errlog) + if err != nil { + log.Fatal(err) + } + dropped, err := dropPrivileges() + if err != nil { + log.Fatal(err) + } if !dropped { _ = warn.Log("msg", "dropping privileges to 'nobody' failed") } + gemSrv, err := buildGemini(ctx, conf, gemTLS, info, errlog) + if err != nil { + log.Fatal(err) + } + wg := &sync.WaitGroup{} - go startGemini(ctx, conf, wg, gemTLS, info, errlog) - go startFinger(ctx, conf, wg, info, errlog) - wg.Add(2) + + wg.Add(3) + go runServer(gemSrv, wg) + go runServer(gopherSrv, wg) + go runServer(fingerSrv, wg) wg.Wait() } -func startGemini( +func buildGemini( ctx context.Context, conf config, - wg *sync.WaitGroup, gemTLS *tls.Config, infolog logging.Logger, errlog logging.Logger, -) { - defer wg.Done() - +) (sr.Server, error) { handler := logging.LogRequests(infolog)(geminiRouter(conf)) - server, err := gemini.NewServer( - ctx, - conf.hostname, - "tcp", - "", - handler, - errlog, - gemTLS, - ) - if err != nil { - _ = errlog.Log("msg", "error building server", "error", err) - os.Exit(1) - } - - if err := server.Serve(); err != nil { - _ = errlog.Log("msg", "error serving gemini", "error", err) - os.Exit(1) - } + infolog.Log("msg", "starting gemini server", "gemini_root", conf.geminiRoot) + return gemini.NewServer(ctx, conf.hostname, "tcp", "", handler, errlog, gemTLS) } -func startFinger( +func buildGopher( ctx context.Context, conf config, - wg *sync.WaitGroup, infolog logging.Logger, errlog logging.Logger, -) { - defer wg.Done() +) (sr.Server, error) { + handler := logging.LogRequests(infolog)(gopherRouter(conf)) + infolog.Log("msg", "starting gopher server", "gopher_root", conf.gopherRoot) + return gopher.NewServer(ctx, conf.hostname, "tcp", "", handler, errlog) +} +func buildFinger( + ctx context.Context, + conf config, + infolog logging.Logger, + errlog logging.Logger, +) (sr.Server, error) { handler := logging.LogRequests(infolog)(fingerHandler(conf)) - server, err := finger.NewServer(ctx, conf.hostname, "tcp", "", handler, errlog) - if err != nil { - _ = errlog.Log("msg", "error building server", "error", err) - os.Exit(1) - } + infolog.Log("msg", "starting finger server", "finger_users", len(conf.fingerResponses)) + return finger.NewServer(ctx, conf.hostname, "tcp", "", handler, errlog) +} - if err := server.Serve(); err != nil { - _ = errlog.Log("msg", "error serving finger", "error", err) - os.Exit(1) - } +func runServer(server sr.Server, wg *sync.WaitGroup) error { + defer wg.Done() + return server.Serve() } diff --git a/routes.go b/routes.go index 35bdfc1..c4c9df0 100644 --- a/routes.go +++ b/routes.go @@ -21,12 +21,14 @@ import ( func geminiRouter(conf config) sr.Handler { fsys := os.DirFS(conf.geminiRoot) + privileged := tlsAuth(conf.privilegedUsers) + router := &sr.Router{} router.Route( "/*", gemini.GeminiOnly(true)(sr.FallthroughHandler( - fs.TitanUpload(tlsAuth(conf.uploaderFingerprints), conf.geminiRoot)(postUploadRedirect), + fs.TitanUpload(privileged, conf.geminiRoot)(postUploadRedirect), fs.GeminiFileHandler(fsys), fs.GeminiDirectoryDefault(fsys, "index.gmi"), fs.GeminiDirectoryListing(fsys, nil), @@ -35,7 +37,47 @@ func geminiRouter(conf config) sr.Handler { router.Route( "/cgi-bin/*", - gemini.GeminiOnly(false)(cgi.GeminiCGIDirectory("/cgi-bin/", "./cgi-bin/")), + gemini.GeminiOnly(false)(cgi.GeminiCGIDirectory( + "/cgi-bin/", + strings.Join([]string{".", strings.Trim(conf.geminiRoot, "/"), "cgi-bin"}, "/"), + )), + ) + + router.Route( + "/cgi-bin/private/*", + gemini.GeminiOnly(false)(tlsauth.GeminiAuth(privileged)( + cgi.GeminiCGIDirectory("/cgi-bin/private/", strings.Join([]string{ + ".", + strings.Trim(conf.geminiRoot, "/"), + "cgi-bin", + "private", + }, "/")), + )), + ) + + return router.Handler() +} + +func gopherRouter(conf config) sr.Handler { + fsys := os.DirFS(conf.gopherRoot) + + router := &sr.Router{} + + router.Route( + "/*", + sr.FallthroughHandler( + fs.GopherFileHandler(fsys), + fs.GopherDirectoryDefault(fsys, "index.gophermap"), + fs.GopherDirectoryListing(fsys, nil), + ), + ) + + router.Route( + "/cgi-bin/*", + cgi.GopherCGIDirectory( + "/cgi-bin/", + strings.Join([]string{".", strings.Trim(conf.gopherRoot, "/"), "cgi-bin"}, "/"), + ), ) return router.Handler() -- cgit v1.2.3