From 9ae0b325a6d75b163c6f1fefb66ca4817c133438 Mon Sep 17 00:00:00 2001 From: Jan Larwig Date: Thu, 19 Mar 2026 00:08:50 +0800 Subject: [PATCH] feat: add support for setting a unix binding's socket file mode (#3376) fix: linter issues and set default unix socket permissions to 0660 Signed-off-by: Jan Larwig Co-authored-by: Tristan --- CHANGELOG.md | 1 + docs/docs/configuration/alpha_config.md | 4 +- docs/docs/configuration/overview.md | 2 +- pkg/apis/options/server.go | 9 +++++ pkg/proxyhttp/server.go | 52 ++++++++++++++++++++++++- pkg/proxyhttp/server_test.go | 50 ++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e0a4b23..ffd8f6de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - [#3365](https://github.com/oauth2-proxy/oauth2-proxy/pull/3365) fix: filter empty strings from allowed groups (@Br1an67) - [#3338](https://github.com/oauth2-proxy/oauth2-proxy/pull/3338) feat: add --config-test flag for validating configuration (@MayorFaj) - [#3347](https://github.com/oauth2-proxy/oauth2-proxy/pull/3347) feat: add same site option for csrf cookies (@jvnoije) +- [#3376](https://github.com/oauth2-proxy/oauth2-proxy/pull/3376) feat: allow setting unix socket file mode when declaring listener (@Tristan971 / @tuunit) # V7.14.3 diff --git a/docs/docs/configuration/alpha_config.md b/docs/docs/configuration/alpha_config.md index d8cce916..ee1883bb 100644 --- a/docs/docs/configuration/alpha_config.md +++ b/docs/docs/configuration/alpha_config.md @@ -584,8 +584,8 @@ Server represents the configuration for an HTTP(S) server | Field | Type | Description | | ----- | ---- | ----------- | -| `bindAddress` | _string_ | BindAddress is the address on which to serve traffic.
Leave blank or set to "-" to disable. | -| `secureBindAddress` | _string_ | SecureBindAddress is the address on which to serve secure traffic.
Leave blank or set to "-" to disable. | +| `bindAddress` | _string_ | BindAddress is the address on which to serve traffic.
Different types of bind addresses are supported:
* `[http://]:`
* `fd:` (case insensitive)
* `unix://`
Unix sockets are created with default system umask mode, which can be overridden, e.g.: `unix://my-socket,mode=0777`
Square brackets are required for ipv6 address, e.g. `http://[::1]:4180`
Leave blank or set to "-" to disable. | +| `secureBindAddress` | _string_ | SecureBindAddress is the address on which to serve secure traffic.
Secure bind addresses need to respond with valid SSL and use the following format:
* `[https://]:`
Square brackets are required for ipv6 address, e.g. `https://[::1]:4180`
Leave blank or set to "-" to disable. | | `tls` | _[TLS](#tls)_ | TLS contains the information for loading the certificate and key for the
secure traffic and further configuration for the TLS server. | ### TLS diff --git a/docs/docs/configuration/overview.md b/docs/docs/configuration/overview.md index c225228e..37f385c7 100644 --- a/docs/docs/configuration/overview.md +++ b/docs/docs/configuration/overview.md @@ -264,7 +264,7 @@ Provider specific options can be found on their respective subpages. | Flag / Config Field | Type | Description | Default | | ------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -| flag: `--http-address`
toml: `http_address` | string | `[http://]:` or `unix://` or `fd:` (case insensitive) to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` | +| flag: `--http-address`
toml: `http_address` | string | `[http://]:` or `unix://` or `fd:` (case insensitive) to listen on for HTTP clients. Unix sockets are created with default system umask mode, which can be overridden, e.g. `unix://my-socket,mode=0777`. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` | | flag: `--https-address`
toml: `https_address` | string | `[https://]:` to listen on for HTTPS clients. Square brackets are required for ipv6 address, e.g. `https://[::1]:443` | `":443"` | | flag: `--metrics-address`
toml: `metrics_address` | string | the address prometheus metrics will be scraped from | `""` | | flag: `--metrics-secure-address`
toml: `metrics_secure_address` | string | the address prometheus metrics will be scraped from if using HTTPS | `""` | diff --git a/pkg/apis/options/server.go b/pkg/apis/options/server.go index 8fa41af8..830ea09a 100644 --- a/pkg/apis/options/server.go +++ b/pkg/apis/options/server.go @@ -3,10 +3,19 @@ package options // Server represents the configuration for an HTTP(S) server type Server struct { // BindAddress is the address on which to serve traffic. + // Different types of bind addresses are supported: + // * `[http://]:` + // * `fd:` (case insensitive) + // * `unix://` + // Unix sockets are created with default system umask mode, which can be overridden, e.g.: `unix://my-socket,mode=0777` + // Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` // Leave blank or set to "-" to disable. BindAddress string `yaml:"bindAddress,omitempty"` // SecureBindAddress is the address on which to serve secure traffic. + // Secure bind addresses need to respond with valid SSL and use the following format: + // * `[https://]:` + // Square brackets are required for ipv6 address, e.g. `https://[::1]:4180` // Leave blank or set to "-" to disable. SecureBindAddress string `yaml:"secureBindAddress,omitempty"` diff --git a/pkg/proxyhttp/server.go b/pkg/proxyhttp/server.go index a0fc6054..2982e1fc 100644 --- a/pkg/proxyhttp/server.go +++ b/pkg/proxyhttp/server.go @@ -8,6 +8,7 @@ import ( "net" "net/http" "os" + "strconv" "strings" "time" @@ -95,15 +96,62 @@ func (s *server) setupListener(opts Opts) error { networkType := getNetworkScheme(opts.BindAddress) listenAddr := getListenAddress(opts.BindAddress) - listener, err := net.Listen(networkType, listenAddr) + listener, err := func() (net.Listener, error) { + if networkType == "unix" { + return setupUnixSocketListener(networkType, listenAddr) + } + return net.Listen(networkType, listenAddr) + }() + if err != nil { return fmt.Errorf("listen (%s, %s) failed: %w", networkType, listenAddr, err) } - s.listener = listener + s.listener = listener return nil } +func setupUnixSocketListener(networkType string, address string) (net.Listener, error) { + socketOpts := strings.Split(address, ",") + if len(socketOpts) < 2 { + return net.Listen(networkType, address) + } + + socketPath := socketOpts[0] + var socketMode os.FileMode + hasSocketMode := false + + for _, socketOpt := range socketOpts[1:] { + socketOpt := strings.SplitN(socketOpt, "=", 2) + if len(socketOpt) != 2 { + return nil, fmt.Errorf("unix socket option %s expects a value", socketOpt[0]) + } + + if socketOpt[0] == "mode" { + mode, err := strconv.ParseUint(socketOpt[1], 8, 32) + if err != nil { + return nil, fmt.Errorf("unix socket file mode has invalid value %s", socketOpt[1]) + } + socketMode = os.FileMode(mode) + hasSocketMode = true + } + } + + listener, err := net.Listen(networkType, socketPath) + if err != nil { + return nil, err + } + + if hasSocketMode { + err = os.Chmod(socketPath, socketMode) + if err != nil { + return nil, fmt.Errorf("cannot set unix socket file mode on %s: %v", socketPath, err) + } + } + + return listener, nil +} + func parseCipherSuites(names []string) ([]uint16, error) { cipherNameMap := make(map[string]uint16) diff --git a/pkg/proxyhttp/server_test.go b/pkg/proxyhttp/server_test.go index d97dcec2..f6d12436 100644 --- a/pkg/proxyhttp/server_test.go +++ b/pkg/proxyhttp/server_test.go @@ -28,6 +28,8 @@ var _ = Describe("Server", func() { expectedErr error expectHTTPListener bool expectTLSListener bool + expectedSocketMode os.FileMode + socketPath string fdAddr string ipv6 bool } @@ -57,6 +59,12 @@ var _ = Describe("Server", func() { s, ok := srv.(*server) Expect(ok).To(BeTrue()) + if in.socketPath != "" { + fileInfo, err := os.Stat(in.socketPath) + Expect(err).ToNot(HaveOccurred()) + Expect(fileInfo.Mode().Perm()).To(Equal(in.expectedSocketMode.Perm())) + } + Expect(s.listener != nil).To(Equal(in.expectHTTPListener)) if in.expectHTTPListener { Expect(s.listener.Close()).To(Succeed()) @@ -648,6 +656,48 @@ var _ = Describe("Server", func() { expectTLSListener: true, ipv6: true, }), + Entry("with a valid unix socket path", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "unix:///tmp/oauth2-proxy.sock", + }, + expectedErr: nil, + expectHTTPListener: true, + expectTLSListener: false, + ipv6: false, + }), + Entry("with a valid unix socket path and a valid socket file mode", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "unix:///tmp/oauth2-proxy.sock,mode=0777", + }, + expectedErr: nil, + expectHTTPListener: true, + expectTLSListener: false, + expectedSocketMode: 0o777, + socketPath: "/tmp/oauth2-proxy.sock", + ipv6: false, + }), + Entry("with a valid unix socket path and a value-less socket file mode argument", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "unix:///tmp/oauth2-proxy.sock,mode", + }, + expectedErr: errors.New("error setting up listener: listen (unix, /tmp/oauth2-proxy.sock,mode) failed: unix socket option mode expects a value"), + expectHTTPListener: false, + expectTLSListener: false, + ipv6: false, + }), + Entry("with a valid unix socket path and an invalid socket file mode value", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "unix:///tmp/oauth2-proxy.sock,mode=-1", + }, + expectedErr: errors.New("error setting up listener: listen (unix, /tmp/oauth2-proxy.sock,mode=-1) failed: unix socket file mode has invalid value -1"), + expectHTTPListener: false, + expectTLSListener: false, + ipv6: false, + }), ) })