implement CGICommand as a CGI program override
This commit is contained in:
parent
e42c366565
commit
c9838f3f29
24
README.md
24
README.md
|
@ -285,17 +285,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 +311,11 @@ 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. It can still run the cgi program by executing
|
||||
`$SCRIPT_PATH`, but before doing so it should change the effective
|
||||
user, chroot into a limited file system, and take any other steps
|
||||
appropriate 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
|
||||
|
|
|
@ -2,10 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/BurntSushi/toml"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -24,6 +24,7 @@ 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
|
||||
|
@ -61,6 +62,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"
|
||||
|
||||
|
|
17
dynamic.go
17
dynamic.go
|
@ -13,6 +13,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/shlex"
|
||||
)
|
||||
|
||||
func handleCGI(config Config, path string, cgiPath string, URL *url.URL, log *LogEntry, errorLog *log.Logger, conn net.Conn) {
|
||||
|
@ -47,10 +49,23 @@ 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 != "" {
|
||||
spl, err := shlex.Split(config.CGICommand)
|
||||
if err != nil {
|
||||
errorLog.Printf("Failed to split CGICommand: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
script = spl
|
||||
} 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)
|
||||
|
|
Loading…
Reference in New Issue