Add the ability to listen to systemd-provided sockets.

Add a new flag, -systemdfds, which causes Red October to expect to be
provisioned on launch with file descriptors for sockets opened by
systemd. This is useful for socket activation, but also allows systemd
to bind privileged ports for us. I've included example systemd
configuration files that successfully start Red October as a service
user without admin rights but bound to 443 in a Jessie VM for me. They
need to be installed where systemd expects them, which on Jessie is
/etc/systemd/system/redoctober.service and
/etc/systemd/system/sockets.target.wants/redoctober.socket.
This commit is contained in:
Joshua Kroll
2015-10-09 11:24:08 -07:00
parent f8ed334437
commit 638a25bbbc
3 changed files with 59 additions and 9 deletions

View File

@@ -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)
}

26
redoctober.service Normal file
View File

@@ -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

10
redoctober.socket Normal file
View File

@@ -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