add go module support #34
37
README.md
37
README.md
|
@ -55,9 +55,10 @@ Molly Brown is known to run on:
|
|||
|
||||
Please let us know if you get it to work on some other platform!
|
||||
|
||||
Molly Brown only has a single dependency beyond the Go standard
|
||||
library, which is [this TOML parsing
|
||||
library](https://github.com/BurntSushi/toml).
|
||||
Molly Brown only has two dependencies beyond the Go standard
|
||||
library, which are [this TOML parsing
|
||||
library](https://github.com/BurntSushi/toml) and [this shell command
|
||||
lexer](https://github.com/google/shlex).
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -285,17 +286,14 @@ a request URL includes components after the path to an executable
|
|||
executable (e.g. `/var/gemini/cgi-bin/scripty.py`) while the variable
|
||||
`PATH_INFO` will contain the remainder (e.g. `foo/bar/baz`).
|
||||
|
||||
It is very important to be aware that programs written in Go are
|
||||
*unable* to reliably change their UID once started, due to how
|
||||
goroutines are implemented on unix systems. As an unavoidable
|
||||
consequence of this, CGI processes started by Molly Brown are run as
|
||||
the same user as the server process. This means CGI processes
|
||||
necessarily have read and write access to the server logs and to the
|
||||
TLS private key. There is no way to work around this. As such you
|
||||
must be extremely careful about only running trustworthy CGI
|
||||
applications, ideally only applications you have carefully written
|
||||
yourself. Allowing untrusted users to upload arbitrary executable
|
||||
files into a CGI path is a serious security vulnerability.
|
||||
Be aware that CGI processes started by Molly Brown are run as the same
|
||||
user as the server process. This means that by default, CGI processes
|
||||
have read and write access to the server logs and to the TLS private
|
||||
key. Set a `CGICommand` to something that drop privileges before
|
||||
running `$SCRIPT_PATH` to add guard rails to CGI processes provided by
|
||||
other system users. Otherwise, allowing untrusted users to upload
|
||||
arbitrary executable files into a CGI path is a serious security
|
||||
vulnerability.
|
||||
|
||||
SCGI applications must be started separately (i.e. Molly Brown expects
|
||||
them to already be running and will not attempt to start them itself),
|
||||
|
@ -314,6 +312,17 @@ startup, database connection etc. on each request).
|
|||
if wildcards are used, the path should *not* end in a trailing slash
|
||||
- this appears to be a peculiarity of the Go standard library's
|
||||
`filepath.Glob` function.
|
||||
* `CGICommand`: An executable command which will be run instead of
|
||||
cgi programs. Arguments specified in `CGICommand` (though not the
|
||||
initial command itself) will have a few simple string replacements
|
||||
performed on them: `{}` and `{prg}` are populated with the same
|
||||
value as `$SCRIPT_PATH`, and `{user}` with the `~username` in the
|
||||
URL. Ultimately, the command should typically end up running the
|
||||
CGI program anwyay, either as `{}` or `{prg}` in the `CGICommand`,
|
||||
or as `$SCRIPT_PATH` in a script that `CGICommand` points to. But
|
||||
this facility gives a server admin the opportunity to change the
|
||||
effective user, chroot into a limited filesystem, or take any other
|
||||
appropriate steps to sandbox user-provided programs.
|
||||
* `SCGIPaths`: In this section of the config file, keys are URL path
|
||||
prefixes and values are filesystem paths to unix domain sockets.
|
||||
Any request for a URL whose path begins with one of the specified
|
||||
|
|
17
config.go
17
config.go
|
@ -2,10 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/BurntSushi/toml"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"github.com/BurntSushi/toml"
|
||||
|
||||
"github.com/google/shlex"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -24,11 +26,14 @@ type Config struct {
|
|||
PermRedirects map[string]string
|
||||
MimeOverrides map[string]string
|
||||
CGIPaths []string
|
||||
CGICommand string
|
||||
SCGIPaths map[string]string
|
||||
CertificateZones map[string][]string
|
||||
DirectorySort string
|
||||
DirectoryReverse bool
|
||||
DirectoryTitles bool
|
||||
|
||||
cgiCommandArgs []string
|
||||
}
|
||||
|
||||
type MollyFile struct {
|
||||
|
@ -61,6 +66,7 @@ func getConfig(filename string) (Config, error) {
|
|||
config.TempRedirects = make(map[string]string)
|
||||
config.PermRedirects = make(map[string]string)
|
||||
config.CGIPaths = make([]string, 0)
|
||||
config.CGICommand = ""
|
||||
config.SCGIPaths = make(map[string]string)
|
||||
config.DirectorySort = "Name"
|
||||
|
||||
|
@ -93,6 +99,15 @@ func getConfig(filename string) (Config, error) {
|
|||
}
|
||||
config.CGIPaths = cgiPaths
|
||||
|
||||
if config.CGICommand != "" {
|
||||
spl, err := shlex.Split(config.CGICommand)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
config.CGICommand = spl[0]
|
||||
config.cgiCommandArgs = spl[1:]
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
|
41
dynamic.go
41
dynamic.go
|
@ -10,6 +10,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -47,10 +48,18 @@ func handleCGI(config Config, path string, cgiPath string, URL *url.URL, log *Lo
|
|||
// Prepare environment variables
|
||||
vars := prepareCGIVariables(config, URL, conn, scriptPath, pathInfo)
|
||||
|
||||
// Use the CGICommand if provided
|
||||
var script []string
|
||||
if config.CGICommand != "" {
|
||||
script = prepareCGICommand(config, scriptPath)
|
||||
} else {
|
||||
script = []string{scriptPath}
|
||||
}
|
||||
|
||||
// Spawn process
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, scriptPath)
|
||||
cmd := exec.CommandContext(ctx, script[0], script[1:]...)
|
||||
cmd.Env = []string{}
|
||||
for key, value := range vars {
|
||||
cmd.Env = append(cmd.Env, key+"="+value)
|
||||
|
@ -148,6 +157,36 @@ func handleSCGI(URL *url.URL, scgiPath string, scgiSocket string, config Config,
|
|||
}
|
||||
}
|
||||
|
||||
func prepareCGICommand(config Config, scriptPath string) []string {
|
||||
if config.CGICommand == "" {
|
||||
return []string{scriptPath}
|
||||
}
|
||||
|
||||
cmd := make([]string, len(config.cgiCommandArgs)+1)
|
||||
cmd[0] = config.CGICommand
|
||||
|
||||
substitutions := map[string]string{
|
||||
"{}": scriptPath,
|
||||
"{prg}": scriptPath,
|
||||
}
|
||||
base := filepath.Join(config.DocBase, config.HomeDocBase) + string(filepath.Separator)
|
||||
if strings.HasPrefix(scriptPath, base) {
|
||||
segments := strings.Split(scriptPath[len(base):], string(filepath.Separator))
|
||||
if len(segments) > 1 {
|
||||
substitutions["{user}"] = segments[0]
|
||||
}
|
||||
}
|
||||
|
||||
for i, arg := range config.cgiCommandArgs {
|
||||
for key, val := range substitutions {
|
||||
arg = strings.ReplaceAll(arg, key, val)
|
||||
}
|
||||
cmd[i+1] = arg
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func prepareCGIVariables(config Config, URL *url.URL, conn net.Conn, script_path string, path_info string) map[string]string {
|
||||
vars := prepareGatewayVariables(config, URL, conn)
|
||||
vars["GATEWAY_INTERFACE"] = "CGI/1.1"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
module mollybrown
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.1.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
Loading…
Reference in New Issue