Merge pull request #1985 from isodude/systemd-socket
Add support for systemd socket
This commit is contained in:
		
						commit
						4d2b5c30a1
					
				|  | @ -10,6 +10,7 @@ | ||||||
| 
 | 
 | ||||||
| - [#2800](https://github.com/oauth2-proxy/oauth2-proxy/pull/2800) Add some opencontainer labels to docker image (@halkeye) | - [#2800](https://github.com/oauth2-proxy/oauth2-proxy/pull/2800) Add some opencontainer labels to docker image (@halkeye) | ||||||
| - [#2755](https://github.com/oauth2-proxy/oauth2-proxy/pull/2755) feat: add X-Envoy-External-Address as supported header (@bjencks) | - [#2755](https://github.com/oauth2-proxy/oauth2-proxy/pull/2755) feat: add X-Envoy-External-Address as supported header (@bjencks) | ||||||
|  | - [#1985](https://github.com/oauth2-proxy/oauth2-proxy/pull/1985) Add support for systemd socket (@isodude) | ||||||
| 
 | 
 | ||||||
| # V7.7.1 | # V7.7.1 | ||||||
| 
 | 
 | ||||||
|  | @ -58,6 +59,7 @@ | ||||||
| - [#2790](https://github.com/oauth2-proxy/oauth2-proxy/pull/2790) chore(deps): update all golang dependencies (@tuunit) | - [#2790](https://github.com/oauth2-proxy/oauth2-proxy/pull/2790) chore(deps): update all golang dependencies (@tuunit) | ||||||
| - [#2607](https://github.com/oauth2-proxy/oauth2-proxy/pull/2607) fix(csrf): fix possible infinite loop (@Primexz) | - [#2607](https://github.com/oauth2-proxy/oauth2-proxy/pull/2607) fix(csrf): fix possible infinite loop (@Primexz) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| # V7.6.0 | # V7.6.0 | ||||||
| 
 | 
 | ||||||
| ## Release Highlights | ## Release Highlights | ||||||
|  |  | ||||||
|  | @ -219,7 +219,7 @@ Provider specific options can be found on their respective subpages. | ||||||
| 
 | 
 | ||||||
| | Flag / Config Field                                                 | Type           | Description                                                                                                                                                                                                                                                                                                   | Default            | | | Flag / Config Field                                                 | Type           | Description                                                                                                                                                                                                                                                                                                   | Default            | | ||||||
| | ------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | | ------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | ||||||
| | flag: `--http-address`<br/>toml: `http_address`                     | string         | `[http://]<addr>:<port>` or `unix://<path>` 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`<br/>toml: `http_address`                     | string         | `[http://]<addr>:<port>` or `unix://<path>` or `fd:<int>` (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: `--https-address`<br/>toml: `https_address`                   | string         | `[https://]<addr>:<port>` to listen on for HTTPS clients. Square brackets are required for ipv6 address, e.g. `https://[::1]:443`                                                                                                                                                                             | `":443"`           | | | flag: `--https-address`<br/>toml: `https_address`                   | string         | `[https://]<addr>:<port>` to listen on for HTTPS clients. Square brackets are required for ipv6 address, e.g. `https://[::1]:443`                                                                                                                                                                             | `":443"`           | | ||||||
| | flag: `--metrics-address`<br/>toml: `metrics_address`               | string         | the address prometheus metrics will be scraped from                                                                                                                                                                                                                                                           | `""`               | | | flag: `--metrics-address`<br/>toml: `metrics_address`               | string         | the address prometheus metrics will be scraped from                                                                                                                                                                                                                                                           | `""`               | | ||||||
| | flag: `--metrics-secure-address`<br/>toml: `metrics_secure_address` | string         | the address prometheus metrics will be scraped from if using HTTPS                                                                                                                                                                                                                                            | `""`               | | | flag: `--metrics-secure-address`<br/>toml: `metrics_secure_address` | string         | the address prometheus metrics will be scraped from if using HTTPS                                                                                                                                                                                                                                            | `""`               | | ||||||
|  |  | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | --- | ||||||
|  | id: systemd_socket | ||||||
|  | title: Systemd Socket Activation | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Pass an existing listener created by systemd.socket to oauth2-proxy. | ||||||
|  | 
 | ||||||
|  | To do this create a socket: | ||||||
|  | 
 | ||||||
|  | oauth2-proxy.socket | ||||||
|  | ``` | ||||||
|  | [Socket] | ||||||
|  | ListenStream=%t/oauth2.sock | ||||||
|  | SocketGroup=www-data | ||||||
|  | SocketMode=0660 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Now it's possible to call this socket from e.g. nginx: | ||||||
|  | ``` | ||||||
|  | server { | ||||||
|  |     location /oauth2/ { | ||||||
|  |       proxy_pass http://unix:/run/oauth2-proxy/oauth2.sock; | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The oauth2-proxy should have `--http-address=fd:3` as a parameter. | ||||||
|  | Here fd is case insensitive and means file descriptor. The number 3 refers to the first non-stdin/stdout/stderr file descriptor, | ||||||
|  | systemd-socket-activate (which is what systemd.socket uses), listens to what it is told and passes | ||||||
|  | the listener it created onto the process, starting with file descriptor 3. | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | ./oauth2-proxy \ | ||||||
|  |     --http-address="fd:3" \ | ||||||
|  |     --email-domain="yourcompany.com"  \ | ||||||
|  |     --upstream=http://127.0.0.1:8080/ \ | ||||||
|  |     --cookie-secret=... \ | ||||||
|  |     --cookie-secure=true \ | ||||||
|  |     --provider=... \ | ||||||
|  |     --client-id=... \ | ||||||
|  |     --client-secret=... | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Currently TLS is not supported (but it's doable). | ||||||
|  | @ -29,3 +29,4 @@ title: Installation | ||||||
| 2.  [Select a Provider and Register an OAuth Application with a Provider](configuration/providers/index.md) | 2.  [Select a Provider and Register an OAuth Application with a Provider](configuration/providers/index.md) | ||||||
| 3.  [Configure OAuth2 Proxy using config file, command line options, or environment variables](configuration/overview.md) | 3.  [Configure OAuth2 Proxy using config file, command line options, or environment variables](configuration/overview.md) | ||||||
| 4.  [Configure SSL or Deploy behind an SSL endpoint](configuration/tls.md) (example provided for Nginx) | 4.  [Configure SSL or Deploy behind an SSL endpoint](configuration/tls.md) (example provided for Nginx) | ||||||
|  | 5.  [Configure OAuth2 Proxy using systemd.socket](configuration/systemd_socket.md) (example provided for Nginx/Systemd) | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							|  | @ -11,6 +11,7 @@ require ( | ||||||
| 	github.com/bitly/go-simplejson v0.5.1 | 	github.com/bitly/go-simplejson v0.5.1 | ||||||
| 	github.com/bsm/redislock v0.9.4 | 	github.com/bsm/redislock v0.9.4 | ||||||
| 	github.com/coreos/go-oidc/v3 v3.11.0 | 	github.com/coreos/go-oidc/v3 v3.11.0 | ||||||
|  | 	github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf | ||||||
| 	github.com/fsnotify/fsnotify v1.7.0 | 	github.com/fsnotify/fsnotify v1.7.0 | ||||||
| 	github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 | 	github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 | ||||||
| 	github.com/go-jose/go-jose/v3 v3.0.1 | 	github.com/go-jose/go-jose/v3 v3.0.1 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										2
									
								
								go.sum
								
								
								
								
							|  | @ -42,6 +42,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk | ||||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||||
| github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= | github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= | ||||||
| github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= | github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= | ||||||
|  | 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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||||
|  |  | ||||||
|  | @ -606,7 +606,9 @@ func (p *OAuthProxy) isAPIPath(req *http.Request) bool { | ||||||
| 
 | 
 | ||||||
| // isTrustedIP is used to check if a request comes from a trusted client IP address.
 | // isTrustedIP is used to check if a request comes from a trusted client IP address.
 | ||||||
| func (p *OAuthProxy) isTrustedIP(req *http.Request) bool { | func (p *OAuthProxy) isTrustedIP(req *http.Request) bool { | ||||||
| 	if p.trustedIPs == nil { | 	// RemoteAddr @ means unix socket
 | ||||||
|  | 	// https://github.com/golang/go/blob/0fa53e41f122b1661d0678a6d36d71b7b5ad031d/src/syscall/syscall_linux.go#L506-L511
 | ||||||
|  | 	if p.trustedIPs == nil && req.RemoteAddr != "@" { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -470,7 +470,7 @@ func legacyServerFlagset() *pflag.FlagSet { | ||||||
| 	flagSet.String("metrics-secure-address", "", "the address /metrics will be served on for HTTPS clients (e.g. \":9100\")") | 	flagSet.String("metrics-secure-address", "", "the address /metrics will be served on for HTTPS clients (e.g. \":9100\")") | ||||||
| 	flagSet.String("metrics-tls-cert-file", "", "path to certificate file for secure metrics server") | 	flagSet.String("metrics-tls-cert-file", "", "path to certificate file for secure metrics server") | ||||||
| 	flagSet.String("metrics-tls-key-file", "", "path to private key file for secure metrics server") | 	flagSet.String("metrics-tls-key-file", "", "path to private key file for secure metrics server") | ||||||
| 	flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> to listen on for HTTP clients") | 	flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> or fd:<int> (case insensitive) to listen on for HTTP clients") | ||||||
| 	flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients") | 	flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients") | ||||||
| 	flagSet.String("tls-cert-file", "", "path to certificate file") | 	flagSet.String("tls-cert-file", "", "path to certificate file") | ||||||
| 	flagSet.String("tls-key-file", "", "path to private key file") | 	flagSet.String("tls-key-file", "", "path to private key file") | ||||||
|  |  | ||||||
|  | @ -7,15 +7,27 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/coreos/go-systemd/activation" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options/util" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options/util" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" | ||||||
| 	"golang.org/x/sync/errgroup" | 	"golang.org/x/sync/errgroup" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // listenFdsStart corresponds to `SD_LISTEN_FDS_START`.
 | ||||||
|  | // Since the 3 first file descriptors in every linux process is
 | ||||||
|  | // stdin, stdout and stderr. The first usable file descriptor is 3.
 | ||||||
|  | // systemd-socket-activate will always assume that the first socket will be
 | ||||||
|  | // 3 and the rest follow.
 | ||||||
|  | const ( | ||||||
|  | 	listenFdsStart = 3 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // Server represents an HTTP or HTTPS server.
 | // Server represents an HTTP or HTTPS server.
 | ||||||
| type Server interface { | type Server interface { | ||||||
| 	// Start blocks and runs the server.
 | 	// Start blocks and runs the server.
 | ||||||
|  | @ -35,6 +47,9 @@ type Opts struct { | ||||||
| 
 | 
 | ||||||
| 	// TLS is the TLS configuration for the server.
 | 	// TLS is the TLS configuration for the server.
 | ||||||
| 	TLS *options.TLS | 	TLS *options.TLS | ||||||
|  | 
 | ||||||
|  | 	// Let testing infrastructure circumvent parsing file descriptors
 | ||||||
|  | 	fdFiles []*os.File | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewServer creates a new Server from the options given.
 | // NewServer creates a new Server from the options given.
 | ||||||
|  | @ -42,6 +57,11 @@ func NewServer(opts Opts) (Server, error) { | ||||||
| 	s := &server{ | 	s := &server{ | ||||||
| 		handler: opts.Handler, | 		handler: opts.Handler, | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(opts.fdFiles) > 0 { | ||||||
|  | 		s.fdFiles = opts.fdFiles | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if err := s.setupListener(opts); err != nil { | 	if err := s.setupListener(opts); err != nil { | ||||||
| 		return nil, fmt.Errorf("error setting up listener: %v", err) | 		return nil, fmt.Errorf("error setting up listener: %v", err) | ||||||
| 	} | 	} | ||||||
|  | @ -58,6 +78,30 @@ type server struct { | ||||||
| 
 | 
 | ||||||
| 	listener    net.Listener | 	listener    net.Listener | ||||||
| 	tlsListener net.Listener | 	tlsListener net.Listener | ||||||
|  | 
 | ||||||
|  | 	// ensure activation.Files are called once
 | ||||||
|  | 	fdFiles []*os.File | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // convert a string filedescriptor to an actual listener
 | ||||||
|  | func (s *server) fdToListener(bindAddress string) (net.Listener, error) { | ||||||
|  | 	fd, err := strconv.Atoi(bindAddress) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("listen failed: fd with name is not implemented yet") | ||||||
|  | 	} | ||||||
|  | 	fdIndex := fd - listenFdsStart | ||||||
|  | 
 | ||||||
|  | 	if len(s.fdFiles) == 0 { | ||||||
|  | 		s.fdFiles = activation.Files(true) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l := len(s.fdFiles) | ||||||
|  | 
 | ||||||
|  | 	if fdIndex < 0 || fdIndex >= l || l == 0 { | ||||||
|  | 		return nil, fmt.Errorf("listen failed: fd outside of range of available file descriptors") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return net.FileListener(s.fdFiles[fdIndex]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // setupListener sets the server listener if the HTTP server is enabled.
 | // setupListener sets the server listener if the HTTP server is enabled.
 | ||||||
|  | @ -69,6 +113,22 @@ func (s *server) setupListener(opts Opts) error { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Use fd: as a prefix for systemd socket activation, it's generic
 | ||||||
|  | 	// enough and short.
 | ||||||
|  | 	// The most common usage would be --http-address fd:3.
 | ||||||
|  | 	// This causes oauth2-proxy to just assume that the third fd passed
 | ||||||
|  | 	// to the program is indeed a net.Listener and starts using it
 | ||||||
|  | 	// without setting up a new listener.
 | ||||||
|  | 	if strings.HasPrefix(strings.ToLower(opts.BindAddress), "fd:") { | ||||||
|  | 		listenAddr := opts.BindAddress[3:] | ||||||
|  | 		listener, err := s.fdToListener(listenAddr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			err = fmt.Errorf("listen (%s, %s) failed: %v", "file", listenAddr, err) | ||||||
|  | 		} | ||||||
|  | 		s.listener = listener | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	networkType := getNetworkScheme(opts.BindAddress) | 	networkType := getNetworkScheme(opts.BindAddress) | ||||||
| 	listenAddr := getListenAddress(opts.BindAddress) | 	listenAddr := getListenAddress(opts.BindAddress) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
|  | @ -17,16 +18,17 @@ import ( | ||||||
| const hello = "Hello World!" | const hello = "Hello World!" | ||||||
| 
 | 
 | ||||||
| var _ = Describe("Server", func() { | var _ = Describe("Server", func() { | ||||||
|  | 
 | ||||||
| 	handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { | 	handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { | ||||||
| 		rw.Write([]byte(hello)) | 		rw.Write([]byte(hello)) | ||||||
| 	}) | 	}) | ||||||
| 
 |  | ||||||
| 	Context("NewServer", func() { | 	Context("NewServer", func() { | ||||||
| 		type newServerTableInput struct { | 		type newServerTableInput struct { | ||||||
| 			opts               Opts | 			opts               Opts | ||||||
| 			expectedErr        error | 			expectedErr        error | ||||||
| 			expectHTTPListener bool | 			expectHTTPListener bool | ||||||
| 			expectTLSListener  bool | 			expectTLSListener  bool | ||||||
|  | 			fdAddr             string | ||||||
| 			ipv6               bool | 			ipv6               bool | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -34,6 +36,15 @@ var _ = Describe("Server", func() { | ||||||
| 			if in.ipv6 { | 			if in.ipv6 { | ||||||
| 				skipDevContainer() | 				skipDevContainer() | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			if in.fdAddr != "" { | ||||||
|  | 				l, err := net.Listen("tcp", in.fdAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				f, err := l.(*net.TCPListener).File() | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				in.opts.fdFiles = []*os.File{f} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			srv, err := NewServer(in.opts) | 			srv, err := NewServer(in.opts) | ||||||
| 			if in.expectedErr != nil { | 			if in.expectedErr != nil { | ||||||
| 				Expect(err).To(MatchError(ContainSubstring(in.expectedErr.Error()))) | 				Expect(err).To(MatchError(ContainSubstring(in.expectedErr.Error()))) | ||||||
|  | @ -55,6 +66,46 @@ var _ = Describe("Server", func() { | ||||||
| 				Expect(s.tlsListener.Close()).To(Succeed()) | 				Expect(s.tlsListener.Close()).To(Succeed()) | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  | 			Entry("with a valid non-lowercase fd IPv4 bind address", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "Fd:3", | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        nil, | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  false, | ||||||
|  | 				fdAddr:             "127.0.0.1:0", | ||||||
|  | 			}), | ||||||
|  | 			Entry("with a valid fd IPv4 bind address", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "fd:3", | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        nil, | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  false, | ||||||
|  | 				fdAddr:             "127.0.0.1:0", | ||||||
|  | 			}), | ||||||
|  | 			Entry("with a invalid fd named bind address", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "fd:hello", | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        fmt.Errorf("error setting up listener: listen (file, %s) failed: listen failed: fd with name is not implemented yet", "hello"), | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  false, | ||||||
|  | 				fdAddr:             "127.0.0.1:0", | ||||||
|  | 			}), | ||||||
|  | 			Entry("with a invalid fd IPv4 bind address", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "fd:4", | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        fmt.Errorf("error setting up listener: listen (file, %d) failed: listen failed: fd outside of range of available file descriptors", 4), | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  false, | ||||||
|  | 				fdAddr:             "127.0.0.1:0", | ||||||
|  | 			}), | ||||||
| 			Entry("with an ipv4 valid http bind address", &newServerTableInput{ | 			Entry("with an ipv4 valid http bind address", &newServerTableInput{ | ||||||
| 				opts: Opts{ | 				opts: Opts{ | ||||||
| 					Handler:     handler, | 					Handler:     handler, | ||||||
|  | @ -86,6 +137,21 @@ var _ = Describe("Server", func() { | ||||||
| 				expectHTTPListener: false, | 				expectHTTPListener: false, | ||||||
| 				expectTLSListener:  true, | 				expectTLSListener:  true, | ||||||
| 			}), | 			}), | ||||||
|  | 			Entry("with a both a fd valid http and ipv4 valid https bind address, and valid TLS config", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:           handler, | ||||||
|  | 					BindAddress:       "fd:3", | ||||||
|  | 					SecureBindAddress: "127.0.0.1:0", | ||||||
|  | 					TLS: &options.TLS{ | ||||||
|  | 						Key:  &ipv4KeyDataSource, | ||||||
|  | 						Cert: &ipv4CertDataSource, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        nil, | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  true, | ||||||
|  | 				fdAddr:             "127.0.0.1:0", | ||||||
|  | 			}), | ||||||
| 			Entry("with a both a ipv4 valid http and ipv4 valid https bind address, and valid TLS config", &newServerTableInput{ | 			Entry("with a both a ipv4 valid http and ipv4 valid https bind address, and valid TLS config", &newServerTableInput{ | ||||||
| 				opts: Opts{ | 				opts: Opts{ | ||||||
| 					Handler:           handler, | 					Handler:           handler, | ||||||
|  | @ -300,6 +366,27 @@ var _ = Describe("Server", func() { | ||||||
| 				expectHTTPListener: false, | 				expectHTTPListener: false, | ||||||
| 				expectTLSListener:  true, | 				expectTLSListener:  true, | ||||||
| 			}), | 			}), | ||||||
|  | 			Entry("with valid fd IPv6 bind address", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "fd:3", | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        nil, | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  false, | ||||||
|  | 				fdAddr:             "[::1]:0", | ||||||
|  | 				ipv6:               true, | ||||||
|  | 			}), | ||||||
|  | 			Entry("with a invalid fd IPv6 bind address", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "fd:4", | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        fmt.Errorf("error setting up listener: listen (file, %d) failed: listen failed: fd outside of range of available file descriptors", 4), | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  false, | ||||||
|  | 				fdAddr:             "[::1]:0", | ||||||
|  | 			}), | ||||||
| 			Entry("with an ipv6 valid http bind address", &newServerTableInput{ | 			Entry("with an ipv6 valid http bind address", &newServerTableInput{ | ||||||
| 				opts: Opts{ | 				opts: Opts{ | ||||||
| 					Handler:     handler, | 					Handler:     handler, | ||||||
|  | @ -334,6 +421,22 @@ var _ = Describe("Server", func() { | ||||||
| 				expectTLSListener:  true, | 				expectTLSListener:  true, | ||||||
| 				ipv6:               true, | 				ipv6:               true, | ||||||
| 			}), | 			}), | ||||||
|  | 			Entry("with a both a fd valid http and ipv6 valid https bind address, and valid TLS config", &newServerTableInput{ | ||||||
|  | 				opts: Opts{ | ||||||
|  | 					Handler:           handler, | ||||||
|  | 					BindAddress:       "fd:3", | ||||||
|  | 					SecureBindAddress: "[::1]:0", | ||||||
|  | 					TLS: &options.TLS{ | ||||||
|  | 						Key:  &ipv6KeyDataSource, | ||||||
|  | 						Cert: &ipv6CertDataSource, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				expectedErr:        nil, | ||||||
|  | 				expectHTTPListener: true, | ||||||
|  | 				expectTLSListener:  true, | ||||||
|  | 				fdAddr:             "[::1]:0", | ||||||
|  | 				ipv6:               true, | ||||||
|  | 			}), | ||||||
| 			Entry("with a both a ipv6 valid http and ipv6 valid https bind address, and valid TLS config", &newServerTableInput{ | 			Entry("with a both a ipv6 valid http and ipv6 valid https bind address, and valid TLS config", &newServerTableInput{ | ||||||
| 				opts: Opts{ | 				opts: Opts{ | ||||||
| 					Handler:           handler, | 					Handler:           handler, | ||||||
|  | @ -563,6 +666,58 @@ var _ = Describe("Server", func() { | ||||||
| 
 | 
 | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
|  | 		Context("with an fd ipv4 http server", func() { | ||||||
|  | 			var listenAddr string | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				l, err := net.Listen("tcp", "127.0.0.1:0") | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				f, err := l.(*net.TCPListener).File() | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				srv, err = NewServer(Opts{ | ||||||
|  | 					Handler:     handler, | ||||||
|  | 					BindAddress: "fd:3", | ||||||
|  | 					fdFiles:     []*os.File{f}, | ||||||
|  | 				}) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				listenAddr = fmt.Sprintf("http://%s/", l.Addr().String()) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Starts the server and serves the handler", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				resp, err := httpGet(ctx, listenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||||||
|  | 
 | ||||||
|  | 				body, err := io.ReadAll(resp.Body) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(string(body)).To(Equal(hello)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Stops the server when the context is cancelled", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				_, err := httpGet(ctx, listenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				cancel() | ||||||
|  | 
 | ||||||
|  | 				Eventually(func() error { | ||||||
|  | 					_, err := httpGet(ctx, listenAddr) | ||||||
|  | 					return err | ||||||
|  | 				}).Should(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
| 		Context("with an ipv4 http server", func() { | 		Context("with an ipv4 http server", func() { | ||||||
| 			var listenAddr string | 			var listenAddr string | ||||||
| 
 | 
 | ||||||
|  | @ -682,6 +837,88 @@ var _ = Describe("Server", func() { | ||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
|  | 		Context("with a fd ipv4 http and an ipv4 https server", func() { | ||||||
|  | 			var listenAddr, secureListenAddr string | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				l, err := net.Listen("tcp", "127.0.0.1:0") | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				f, err := l.(*net.TCPListener).File() | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				srv, err = NewServer(Opts{ | ||||||
|  | 					Handler:           handler, | ||||||
|  | 					BindAddress:       "fd:3", | ||||||
|  | 					fdFiles:           []*os.File{f}, | ||||||
|  | 					SecureBindAddress: "127.0.0.1:0", | ||||||
|  | 					TLS: &options.TLS{ | ||||||
|  | 						Key:  &ipv4KeyDataSource, | ||||||
|  | 						Cert: &ipv4CertDataSource, | ||||||
|  | 					}, | ||||||
|  | 				}) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				s, ok := srv.(*server) | ||||||
|  | 				Expect(ok).To(BeTrue()) | ||||||
|  | 
 | ||||||
|  | 				listenAddr = fmt.Sprintf("http://%s/", l.Addr().String()) | ||||||
|  | 				secureListenAddr = fmt.Sprintf("https://%s/", s.tlsListener.Addr().String()) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Starts the server and serves the handler on http", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				resp, err := httpGet(ctx, listenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||||||
|  | 
 | ||||||
|  | 				body, err := io.ReadAll(resp.Body) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(string(body)).To(Equal(hello)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Starts the server and serves the handler on https", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				resp, err := httpGet(ctx, secureListenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||||||
|  | 
 | ||||||
|  | 				body, err := io.ReadAll(resp.Body) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(string(body)).To(Equal(hello)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Stops both servers when the context is cancelled", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				_, err := httpGet(ctx, listenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				_, err = httpGet(ctx, secureListenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				cancel() | ||||||
|  | 
 | ||||||
|  | 				Eventually(func() error { | ||||||
|  | 					_, err := httpGet(ctx, listenAddr) | ||||||
|  | 					return err | ||||||
|  | 				}).Should(HaveOccurred()) | ||||||
|  | 				Eventually(func() error { | ||||||
|  | 					_, err := httpGet(ctx, secureListenAddr) | ||||||
|  | 					return err | ||||||
|  | 				}).Should(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
| 		Context("with both an ipv4 http and an ipv4 https server", func() { | 		Context("with both an ipv4 http and an ipv4 https server", func() { | ||||||
| 			var listenAddr, secureListenAddr string | 			var listenAddr, secureListenAddr string | ||||||
| 
 | 
 | ||||||
|  | @ -880,6 +1117,89 @@ var _ = Describe("Server", func() { | ||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
|  | 		Context("with an fd ipv6 http and an ipv6 https server", func() { | ||||||
|  | 			var listenAddr, secureListenAddr string | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				skipDevContainer() | ||||||
|  | 				l, err := net.Listen("tcp", "[::1]:0") | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				f, err := l.(*net.TCPListener).File() | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				srv, err = NewServer(Opts{ | ||||||
|  | 					Handler:           handler, | ||||||
|  | 					BindAddress:       "fd:3", | ||||||
|  | 					fdFiles:           []*os.File{f}, | ||||||
|  | 					SecureBindAddress: "[::1]:0", | ||||||
|  | 					TLS: &options.TLS{ | ||||||
|  | 						Key:  &ipv6KeyDataSource, | ||||||
|  | 						Cert: &ipv6CertDataSource, | ||||||
|  | 					}, | ||||||
|  | 				}) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				s, ok := srv.(*server) | ||||||
|  | 				Expect(ok).To(BeTrue()) | ||||||
|  | 
 | ||||||
|  | 				listenAddr = fmt.Sprintf("http://%s/", l.Addr().String()) | ||||||
|  | 				secureListenAddr = fmt.Sprintf("https://%s/", s.tlsListener.Addr().String()) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Starts the server and serves the handler on http", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				resp, err := httpGet(ctx, listenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||||||
|  | 
 | ||||||
|  | 				body, err := io.ReadAll(resp.Body) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(string(body)).To(Equal(hello)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Starts the server and serves the handler on https", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				resp, err := httpGet(ctx, secureListenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(resp.StatusCode).To(Equal(http.StatusOK)) | ||||||
|  | 
 | ||||||
|  | 				body, err := io.ReadAll(resp.Body) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(string(body)).To(Equal(hello)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("Stops both servers when the context is cancelled", func() { | ||||||
|  | 				go func() { | ||||||
|  | 					defer GinkgoRecover() | ||||||
|  | 					Expect(srv.Start(ctx)).To(Succeed()) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				_, err := httpGet(ctx, listenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				_, err = httpGet(ctx, secureListenAddr) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				cancel() | ||||||
|  | 
 | ||||||
|  | 				Eventually(func() error { | ||||||
|  | 					_, err := httpGet(ctx, listenAddr) | ||||||
|  | 					return err | ||||||
|  | 				}).Should(HaveOccurred()) | ||||||
|  | 				Eventually(func() error { | ||||||
|  | 					_, err := httpGet(ctx, secureListenAddr) | ||||||
|  | 					return err | ||||||
|  | 				}).Should(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
| 		Context("with both an ipv6 http and an ipv6 https server", func() { | 		Context("with both an ipv6 http and an ipv6 https server", func() { | ||||||
| 			var listenAddr, secureListenAddr string | 			var listenAddr, secureListenAddr string | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue