diff --git a/redoctober.go b/redoctober.go index e70eb5b..9024a7d 100644 --- a/redoctober.go +++ b/redoctober.go @@ -22,6 +22,7 @@ import ( "time" "github.com/cloudflare/redoctober/core" + "github.com/coreos/go-systemd/activation" ) // List of URLs to register and their related functions @@ -82,7 +83,7 @@ func queueRequest(process chan<- userRequest, requestType string, w http.Respons // // Returns a valid http.Server handling redoctober JSON requests (and // its associated listener) or an error -func NewServer(process chan<- userRequest, staticPath, addr, certPath, keyPath, caPath string) (*http.Server, *net.Listener, error) { +func NewServer(process chan<- userRequest, staticPath, addr, certPath, keyPath, caPath string, useSystemdSocket bool) (*http.Server, *net.Listener, error) { cert, err := tls.LoadX509KeyPair(certPath, keyPath) if err != nil { return nil, nil, fmt.Errorf("Error loading certificate (%s, %s): %s", certPath, keyPath, err) @@ -122,13 +123,25 @@ func NewServer(process chan<- userRequest, staticPath, addr, certPath, keyPath, config.ClientCAs = rootPool } - conn, err := net.Listen("tcp", addr) - if err != nil { - return nil, nil, fmt.Errorf("Error starting TCP listener on %s: %s\n", addr, err) + var lstnr net.Listener + if useSystemdSocket { + listenFDs, err := activation.Listeners(true) + if err != nil { + log.Fatal(err) + } + if len(listenFDs) != 1 { + log.Fatal("Unexpected number of socket activation FDs!") + } + lstnr = listenFDs[0] + } else { + conn, err := net.Listen("tcp", addr) + if err != nil { + return nil, nil, fmt.Errorf("Error starting TCP listener on %s: %s\n", addr, err) + } + + lstnr = tls.NewListener(conn, &config) + } - - lstnr := tls.NewListener(conn, &config) - mux := http.NewServeMux() // queue up post URIs @@ -198,12 +211,13 @@ func main() { var staticPath = flag.String("static", "", "Path to override built-in index.html") var vaultPath = flag.String("vaultpath", "diskrecord.json", "Path to the the disk vault") var addr = flag.String("addr", "localhost:8080", "Server and port separated by :") + var useSystemdSocket = flag.Bool("systemdfds", false, "Use systemd socket activation to listen on a file. Useful for binding privileged sockets.") var certPath = flag.String("cert", "", "Path of TLS certificate in PEM format") var keyPath = flag.String("key", "", "Path of TLS private key in PEM format") var caPath = flag.String("ca", "", "Path of TLS CA for client authentication (optional)") flag.Parse() - if *vaultPath == "" || *addr == "" || *certPath == "" || *keyPath == "" { + if *vaultPath == "" || (*addr == "" && *useSystemdSocket == false) || *certPath == "" || *keyPath == "" { fmt.Fprint(os.Stderr, usage) flag.PrintDefaults() os.Exit(2) @@ -243,7 +257,7 @@ func main() { } }() - s, l, err := NewServer(process, *staticPath, *addr, *certPath, *keyPath, *caPath) + s, l, err := NewServer(process, *staticPath, *addr, *certPath, *keyPath, *caPath, *useSystemdSocket) if err != nil { log.Fatalf("Error starting redoctober server: %s\n", err) } diff --git a/redoctober.service b/redoctober.service new file mode 100644 index 0000000..12c2a30 --- /dev/null +++ b/redoctober.service @@ -0,0 +1,26 @@ +# An example service file for systemd-managed Red October, for use +# with the accompanying socket file. Useful for running as an +# unprivileged user while binding to a privileged port, for example. + +[Unit] +Description='Red October' + +Requires=network.target +After=multi-user.target + +[Exec] +User=redoctober +Group=redoctober + +[Service] +Type=simple +ExecStart=/usr/local/sbin/redoctober -vaultpath=/etc/redoctober/diskrecord.json -cert=/etc/redoctober/cert/server.crt -key=/etc/redoctober/cert/server.pem -systemdfds +User=redoctober +Group=redoctober +SyslogIdentifier=redoctober +StandardOutput=syslog +StandardError=inherit +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/redoctober.socket b/redoctober.socket new file mode 100644 index 0000000..3faf6d5 --- /dev/null +++ b/redoctober.socket @@ -0,0 +1,10 @@ +# Example socket file, instructing systemd to bind 443. Used by the +# corresponding redoctober.service target to launch a socket-activated +# Red October instance that can run unprivileged but bind to a +# privileged port. + +[Socket] +ListenStream=127.0.0.1:443 + +[Install] +WantedBy=sockets.target \ No newline at end of file