Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for systemd socket activation #704

Merged
merged 3 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ Note that when running in this mode the [`ip-whitelist`](docs/Hook-Rules.md#matc
## CORS Headers
If you want to set CORS headers, you can use the `-header name=value` flag while starting [webhook][w] to set the appropriate CORS headers that will be returned with each response.

## Running under `systemd`
On platforms that use [systemd](https://systemd.io), [webhook][w] supports the _socket activation_ mechanism. If [webhook][w] detects that it has been launched from a systemd-managed socket it will automatically use that instead of opening its own listening port. See [the systemd page](docs/Systemd-Activation.md) for full details.

## Interested in running webhook inside of a Docker container?
You can use one of the following Docker images, or create your own (please read [this discussion](https://github.com/adnanh/webhook/issues/63)):
- [almir/webhook](https://github.com/almir/docker-webhook)
Expand Down
61 changes: 61 additions & 0 deletions docs/Systemd-Activation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Using systemd socket activation

_New in v2.9.0_
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to whatever the next release version number will be...


On platforms that use [systemd](https://systemd.io), [webhook][w]
supports the _socket activation_ mechanism. In this mode, systemd itself is responsible for managing the listening socket, and it launches [webhook][w] the first time it receives a request on the socket. This has a number of advantages over the standard mode:

- [webhook][w] can run as a normal user while still being able to use a port number like 80 or 443 that would normally require root privilege
- if the [webhook][w] process dies and is restarted, pending connections are not dropped - they just keep waiting until the restarted [webhook][w] is ready

No special configuration is necessary to tell [webhook][w] that socket activation is being used - socket activation sets specific environment variables when launching the activated service, if [webhook][w] detects these variables it will ignore the `-port` and `-socket` options and simply use the systemd-provided socket instead of opening its own.

## Configuration
To run [webhook][w] with socket activation you need to create _two_ separate unit files in your systemd configuration directory (typically `/etc/systemd/system`), one for the socket and one for the service. They must have matching names; in this example we use `webhook.socket` and `webhook.service`. At their simplest, these files should look like:

**webhook.socket**
```
[Unit]
Description=Webhook server socket

[Socket]
# Listen on all network interfaces, port 9000
ListenStream=9000

# Alternatives:

## Listen on one specific interface only
# ListenStream=10.0.0.1:9000
# FreeBind=true

## Listen on a Unix domain socket
# ListenStream=/tmp/webhook.sock

[Install]
WantedBy=multi-user.target
```

**webhook.service**
```
[Unit]
Description=Webhook server

[Service]
Type=exec
ExecStart=webhook -nopanic -hooks /etc/webhook/hooks.yml

# Which user should the webhooks run as?
User=nobody
Group=nogroup
```

You should enable and start the _socket_, but it is not necessary to enable the _service_ - this will be started automatically when the socket receives its first request.

```sh
sudo systemctl enable webhook.socket
sudo systemctl start webhook.socket
```

Systemd unit files support many other options, see the [systemd.socket](https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html) and [systemd.service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html) manual pages for full details.

[w]: https://github.com/adnanh/webhook
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.22.0
require (
github.com/Microsoft/go-winio v0.6.2
github.com/clbanning/mxj/v2 v2.7.0
github.com/coreos/go-systemd/v22 v22.5.0
github.com/dustin/go-humanize v1.0.1
github.com/fsnotify/fsnotify v1.7.0
github.com/ghodss/yaml v1.0.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
Expand All @@ -10,6 +12,7 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
Expand Down
25 changes: 25 additions & 0 deletions platform_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,40 @@ package main
import (
"flag"
"fmt"
"github.com/coreos/go-systemd/v22/activation"
"net"
)

func platformFlags() {
flag.StringVar(&socket, "socket", "", "path to a Unix socket (e.g. /tmp/webhook.sock) to use instead of listening on an ip and port; if specified, the ip and port options are ignored")
flag.IntVar(&setGID, "setgid", 0, "set group ID after opening listening port; must be used with setuid, not permitted with -socket")
flag.IntVar(&setUID, "setuid", 0, "set user ID after opening listening port; must be used with setgid, not permitted with -socket")
Comment on lines +15 to +16
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since setuid and setgid don't work on Windows anyway, I've moved then in here so that they are only available as flags for non-Windows builds.

}

func trySocketListener() (net.Listener, error) {
// first check whether we have any sockets from systemd
listeners, err := activation.Listeners()
if err != nil {
return nil, fmt.Errorf("failed to retrieve sockets from systemd: %w", err)
}
numListeners := len(listeners)
if numListeners > 1 {
return nil, fmt.Errorf("received %d sockets from systemd, but only 1 is supported", numListeners)
}
if numListeners == 1 {
sockAddr := listeners[0].Addr()
if sockAddr.Network() == "tcp" {
addr = sockAddr.String()
} else {
addr = fmt.Sprintf("{%s:%s}", sockAddr.Network(), sockAddr.String())
}
return listeners[0], nil
}
// if we get to here, we got no sockets from systemd, so check -socket flag
if socket != "" {
if setGID != 0 || setUID != 0 {
return nil, fmt.Errorf("-setuid and -setgid options are not compatible with -socket. If you need to bind a socket as root but run webhook as a different user, consider using systemd activation")
}
addr = fmt.Sprintf("{unix:%s}", socket)
return net.Listen("unix", socket)
}
Expand Down
191 changes: 191 additions & 0 deletions vendor/github.com/coreos/go-systemd/v22/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions vendor/github.com/coreos/go-systemd/v22/NOTICE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading