From c6a98369a0e544da8a36241426e02052cc6a6598 Mon Sep 17 00:00:00 2001 From: t-katsumura <83070078+t-katsumura@users.noreply.github.com> Date: Mon, 23 May 2022 18:17:29 +0900 Subject: [PATCH] Add description on ipv6 address (#1635) * add docs on ipv6 * add tests for ipv6 address * add tests for ipv6 address * remove Print function for debugging * Updated changelog --- CHANGELOG.md | 1 + docs/docs/configuration/overview.md | 4 +- pkg/http/http_suite_test.go | 39 +- pkg/http/server_test.go | 503 ++++++++++++++++++++++--- pkg/sessions/redis/redis_store_test.go | 2 +- pkg/util/util.go | 4 +- 6 files changed, 486 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 429b00ec..c639091e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ If you are using an architecture specific tag (ex: v7.2.1-arm64) you should move - [#1594](https://github.com/oauth2-proxy/oauth2-proxy/pull/1594) Release ARMv8 docker images (@braunsonm) - [#1649](https://github.com/oauth2-proxy/oauth2-proxy/pull/1649) Return a 400 instead of a 500 when a request contains an invalid redirect target (@niksko) - [#1638](https://github.com/oauth2-proxy/oauth2-proxy/pull/1638) Implement configurable upstream timeout (@jacksgt) +- [#1635](https://github.com/oauth2-proxy/oauth2-proxy/pull/1635) Added description and unit tests for ipv6 address (@t-katsumura) # V7.2.1 diff --git a/docs/docs/configuration/overview.md b/docs/docs/configuration/overview.md index 85d3cad5..00c59ca5 100644 --- a/docs/docs/configuration/overview.md +++ b/docs/docs/configuration/overview.md @@ -119,8 +119,8 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | `--google-service-account-json` | string | the path to the service account json credentials | | | `--htpasswd-file` | string | additionally authenticate against a htpasswd file. Entries must be created with `htpasswd -B` for bcrypt encryption | | | `--htpasswd-user-group` | string \| list | the groups to be set on sessions for htpasswd users | | -| `--http-address` | string | `[http://]:` or `unix://` to listen on for HTTP clients | `"127.0.0.1:4180"` | -| `--https-address` | string | `:` to listen on for HTTPS clients | `":443"` | +| `--http-address` | string | `[http://]:` or `unix://` to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` | +| `--https-address` | string | `[https://]:` to listen on for HTTPS clients. Square brackets are required for ipv6 address, e.g. `https://[::1]:443` | `":443"` | | `--logging-compress` | bool | Should rotated log files be compressed using gzip | false | | `--logging-filename` | string | File to log requests to, empty for `stdout` | `""` (stdout) | | `--logging-local-time` | bool | Use local time in log files and backup filenames instead of UTC | true (local time) | diff --git a/pkg/http/http_suite_test.go b/pkg/http/http_suite_test.go index 62d5a3e6..79aa19a8 100644 --- a/pkg/http/http_suite_test.go +++ b/pkg/http/http_suite_test.go @@ -15,8 +15,9 @@ import ( . "github.com/onsi/gomega" ) -var certData []byte -var certDataSource, keyDataSource options.SecretSource +var ipv4CertData, ipv6CertData []byte +var ipv4CertDataSource, ipv4KeyDataSource options.SecretSource +var ipv6CertDataSource, ipv6KeyDataSource options.SecretSource var client *http.Client func TestHTTPSuite(t *testing.T) { @@ -28,28 +29,46 @@ func TestHTTPSuite(t *testing.T) { } var _ = BeforeSuite(func() { - By("Generating a self-signed cert for TLS tests", func() { - certBytes, keyBytes, err := util.GenerateCert() + By("Generating a ipv4 self-signed cert for TLS tests", func() { + certBytes, keyBytes, err := util.GenerateCert("127.0.0.1") Expect(err).ToNot(HaveOccurred()) - certData = certBytes + ipv4CertData = certBytes certOut := new(bytes.Buffer) Expect(pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})).To(Succeed()) - certDataSource.Value = certOut.Bytes() + ipv4CertDataSource.Value = certOut.Bytes() keyOut := new(bytes.Buffer) Expect(pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})).To(Succeed()) - keyDataSource.Value = keyOut.Bytes() + ipv4KeyDataSource.Value = keyOut.Bytes() + }) + + By("Generating a ipv6 self-signed cert for TLS tests", func() { + certBytes, keyBytes, err := util.GenerateCert("::1") + Expect(err).ToNot(HaveOccurred()) + ipv6CertData = certBytes + + certOut := new(bytes.Buffer) + Expect(pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})).To(Succeed()) + ipv6CertDataSource.Value = certOut.Bytes() + keyOut := new(bytes.Buffer) + Expect(pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})).To(Succeed()) + ipv6KeyDataSource.Value = keyOut.Bytes() }) By("Setting up a http client", func() { - cert, err := tls.X509KeyPair(certDataSource.Value, keyDataSource.Value) + ipv4cert, err := tls.X509KeyPair(ipv4CertDataSource.Value, ipv4KeyDataSource.Value) + Expect(err).ToNot(HaveOccurred()) + ipv6cert, err := tls.X509KeyPair(ipv6CertDataSource.Value, ipv6KeyDataSource.Value) Expect(err).ToNot(HaveOccurred()) - certificate, err := x509.ParseCertificate(cert.Certificate[0]) + ipv4certificate, err := x509.ParseCertificate(ipv4cert.Certificate[0]) + Expect(err).ToNot(HaveOccurred()) + ipv6certificate, err := x509.ParseCertificate(ipv6cert.Certificate[0]) Expect(err).ToNot(HaveOccurred()) certpool := x509.NewCertPool() - certpool.AddCert(certificate) + certpool.AddCert(ipv4certificate) + certpool.AddCert(ipv6certificate) transport := http.DefaultTransport.(*http.Transport).Clone() transport.TLSClientConfig.RootCAs = certpool diff --git a/pkg/http/server_test.go b/pkg/http/server_test.go index 0451fa71..3c48cabe 100644 --- a/pkg/http/server_test.go +++ b/pkg/http/server_test.go @@ -50,7 +50,7 @@ var _ = Describe("Server", func() { Expect(s.tlsListener.Close()).To(Succeed()) } }, - Entry("with a valid http bind address", &newServerTableInput{ + Entry("with an ipv4 valid http bind address", &newServerTableInput{ opts: Opts{ Handler: handler, BindAddress: "127.0.0.1:0", @@ -59,7 +59,7 @@ var _ = Describe("Server", func() { expectHTTPListener: true, expectTLSListener: false, }), - Entry("with a valid https bind address, with no TLS config", &newServerTableInput{ + Entry("with an ipv4 valid https bind address, with no TLS config", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", @@ -68,27 +68,27 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: false, }), - Entry("with a valid https bind address, and valid TLS config", &newServerTableInput{ + Entry("with an ipv4 valid https bind address, and valid TLS config", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: nil, expectHTTPListener: false, expectTLSListener: true, }), - Entry("with a both a valid http and 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{ Handler: handler, BindAddress: "127.0.0.1:0", SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: nil, @@ -113,7 +113,7 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: false, }), - Entry("with an invalid bind address scheme", &newServerTableInput{ + Entry("with an ipv4 invalid bind address scheme", &newServerTableInput{ opts: Opts{ Handler: handler, BindAddress: "invalid://127.0.0.1:0", @@ -122,20 +122,20 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: false, }), - Entry("with an invalid secure bind address scheme", &newServerTableInput{ + Entry("with an ipv4 invalid secure bind address scheme", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "invalid://127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: nil, expectHTTPListener: false, expectTLSListener: true, }), - Entry("with an invalid bind address port", &newServerTableInput{ + Entry("with an ipv4 invalid bind address port", &newServerTableInput{ opts: Opts{ Handler: handler, BindAddress: "127.0.0.1:a", @@ -144,20 +144,20 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: false, }), - Entry("with an invalid secure bind address port", &newServerTableInput{ + Entry("with an ipv4 invalid secure bind address port", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:a", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: errors.New("error setting up TLS listener: listen (127.0.0.1:a) failed: listen tcp: "), expectHTTPListener: false, expectTLSListener: false, }), - Entry("with an invalid TLS key", &newServerTableInput{ + Entry("with an ipv4 invalid TLS key", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", @@ -165,19 +165,19 @@ var _ = Describe("Server", func() { Key: &options.SecretSource{ Value: []byte("invalid"), }, - Cert: &certDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not parse certificate data: tls: failed to find any PEM data in key input"), expectHTTPListener: false, expectTLSListener: false, }), - Entry("with an invalid TLS cert", &newServerTableInput{ + Entry("with an ipv4 invalid TLS cert", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, + Key: &ipv4KeyDataSource, Cert: &options.SecretSource{ Value: []byte("invalid"), }, @@ -187,31 +187,31 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: false, }), - Entry("with no TLS key", &newServerTableInput{ + Entry("with an ipv4 address, with no TLS key", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Cert: &certDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not load key data: no configuration provided"), expectHTTPListener: false, expectTLSListener: false, }), - Entry("with no TLS cert", &newServerTableInput{ + Entry("with an ipv4 address, with no TLS cert", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, + Key: &ipv4KeyDataSource, }, }, expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not load cert data: no configuration provided"), expectHTTPListener: false, expectTLSListener: false, }), - Entry("when the bind address is prefixed with the http scheme", &newServerTableInput{ + Entry("when the ipv4 bind address is prefixed with the http scheme", &newServerTableInput{ opts: Opts{ Handler: handler, BindAddress: "http://127.0.0.1:0", @@ -220,26 +220,26 @@ var _ = Describe("Server", func() { expectHTTPListener: true, expectTLSListener: false, }), - Entry("when the secure bind address is prefixed with the https scheme", &newServerTableInput{ + Entry("when the ipv4 secure bind address is prefixed with the https scheme", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "https://127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, }, }, expectedErr: nil, expectHTTPListener: false, expectTLSListener: true, }), - Entry("with a valid https bind address, and valid TLS config with MinVersion", &newServerTableInput{ + Entry("with an ipv4 valid https bind address, and valid TLS config with MinVersion", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, MinVersion: "TLS1.3", }, }, @@ -247,13 +247,206 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: true, }), - Entry("with a valid https bind address, and invalid TLS config with unknown MinVersion", &newServerTableInput{ + Entry("with an ipv4 valid https bind address, and invalid TLS config with unknown MinVersion", &newServerTableInput{ opts: Opts{ Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, + MinVersion: "TLS1.42", + }, + }, + expectedErr: errors.New("error setting up TLS listener: unknown TLS MinVersion config provided"), + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with an ipv6 valid http bind address", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "[::1]:0", + }, + expectedErr: nil, + expectHTTPListener: true, + expectTLSListener: false, + }), + Entry("with an ipv6 valid https bind address, with no TLS config", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + }, + expectedErr: errors.New("error setting up TLS listener: no TLS config provided"), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 valid https bind address, and valid TLS config", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: nil, + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with a both a ipv6 valid http and ipv6 valid https bind address, and valid TLS config", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "[::1]:0", + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: nil, + expectHTTPListener: true, + expectTLSListener: true, + }), + Entry("with an ipv6 invalid bind address scheme", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "invalid://[::1]:0", + }, + expectedErr: errors.New("error setting up listener: listen (invalid, [::1]:0) failed: listen invalid: unknown network invalid"), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 invalid secure bind address scheme", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "invalid://[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: nil, + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with an ipv6 invalid bind address port", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "[::1]:a", + }, + expectedErr: errors.New("error setting up listener: listen (tcp, [::1]:a) failed: listen tcp: "), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 invalid secure bind address port", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:a", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: errors.New("error setting up TLS listener: listen ([::1]:a) failed: listen tcp: "), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 invalid TLS key", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &options.SecretSource{ + Value: []byte("invalid"), + }, + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not parse certificate data: tls: failed to find any PEM data in key input"), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 invalid TLS cert", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &options.SecretSource{ + Value: []byte("invalid"), + }, + }, + }, + expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not parse certificate data: tls: failed to find any PEM data in certificate input"), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 address, with no TLS key", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not load key data: no configuration provided"), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("with an ipv6 address, with no TLS cert", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + }, + }, + expectedErr: errors.New("error setting up TLS listener: could not load certificate: could not load cert data: no configuration provided"), + expectHTTPListener: false, + expectTLSListener: false, + }), + Entry("when the ipv6 bind address is prefixed with the http scheme", &newServerTableInput{ + opts: Opts{ + Handler: handler, + BindAddress: "http://[::1]:0", + }, + expectedErr: nil, + expectHTTPListener: true, + expectTLSListener: false, + }), + Entry("when the ipv6 secure bind address is prefixed with the https scheme", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "https://[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + }, + }, + expectedErr: nil, + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with an ipv6 valid https bind address, and valid TLS config with MinVersion", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + MinVersion: "TLS1.3", + }, + }, + expectedErr: nil, + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with an ipv6 valid https bind address, and invalid TLS config with unknown MinVersion", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, MinVersion: "TLS1.42", }, }, @@ -277,7 +470,7 @@ var _ = Describe("Server", func() { cancel() }) - Context("with an http server", func() { + Context("with an ipv4 http server", func() { var listenAddr string BeforeEach(func() { @@ -327,7 +520,7 @@ var _ = Describe("Server", func() { }) }) - Context("with an https server", func() { + Context("with an ipv4 https server", func() { var secureListenAddr string BeforeEach(func() { @@ -336,8 +529,8 @@ var _ = Describe("Server", func() { Handler: handler, SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, }, }) Expect(err).ToNot(HaveOccurred()) @@ -392,11 +585,11 @@ var _ = Describe("Server", func() { Expect(resp.TLS.VerifiedChains).Should(HaveLen(1)) Expect(resp.TLS.VerifiedChains[0]).Should(HaveLen(1)) - Expect(resp.TLS.VerifiedChains[0][0].Raw).Should(Equal(certData)) + Expect(resp.TLS.VerifiedChains[0][0].Raw).Should(Equal(ipv4CertData)) }) }) - Context("with both an http and an https server", func() { + Context("with both an ipv4 http and an ipv4 https server", func() { var listenAddr, secureListenAddr string BeforeEach(func() { @@ -406,8 +599,204 @@ var _ = Describe("Server", func() { BindAddress: "127.0.0.1:0", SecureBindAddress: "127.0.0.1:0", TLS: &options.TLS{ - Key: &keyDataSource, - Cert: &certDataSource, + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, + }, + }) + Expect(err).ToNot(HaveOccurred()) + + s, ok := srv.(*server) + Expect(ok).To(BeTrue()) + + listenAddr = fmt.Sprintf("http://%s/", s.listener.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 := client.Get(listenAddr) + Expect(err).ToNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + body, err := ioutil.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 := client.Get(secureListenAddr) + Expect(err).ToNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + body, err := ioutil.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 := client.Get(listenAddr) + Expect(err).ToNot(HaveOccurred()) + _, err = client.Get(secureListenAddr) + Expect(err).ToNot(HaveOccurred()) + + cancel() + + Eventually(func() error { + _, err := client.Get(listenAddr) + return err + }).Should(HaveOccurred()) + Eventually(func() error { + _, err := client.Get(secureListenAddr) + return err + }).Should(HaveOccurred()) + }) + }) + + Context("with an ipv6 http server", func() { + var listenAddr string + + BeforeEach(func() { + var err error + srv, err = NewServer(Opts{ + Handler: handler, + BindAddress: "[::1]:0", + }) + Expect(err).ToNot(HaveOccurred()) + + s, ok := srv.(*server) + Expect(ok).To(BeTrue()) + + listenAddr = fmt.Sprintf("http://%s/", s.listener.Addr().String()) + }) + + It("Starts the server and serves the handler", func() { + go func() { + defer GinkgoRecover() + Expect(srv.Start(ctx)).To(Succeed()) + }() + + resp, err := client.Get(listenAddr) + Expect(err).ToNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + body, err := ioutil.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 := client.Get(listenAddr) + Expect(err).ToNot(HaveOccurred()) + + cancel() + + Eventually(func() error { + _, err := client.Get(listenAddr) + return err + }).Should(HaveOccurred()) + }) + }) + + Context("with an ipv6 https server", func() { + var secureListenAddr string + + BeforeEach(func() { + var err error + srv, err = NewServer(Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, + }, + }) + Expect(err).ToNot(HaveOccurred()) + + s, ok := srv.(*server) + Expect(ok).To(BeTrue()) + + secureListenAddr = fmt.Sprintf("https://%s/", s.tlsListener.Addr().String()) + }) + + It("Starts the server and serves the handler", func() { + go func() { + defer GinkgoRecover() + Expect(srv.Start(ctx)).To(Succeed()) + }() + + resp, err := client.Get(secureListenAddr) + Expect(err).ToNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + body, err := ioutil.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 := client.Get(secureListenAddr) + Expect(err).ToNot(HaveOccurred()) + + cancel() + + Eventually(func() error { + _, err := client.Get(secureListenAddr) + return err + }).Should(HaveOccurred()) + }) + + It("Serves the certificate provided", func() { + go func() { + defer GinkgoRecover() + Expect(srv.Start(ctx)).To(Succeed()) + }() + + resp, err := client.Get(secureListenAddr) + Expect(err).ToNot(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + + Expect(resp.TLS.VerifiedChains).Should(HaveLen(1)) + Expect(resp.TLS.VerifiedChains[0]).Should(HaveLen(1)) + Expect(resp.TLS.VerifiedChains[0][0].Raw).Should(Equal(ipv6CertData)) + }) + }) + + Context("with both an ipv6 http and an ipv6 https server", func() { + var listenAddr, secureListenAddr string + + BeforeEach(func() { + var err error + srv, err = NewServer(Opts{ + Handler: handler, + BindAddress: "[::1]:0", + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv6KeyDataSource, + Cert: &ipv6CertDataSource, }, }) Expect(err).ToNot(HaveOccurred()) @@ -478,11 +867,16 @@ var _ = Describe("Server", func() { DescribeTable("should return the scheme", func(in, expected string) { Expect(getNetworkScheme(in)).To(Equal(expected)) }, - Entry("with no scheme", "127.0.0.1:0", "tcp"), - Entry("with a tcp scheme", "tcp://127.0.0.1:0", "tcp"), - Entry("with a http scheme", "http://192.168.0.1:1", "tcp"), - Entry("with a unix scheme", "unix://172.168.16.2:2", "unix"), - Entry("with a random scheme", "random://10.10.10.10:10", "random"), + Entry("ipv4 address with no scheme", "127.0.0.1:0", "tcp"), + Entry("ipv4 address with a tcp scheme", "tcp://127.0.0.1:0", "tcp"), + Entry("ipv4 address with a http scheme", "http://192.168.0.1:1", "tcp"), + Entry("ipv4 address with a unix scheme", "unix://172.168.16.2:2", "unix"), + Entry("ipv4 address with a random scheme", "random://10.10.10.10:10", "random"), + Entry("ipv6 address with no scheme", "[::1]:0", "tcp"), + Entry("ipv6 address with a tcp scheme", "tcp://[::1]:0", "tcp"), + Entry("ipv6 address with a http scheme", "http://[::ffff:c0a8:1]:1", "tcp"), + Entry("ipv6 address with a unix scheme", "unix://[::ffff:aca8:1002]:2", "unix"), + Entry("ipv6 address with a random scheme", "random://[::ffff:a0a:a0a]:10", "random"), ) }) @@ -490,11 +884,16 @@ var _ = Describe("Server", func() { DescribeTable("should remove the scheme", func(in, expected string) { Expect(getListenAddress(in)).To(Equal(expected)) }, - Entry("with no scheme", "127.0.0.1:0", "127.0.0.1:0"), - Entry("with a tcp scheme", "tcp://127.0.0.1:0", "127.0.0.1:0"), - Entry("with a http scheme", "http://192.168.0.1:1", "192.168.0.1:1"), - Entry("with a unix scheme", "unix://172.168.16.2:2", "172.168.16.2:2"), - Entry("with a random scheme", "random://10.10.10.10:10", "10.10.10.10:10"), + Entry("ipv4 address with no scheme", "127.0.0.1:0", "127.0.0.1:0"), + Entry("ipv4 address with a tcp scheme", "tcp://127.0.0.1:0", "127.0.0.1:0"), + Entry("ipv4 address with a http scheme", "http://192.168.0.1:1", "192.168.0.1:1"), + Entry("ipv4 address with a unix scheme", "unix://172.168.16.2:2", "172.168.16.2:2"), + Entry("ipv4 address with a random scheme", "random://10.10.10.10:10", "10.10.10.10:10"), + Entry("ipv6 address with no scheme", "[::1]:0", "[::1]:0"), + Entry("ipv6 address with a tcp scheme", "tcp://[::1]:0", "[::1]:0"), + Entry("ipv6 address with a http scheme", "http://[::ffff:c0a8:1]:1", "[::ffff:c0a8:1]:1"), + Entry("ipv6 address with a unix scheme", "unix://[::ffff:aca8:1002]:2", "[::ffff:aca8:1002]:2"), + Entry("ipv6 address with a random scheme", "random://[::ffff:a0a:a0a]:10", "[::ffff:a0a:a0a]:10"), ) }) }) diff --git a/pkg/sessions/redis/redis_store_test.go b/pkg/sessions/redis/redis_store_test.go index f5ced7e2..9f0bd0a9 100644 --- a/pkg/sessions/redis/redis_store_test.go +++ b/pkg/sessions/redis/redis_store_test.go @@ -54,7 +54,7 @@ var ( var _ = BeforeSuite(func() { var err error - certBytes, keyBytes, err := util.GenerateCert() + certBytes, keyBytes, err := util.GenerateCert("127.0.0.1") Expect(err).ToNot(HaveOccurred()) certOut := new(bytes.Buffer) Expect(pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})).To(Succeed()) diff --git a/pkg/util/util.go b/pkg/util/util.go index 41c9a09f..d8c1fc06 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -33,7 +33,7 @@ func GetCertPool(paths []string) (*x509.CertPool, error) { } // https://golang.org/src/crypto/tls/generate_cert.go as a function -func GenerateCert() ([]byte, []byte, error) { +func GenerateCert(ipaddr string) ([]byte, []byte, error) { var err error priv, err := rsa.GenerateKey(rand.Reader, 2048) @@ -63,7 +63,7 @@ func GenerateCert() ([]byte, []byte, error) { ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, + IPAddresses: []net.IP{net.ParseIP(ipaddr)}, } certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) return certBytes, keyBytes, err