diff --git a/changelog/unreleased/issue-126 b/changelog/unreleased/issue-126 new file mode 100644 index 00000000..58903cb7 --- /dev/null +++ b/changelog/unreleased/issue-126 @@ -0,0 +1,7 @@ +Feature: Allow running rest-server via systemd socket activation + +We've added the option to have systemd create the listening socket and start the rest-server on demand. + +https://github.com/restic/rest-server/issues/126 +https://github.com/restic/rest-server/pull/151 +https://github.com/restic/rest-server/pull/127 diff --git a/cmd/rest-server/listener_unix.go b/cmd/rest-server/listener_unix.go new file mode 100644 index 00000000..3b3f695d --- /dev/null +++ b/cmd/rest-server/listener_unix.go @@ -0,0 +1,45 @@ +// +build !windows + +package main + +import ( + "fmt" + "log" + "net" + + "github.com/coreos/go-systemd/activation" +) + +// findListener tries to find a listener via systemd socket activation. If that +// fails, it tries to create a listener on addr. +func findListener(addr string) (listener net.Listener, err error) { + // try systemd socket activation + listeners, err := activation.Listeners() + if err != nil { + panic(err) + } + + switch len(listeners) { + case 0: + // no listeners found, listen manually + listener, err = net.Listen("tcp", addr) + if err != nil { + return nil, fmt.Errorf("listen on %v failed: %w", addr, err) + } + + log.Printf("start server on %v", addr) + return listener, nil + + case 1: + // one listener supplied by systemd, use that one + // + // for testing, run rest-server with systemd-socket-activate as follows: + // + // systemd-socket-activate -l 8080 ./rest-server + log.Printf("systemd socket activation mode") + return listeners[0], nil + + default: + return nil, fmt.Errorf("got %d listeners from systemd, expected one", len(listeners)) + } +} diff --git a/cmd/rest-server/listener_windows.go b/cmd/rest-server/listener_windows.go new file mode 100644 index 00000000..b1d8071d --- /dev/null +++ b/cmd/rest-server/listener_windows.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "log" + "net" +) + +// findListener creates a listener. +func findListener(addr string) (listener net.Listener, err error) { + // listen manually + listener, err = net.Listen("tcp", addr) + if err != nil { + return nil, fmt.Errorf("listen on %v failed: %w", addr, err) + } + + log.Printf("start server on %v", addr) + return listener, nil +} diff --git a/cmd/rest-server/main.go b/cmd/rest-server/main.go index 1de36077..5fb16de3 100644 --- a/cmd/rest-server/main.go +++ b/cmd/rest-server/main.go @@ -129,16 +129,17 @@ func runRoot(cmd *cobra.Command, args []string) error { if err != nil { return err } + + listener, err := findListener(server.Listen) + if err != nil { + return fmt.Errorf("unable to listen: %w", err) + } + if !enabledTLS { - log.Printf("Starting server on %s\n", server.Listen) - err = http.ListenAndServe(server.Listen, handler) + err = http.Serve(listener, handler) } else { - - log.Println("TLS enabled") - log.Printf("Private key: %s", privateKey) - log.Printf("Public key(certificate): %s", publicKey) - log.Printf("Starting server on %s\n", server.Listen) - err = http.ListenAndServeTLS(server.Listen, publicKey, privateKey, handler) + log.Printf("TLS enabled, private key %s, pubkey %v", privateKey, publicKey) + err = http.ServeTLS(listener, handler, publicKey, privateKey) } return err diff --git a/examples/systemd/rest-server.service b/examples/systemd/rest-server.service index 360d39f2..b5f28bda 100644 --- a/examples/systemd/rest-server.service +++ b/examples/systemd/rest-server.service @@ -3,6 +3,9 @@ Description=Rest Server After=syslog.target After=network.target +# if you want to use socket activation, make sure to require the socket here +#Requires=rest-server.socket + [Service] Type=simple # You may prefer to use a different user or group on your system. diff --git a/examples/systemd/rest-server.socket b/examples/systemd/rest-server.socket new file mode 100644 index 00000000..ba3262aa --- /dev/null +++ b/examples/systemd/rest-server.socket @@ -0,0 +1,5 @@ +[Socket] +ListenStream = 8080 + +[Install] +WantedBy = sockets.target diff --git a/go.mod b/go.mod index 1176fd04..5f49c0a8 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/restic/rest-server go 1.14 require ( + github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/felixge/httpsnoop v1.0.2 // indirect github.com/gorilla/handlers v1.5.1 github.com/miolini/datacounter v1.0.2 diff --git a/go.sum b/go.sum index 7d6cad57..e698e885 100644 --- a/go.sum +++ b/go.sum @@ -65,6 +65,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=