From 35d1cedc24c7d3dfa6a67628924c7a9da3e32689 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Wed, 25 Dec 2019 23:00:48 -0600 Subject: [PATCH 1/2] Rewrite server to use explicit listener --- webhook.go | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/webhook.go b/webhook.go index ca53a7d..4686fb6 100644 --- a/webhook.go +++ b/webhook.go @@ -8,6 +8,7 @@ import ( "fmt" "io/ioutil" "log" + "net" "net/http" "net/url" "os" @@ -168,7 +169,8 @@ func main() { err = watcher.Add(hooksFilePath) if err != nil { - log.Fatal("error adding hooks file to the watcher\n", err) + log.Print("error adding hooks file to the watcher\n", err) + return } } @@ -196,25 +198,37 @@ func main() { r.HandleFunc(hooksURL, hookHandler) - if !*secure { - log.Printf("serving hooks on http://%s:%d%s", *ip, *port, hooksURL) - log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", *ip, *port), r)) - } - + // Create common HTTP server settings svr := &http.Server{ Addr: fmt.Sprintf("%s:%d", *ip, *port), Handler: r, - TLSConfig: &tls.Config{ - CipherSuites: getTLSCipherSuites(*tlsCipherSuites), - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - MinVersion: getTLSMinVersion(*tlsMinVersion), - PreferServerCipherSuites: true, - }, - TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), // disable http/2 } + // Open listener + ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *ip, *port)) + if err != nil { + log.Printf("error listening on port: %s", err) + return + } + + // Serve HTTP + if !*secure { + log.Printf("serving hooks on http://%s:%d%s", *ip, *port, hooksURL) + log.Print(svr.Serve(ln)) + return + } + + // Server HTTPS + svr.TLSConfig = &tls.Config{ + CipherSuites: getTLSCipherSuites(*tlsCipherSuites), + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + MinVersion: getTLSMinVersion(*tlsMinVersion), + PreferServerCipherSuites: true, + } + svr.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) // disable http/2 + log.Printf("serving hooks on https://%s:%d%s", *ip, *port, hooksURL) - log.Fatal(svr.ListenAndServeTLS(*cert, *key)) + log.Print(svr.ServeTLS(ln, *cert, *key)) } func hookHandler(w http.ResponseWriter, r *http.Request) { From 77159d9db6c8ff5978ff7f8060ca4048eea5fec3 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Wed, 25 Dec 2019 23:23:35 -0600 Subject: [PATCH 2/2] Add setuid & setgid options Only applicable on unix systems, although Go doesn't support Linux at this time. --- docs/Webhook-Parameters.md | 4 ++++ droppriv_nope.go | 12 ++++++++++++ droppriv_unix.go | 21 +++++++++++++++++++++ webhook.go | 25 +++++++++++++++++++++---- 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 droppriv_nope.go create mode 100644 droppriv_unix.go diff --git a/docs/Webhook-Parameters.md b/docs/Webhook-Parameters.md index bd6fcb4..7d5beed 100644 --- a/docs/Webhook-Parameters.md +++ b/docs/Webhook-Parameters.md @@ -25,6 +25,10 @@ Usage of webhook: port the webhook should serve hooks on (default 9000) -secure use HTTPS instead of HTTP + -setgid int + set group ID after opening listening port; must be used with setuid + -setuid int + set user ID after opening listening port; must be used with setgid -template parse hooks file as a Go template -tls-min-version string diff --git a/droppriv_nope.go b/droppriv_nope.go new file mode 100644 index 0000000..3c6a518 --- /dev/null +++ b/droppriv_nope.go @@ -0,0 +1,12 @@ +// +build linux windows + +package main + +import ( + "errors" + "runtime" +) + +func dropPrivileges(uid, gid int) error { + return errors.New("setuid and setgid not supported on " + runtime.GOOS) +} diff --git a/droppriv_unix.go b/droppriv_unix.go new file mode 100644 index 0000000..c292e7d --- /dev/null +++ b/droppriv_unix.go @@ -0,0 +1,21 @@ +// +build !windows,!linux + +package main + +import ( + "syscall" +) + +func dropPrivileges(uid, gid int) error { + err := syscall.Setgid(gid) + if err != nil { + return err + } + + err = syscall.Setuid(uid) + if err != nil { + return err + } + + return nil +} diff --git a/webhook.go b/webhook.go index 4686fb6..6807520 100644 --- a/webhook.go +++ b/webhook.go @@ -48,6 +48,8 @@ var ( useXRequestID = flag.Bool("x-request-id", false, "use X-Request-Id header, if present, as request ID") xRequestIDLimit = flag.Int("x-request-id-limit", 0, "truncate X-Request-Id header to limit; default no limit") maxMultipartMem = flag.Int64("max-multipart-mem", 1<<20, "maximum memory in bytes for parsing multipart form data before disk caching") + setGID = flag.Int("setgid", 0, "set group ID after opening listening port; must be used with setuid") + setUID = flag.Int("setuid", 0, "set user ID after opening listening port; must be used with setgid") responseHeaders hook.ResponseHeaders hooksFiles hook.HooksFiles @@ -96,6 +98,11 @@ func main() { os.Exit(0) } + if (*setUID != 0 || *setGID != 0) && (*setUID == 0 || *setGID == 0) { + fmt.Println("Error: setuid and setgid options must be used together") + os.Exit(1) + } + if *debug { *verbose = true } @@ -198,22 +205,32 @@ func main() { r.HandleFunc(hooksURL, hookHandler) + addr := fmt.Sprintf("%s:%d", *ip, *port) + // Create common HTTP server settings svr := &http.Server{ - Addr: fmt.Sprintf("%s:%d", *ip, *port), + Addr: addr, Handler: r, } // Open listener - ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *ip, *port)) + ln, err := net.Listen("tcp", addr) if err != nil { log.Printf("error listening on port: %s", err) return } + if *setUID != 0 { + err := dropPrivileges(*setUID, *setGID) + if err != nil { + log.Printf("error dropping privileges: %s", err) + return + } + } + // Serve HTTP if !*secure { - log.Printf("serving hooks on http://%s:%d%s", *ip, *port, hooksURL) + log.Printf("serving hooks on http://%s%s", addr, hooksURL) log.Print(svr.Serve(ln)) return } @@ -227,7 +244,7 @@ func main() { } svr.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) // disable http/2 - log.Printf("serving hooks on https://%s:%d%s", *ip, *port, hooksURL) + log.Printf("serving hooks on https://%s%s", addr, hooksURL) log.Print(svr.ServeTLS(ln, *cert, *key)) }