Initial commit.

A gemini server which drops privileges to the 'nobody' user, hosts files
including directory listings (index.gmi or a default listing), and runs
CGIs out of /cgi-bin/*.
This commit is contained in:
tjpcc 2023-09-08 14:54:53 -06:00
commit 1f519d05bf
8 changed files with 245 additions and 0 deletions

7
LICENSE.txt Normal file
View File

@ -0,0 +1,7 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

31
README.gmi Normal file
View File

@ -0,0 +1,31 @@
# SR-71, a small web server
```ASCII art of an SR-71 Blackbird
,
dMb,
,dMMMMb, ,,
,dMMMMMMMMMb, eeee8888"
,mMMm!!!!XXXXMMMMM"""
,d!!XXMMXX88888888W"
`MX88dMM8888WWWMMMMMMb,
'""MMMMMMMMMMMMMMMMb
MMMMMMMMMMMMMMMMMMb,
dMMMMMMMMMMMMMMMMMMMMb,,
_,dMMMMMMMMMMXXXX!!!!!!!!!!!!!!XXXXXMP
_,dMMXX!!!!!!!!!!!!!!!!!!XXXXX888888888WWC
_,dMMX!!!MMMM!!!!!!!!XXXXXX888888888888WWMMMMMb,
dMMX!!!!!MMM!XXXXXX88888888888888888WWMMMMMMMMMMMb
dMMXXXXXX8MMMM88888888888888888WWWMMMMMMMMMMMMMMMMMb ,d8
MMMMWW888888MMMMM8888888WWMMMMMMMMMMMMMMMMMMMMMMMMMMM,d88P'
YMMMMMWW888888WWMMMMMMMMMMP"""' `"YMMMMMMMMMMMXMMMMMP
`""YMMMMMMMMMMMMMP""' mMMMm!XXXXX8888888e,
,d!!XXMM888888888888WW
"MX88dMM888888WWWMMMMMMb
"""``'"YMMMMMMMYMMM
`"YMMMMM
`"YMP
```
The SR-71 Blackbird was arguably the pinnacle of slide rule-based engineering.
The last airplane designed without the use of electronic calculators, it was developed from concept to first flight in just over 2 years. It few higher and faster than any jet of its time (holding the speed record to this day), and pioneered low-observability (stealth) characteristics.

32
README.md Normal file
View File

@ -0,0 +1,32 @@
# SR-71, a small web server
```
,
dMb,
,dMMMMb, ,,
,dMMMMMMMMMb, eeee8888"
,mMMm!!!!XXXXMMMMM"""
,d!!XXMMXX88888888W"
`MX88dMM8888WWWMMMMMMb,
'""MMMMMMMMMMMMMMMMb
MMMMMMMMMMMMMMMMMMb,
dMMMMMMMMMMMMMMMMMMMMb,,
_,dMMMMMMMMMMXXXX!!!!!!!!!!!!!!XXXXXMP
_,dMMXX!!!!!!!!!!!!!!!!!!XXXXX888888888WWC
_,dMMX!!!MMMM!!!!!!!!XXXXXX888888888888WWMMMMMb,
dMMX!!!!!MMM!XXXXXX88888888888888888WWMMMMMMMMMMMb
dMMXXXXXX8MMMM88888888888888888WWWMMMMMMMMMMMMMMMMMb ,d8
MMMMWW888888MMMMM8888888WWMMMMMMMMMMMMMMMMMMMMMMMMMMM,d88P'
YMMMMMWW888888WWMMMMMMMMMMP"""' `"YMMMMMMMMMMMXMMMMMP
`""YMMMMMMMMMMMMMP""' mMMMm!XXXXX8888888e,
,d!!XXMM888888888888WW
"MX88dMM888888WWWMMMMMMb
"""``'"YMMMMMMMYMMM
`"YMMMMM
`"YMP
```
The SR-71 Blackbird was arguably the pinnacle of slide rule-based engineering.
The last airplane designed without the use of electronic calculators, it was developed from concept to first flight in just over 2 years. It few higher and faster than any jet of its time (holding the speed record to this day), and pioneered low-observability (stealth) characteristics.

70
config.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"context"
"os"
"os/signal"
"os/user"
"strconv"
"syscall"
"tildegit.org/tjp/sliderule/logging"
)
type config struct {
hostname string
geminiRoot string
tlsKeyFile string
tlsCertFile string
}
func configure() config {
return config{
hostname: os.Getenv("HOST_NAME"),
geminiRoot: os.Getenv("GEMINI_ROOT"),
tlsKeyFile: os.Getenv("TLS_KEY_FILE"),
tlsCertFile: os.Getenv("TLS_CERT_FILE"),
}
}
func dropPrivileges() (bool, error) {
me, err := user.Current()
if err != nil {
return false, err
}
if me.Uid != "0" {
return false, nil
}
nobody, err := user.Lookup("nobody")
if err != nil {
return false, err
}
uid, err := strconv.Atoi(nobody.Uid)
if err != nil {
return false, err
}
if err := syscall.Setuid(uid); err != nil {
return false, err
}
return true, nil
}
func serverContext() (context.Context, logging.Logger, logging.Logger, logging.Logger, logging.Logger) {
debug, info, warn, err := logging.DefaultLoggers()
ctx := signals(context.Background())
ctx = context.WithValue(ctx, "debuglog", debug)
ctx = context.WithValue(ctx, "infolog", info)
ctx = context.WithValue(ctx, "warnlog", warn)
ctx = context.WithValue(ctx, "errorlog", err)
return ctx, debug, info, warn, err
}
func signals(ctx context.Context) context.Context {
ctx, _ = signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGHUP)
return ctx
}

10
go.mod Normal file
View File

@ -0,0 +1,10 @@
module tildegit.org/tjp/sr-71
go 1.19
require tildegit.org/tjp/sliderule v1.0.1-0.20230502145627-c7f8a1292544
require (
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
)

16
go.sum Normal file
View File

@ -0,0 +1,16 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
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.0 h1:DBXmzm5m3DXxzCE5kTHMulsBo9twd91n6fXR1ciXOoI=
tildegit.org/tjp/sliderule v1.0.0/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik=
tildegit.org/tjp/sliderule v1.0.1-0.20230502041606-3d5acb3b68af h1:OWVyuBsOToP8cm0QWZutyxa5vLJAFvgX7uSzpWLq5RI=
tildegit.org/tjp/sliderule v1.0.1-0.20230502041606-3d5acb3b68af/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik=
tildegit.org/tjp/sliderule v1.0.1-0.20230502144345-ace21031f98f h1:o4+wUZVYl6uPlzxcJ8gSwMz81enfHfMhAYCOnER8DOQ=
tildegit.org/tjp/sliderule v1.0.1-0.20230502144345-ace21031f98f/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik=
tildegit.org/tjp/sliderule v1.0.1-0.20230502145627-c7f8a1292544 h1:5m8q5VZRt1RVJ8F6ti9WFPVkIgrJ1UmnGbxTry+GYIg=
tildegit.org/tjp/sliderule v1.0.1-0.20230502145627-c7f8a1292544/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik=

48
main.go Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"log"
"os"
"tildegit.org/tjp/sliderule/gemini"
"tildegit.org/tjp/sliderule/logging"
)
func main() {
conf := configure()
gemTLS, err := gemini.FileTLS(conf.tlsCertFile, conf.tlsKeyFile)
if err != nil {
log.Fatal(err)
}
dropped, err := dropPrivileges()
if err != nil {
log.Fatal(err)
}
ctx, _, info, warn, errlog := serverContext()
if !dropped {
warn.Log("msg", "dropping privileges to 'nobody' failed")
}
handler := logging.LogRequests(info)(geminiRouter(conf))
server, err := gemini.NewServer(
ctx,
conf.hostname,
"tcp",
"127.0.0.1:1965",
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)
}
}

31
routes.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
"os"
sr "tildegit.org/tjp/sliderule"
"tildegit.org/tjp/sliderule/contrib/cgi"
"tildegit.org/tjp/sliderule/contrib/fs"
)
func geminiRouter(conf config) sr.Handler {
fsys := os.DirFS(conf.geminiRoot)
router := &sr.Router{}
router.Route(
"/*",
sr.FallthroughHandler(
fs.GeminiFileHandler(fsys),
fs.GeminiDirectoryDefault(fsys, "index.gmi"),
fs.GeminiDirectoryListing(fsys, nil),
),
)
router.Route(
"/cgi-bin/*",
cgi.GeminiCGIDirectory("/cgi-bin/", "./cgi-bin/"),
)
return router.Handler()
}