feat: add ability to listen on unix socket/named pipe

Add a -socket option that configures the server to listen on a Unix-domain socket or Windows named pipe instead of a TCP port.  This allows webhook to be used behind a reverse proxy on multi-tenant shared hosting without the need to choose (and the permission to bind to) a free port number.

On Windows, -socket is expected to be a named pipe such as \\.\pipe\webhook, and the code uses https://github.com/microsoft/go-winio to bind the listening socket.  On other platforms, -socket is the path to a Unix domain socket such as /tmp/webhook.sock, or an abstract socket name starting with @, bound using the regular net.Listen function with the "network" parameter set to "unix".

Note: this pushes our minimum Go version up to 1.21 as that is what go-winio requires, but that is already the minimum version against which we are testing in the CI matrix.
This commit is contained in:
Ian Roberts 2024-10-19 16:11:26 +01:00
parent b6f24d00a5
commit 596cc5e70c
38 changed files with 4299 additions and 7 deletions

View file

@ -61,6 +61,8 @@ var (
watcher *fsnotify.Watcher
signals chan os.Signal
pidFile *pidfile.PIDFile
socket = ""
addr = ""
)
func matchLoadedHook(id string) *hook.Hook {
@ -86,6 +88,9 @@ func main() {
flag.Var(&hooksFiles, "hooks", "path to the json file containing defined hooks the webhook should serve, use multiple times to load from different files")
flag.Var(&responseHeaders, "header", "response header to return, specified in format name=value, use multiple times to set multiple headers")
// register platform-specific flags
platformFlags()
flag.Parse()
if *justDisplayVersion {
@ -120,13 +125,21 @@ func main() {
// log file opening prior to writing our first log message.
var logQueue []string
addr := fmt.Sprintf("%s:%d", *ip, *port)
// by default the listen address is ip:port (default 0.0.0.0:9000), but
// this may be modified by trySocketListener
addr = fmt.Sprintf("%s:%d", *ip, *port)
// Open listener early so we can drop privileges.
ln, err := net.Listen("tcp", addr)
ln, err := trySocketListener()
if err != nil {
logQueue = append(logQueue, fmt.Sprintf("error listening on port: %s", err))
logQueue = append(logQueue, fmt.Sprintf("error listening on socket: %s", err))
// we'll bail out below
} else if ln == nil {
// Open listener early so we can drop privileges.
ln, err = net.Listen("tcp", addr)
if err != nil {
logQueue = append(logQueue, fmt.Sprintf("error listening on port: %s", err))
// we'll bail out below
}
}
if *setUID != 0 {
@ -276,7 +289,6 @@ func main() {
// Create common HTTP server settings
svr := &http.Server{
Addr: addr,
Handler: r,
}