diff --git a/CHANGELOG.md b/CHANGELOG.md index 85fe35c4..94aeaae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ N/A - [#1669](https://github.com/oauth2-proxy/oauth2-proxy/pull/1699) Fix method deprecated error in lint (@t-katsumura) - [#1709](https://github.com/oauth2-proxy/oauth2-proxy/pull/1709) Show an alert message when basic auth credentials are invalid (@aiciobanu) +- [#1723](https://github.com/oauth2-proxy/oauth2-proxy/pull/1723) Added ability to specify allowed TLS cipher suites. (@crbednarz) - [#1720](https://github.com/oauth2-proxy/oauth2-proxy/pull/1720) Extract roles from authToken, to allow using allowed roles with Keycloak. diff --git a/docs/docs/configuration/alpha_config.md b/docs/docs/configuration/alpha_config.md index f0e9d182..f3fe9634 100644 --- a/docs/docs/configuration/alpha_config.md +++ b/docs/docs/configuration/alpha_config.md @@ -478,6 +478,7 @@ as well as an optional minimal TLS version that is acceptable. | `Key` | _[SecretSource](#secretsource)_ | Key is the TLS key data to use.
Typically this will come from a file. | | `Cert` | _[SecretSource](#secretsource)_ | Cert is the TLS certificate data to use.
Typically this will come from a file. | | `MinVersion` | _string_ | MinVersion is the minimal TLS version that is acceptable.
E.g. Set to "TLS1.3" to select TLS version 1.3 | +| `CipherSuites` | _[]string_ | CipherSuites is a list of TLS cipher suites that are allowed.
E.g.:
- TLS_RSA_WITH_RC4_128_SHA
- TLS_RSA_WITH_AES_256_GCM_SHA384
If not specified, the default Go safe cipher list is used.
List of valid cipher suites can be found in the [crypto/tls documentation](https://pkg.go.dev/crypto/tls#pkg-constants). | ### URLParameterRule diff --git a/docs/docs/configuration/overview.md b/docs/docs/configuration/overview.md index 49303228..524e3414 100644 --- a/docs/docs/configuration/overview.md +++ b/docs/docs/configuration/overview.md @@ -196,6 +196,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | `--standard-logging` | bool | Log standard runtime information | true | | `--standard-logging-format` | string | Template for standard log lines | see [Logging Configuration](#logging-configuration) | | `--tls-cert-file` | string | path to certificate file | | +| `--tls-cipher-suite` | string \| list | Restricts TLS cipher suites used by server to those listed (e.g. TLS_RSA_WITH_RC4_128_SHA) (may be given multiple times). If not specified, the default Go safe cipher list is used. List of valid cipher suites can be found in the [crypto/tls documentation](https://pkg.go.dev/crypto/tls#pkg-constants). | | | `--tls-key-file` | string | path to private key file | | | `--tls-min-version` | string | minimum TLS version that is acceptable, either `"TLS1.2"` or `"TLS1.3"` | `"TLS1.2"` | | `--upstream` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://` for static response. Routing is based on the path | | diff --git a/docs/docs/configuration/tls.md b/docs/docs/configuration/tls.md index 1efdc5fa..21aa6f3b 100644 --- a/docs/docs/configuration/tls.md +++ b/docs/docs/configuration/tls.md @@ -32,8 +32,9 @@ There are two recommended configurations: The defaults set `TLS1.2` as the minimal version. Regardless of the minimum version configured, `TLS1.3` is currently always used as the maximal version. - The server side cipher suites are the defaults from [`crypto/tls`](https://pkg.go.dev/crypto/tls#CipherSuites) of - the currently used `go` version for building `oauth2-proxy`. + TLS server side cipher suites can be specified with `--tls-cipher-suite=TLS_RSA_WITH_RC4_128_SHA`. + If not specified, the defaults from [`crypto/tls`](https://pkg.go.dev/crypto/tls#CipherSuites) of the currently used `go` version for building `oauth2-proxy` will be used. + A complete list of valid TLS cipher suite names can be found in [`crypto/tls`](https://pkg.go.dev/crypto/tls#pkg-constants). ### Terminate TLS at Reverse Proxy, e.g. Nginx diff --git a/pkg/apis/options/legacy_options.go b/pkg/apis/options/legacy_options.go index 16297e00..b3e1f2b5 100644 --- a/pkg/apis/options/legacy_options.go +++ b/pkg/apis/options/legacy_options.go @@ -447,15 +447,16 @@ func getXAuthRequestAccessTokenHeader() Header { } type LegacyServer struct { - MetricsAddress string `flag:"metrics-address" cfg:"metrics_address"` - MetricsSecureAddress string `flag:"metrics-secure-address" cfg:"metrics_secure_address"` - MetricsTLSCertFile string `flag:"metrics-tls-cert-file" cfg:"metrics_tls_cert_file"` - MetricsTLSKeyFile string `flag:"metrics-tls-key-file" cfg:"metrics_tls_key_file"` - HTTPAddress string `flag:"http-address" cfg:"http_address"` - HTTPSAddress string `flag:"https-address" cfg:"https_address"` - TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file"` - TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"` - TLSMinVersion string `flag:"tls-min-version" cfg:"tls_min_version"` + MetricsAddress string `flag:"metrics-address" cfg:"metrics_address"` + MetricsSecureAddress string `flag:"metrics-secure-address" cfg:"metrics_secure_address"` + MetricsTLSCertFile string `flag:"metrics-tls-cert-file" cfg:"metrics_tls_cert_file"` + MetricsTLSKeyFile string `flag:"metrics-tls-key-file" cfg:"metrics_tls_key_file"` + HTTPAddress string `flag:"http-address" cfg:"http_address"` + HTTPSAddress string `flag:"https-address" cfg:"https_address"` + TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file"` + TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"` + TLSMinVersion string `flag:"tls-min-version" cfg:"tls_min_version"` + TLSCipherSuites []string `flag:"tls-cipher-suite" cfg:"tls_cipher_suites"` } func legacyServerFlagset() *pflag.FlagSet { @@ -470,6 +471,7 @@ func legacyServerFlagset() *pflag.FlagSet { flagSet.String("tls-cert-file", "", "path to certificate file") flagSet.String("tls-key-file", "", "path to private key file") flagSet.String("tls-min-version", "", "minimal TLS version for HTTPS clients (either \"TLS1.2\" or \"TLS1.3\")") + flagSet.StringSlice("tls-cipher-suite", []string{}, "restricts TLS cipher suites to those listed (e.g. TLS_RSA_WITH_RC4_128_SHA) (may be given multiple times)") return flagSet } @@ -600,6 +602,9 @@ func (l LegacyServer) convert() (Server, Server) { }, MinVersion: l.TLSMinVersion, } + if len(l.TLSCipherSuites) != 0 { + appServer.TLS.CipherSuites = l.TLSCipherSuites + } // Preserve backwards compatibility, only run one server appServer.BindAddress = "" } else { diff --git a/pkg/apis/options/legacy_options_test.go b/pkg/apis/options/legacy_options_test.go index da5545d6..03f874ca 100644 --- a/pkg/apis/options/legacy_options_test.go +++ b/pkg/apis/options/legacy_options_test.go @@ -804,6 +804,7 @@ var _ = Describe("Legacy Options", func() { keyPath = "tls.key" minVersion = "TLS1.3" ) + cipherSuites := []string{"TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_256_GCM_SHA384"} var tlsConfig = &TLS{ Cert: &SecretSource{ @@ -820,6 +821,15 @@ var _ = Describe("Legacy Options", func() { MinVersion: minVersion, } + var tlsConfigCipherSuites = &TLS{ + Cert: tlsConfig.Cert, + Key: tlsConfig.Key, + CipherSuites: []string{ + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + }, + } + DescribeTable("should convert to app and metrics servers", func(in legacyServersTableInput) { appServer, metricsServer := in.legacyServer.convert() @@ -860,6 +870,19 @@ var _ = Describe("Legacy Options", func() { TLS: tlsConfigMinVersion, }, }), + Entry("with TLS options specified with CipherSuites", legacyServersTableInput{ + legacyServer: LegacyServer{ + HTTPAddress: insecureAddr, + HTTPSAddress: secureAddr, + TLSKeyFile: keyPath, + TLSCertFile: crtPath, + TLSCipherSuites: cipherSuites, + }, + expectedAppServer: Server{ + SecureBindAddress: secureAddr, + TLS: tlsConfigCipherSuites, + }, + }), Entry("with metrics HTTP and HTTPS addresses", legacyServersTableInput{ legacyServer: LegacyServer{ HTTPAddress: insecureAddr, diff --git a/pkg/apis/options/server.go b/pkg/apis/options/server.go index 704e133e..f423ef2c 100644 --- a/pkg/apis/options/server.go +++ b/pkg/apis/options/server.go @@ -29,4 +29,12 @@ type TLS struct { // MinVersion is the minimal TLS version that is acceptable. // E.g. Set to "TLS1.3" to select TLS version 1.3 MinVersion string + + // CipherSuites is a list of TLS cipher suites that are allowed. + // E.g.: + // - TLS_RSA_WITH_RC4_128_SHA + // - TLS_RSA_WITH_AES_256_GCM_SHA384 + // If not specified, the default Go safe cipher list is used. + // List of valid cipher suites can be found in the [crypto/tls documentation](https://pkg.go.dev/crypto/tls#pkg-constants). + CipherSuites []string } diff --git a/pkg/http/server.go b/pkg/http/server.go index b44d5b10..9daadf39 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -81,6 +81,27 @@ func (s *server) setupListener(opts Opts) error { return nil } +func parseCipherSuites(names []string) ([]uint16, error) { + cipherNameMap := make(map[string]uint16) + + for _, cipherSuite := range tls.CipherSuites() { + cipherNameMap[cipherSuite.Name] = cipherSuite.ID + } + for _, cipherSuite := range tls.InsecureCipherSuites() { + cipherNameMap[cipherSuite.Name] = cipherSuite.ID + } + + result := make([]uint16, len(names)) + for i, name := range names { + id, present := cipherNameMap[name] + if !present { + return nil, fmt.Errorf("unknown TLS cipher suite name specified %q", name) + } + result[i] = id + } + return result, nil +} + // setupTLSListener sets the server TLS listener if the HTTPS server is enabled. // The HTTPS server can be disabled by setting the SecureBindAddress to "-" or by // leaving it empty. @@ -104,6 +125,14 @@ func (s *server) setupTLSListener(opts Opts) error { } config.Certificates = []tls.Certificate{cert} + if len(opts.TLS.CipherSuites) > 0 { + cipherSuites, err := parseCipherSuites(opts.TLS.CipherSuites) + if err != nil { + return fmt.Errorf("could not parse cipher suites: %v", err) + } + config.CipherSuites = cipherSuites + } + if len(opts.TLS.MinVersion) > 0 { switch opts.TLS.MinVersion { case "TLS1.2": diff --git a/pkg/http/server_test.go b/pkg/http/server_test.go index 3c48cabe..6944c491 100644 --- a/pkg/http/server_test.go +++ b/pkg/http/server_test.go @@ -261,6 +261,40 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: true, }), + Entry("with an ipv4 valid https bind address, and valid TLS config with CipherSuites", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "127.0.0.1:0", + TLS: &options.TLS{ + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, + CipherSuites: []string{ + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + }, + }, + }, + expectedErr: nil, + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with an ipv4 valid https bind address, and invalid TLS config with unknown CipherSuites", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "127.0.0.1:0", + TLS: &options.TLS{ + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, + CipherSuites: []string{ + "TLS_RSA_WITH_RC4_64_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + }, + }, + }, + expectedErr: errors.New("error setting up TLS listener: could not parse cipher suites: unknown TLS cipher suite name specified \"TLS_RSA_WITH_RC4_64_SHA\""), + expectHTTPListener: false, + expectTLSListener: true, + }), Entry("with an ipv6 valid http bind address", &newServerTableInput{ opts: Opts{ Handler: handler, @@ -454,6 +488,40 @@ var _ = Describe("Server", func() { expectHTTPListener: false, expectTLSListener: true, }), + Entry("with an ipv6 valid https bind address, and valid TLS config with CipherSuites", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, + CipherSuites: []string{ + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + }, + }, + }, + expectedErr: nil, + expectHTTPListener: false, + expectTLSListener: true, + }), + Entry("with an ipv6 valid https bind address, and invalid TLS config with unknown CipherSuites", &newServerTableInput{ + opts: Opts{ + Handler: handler, + SecureBindAddress: "[::1]:0", + TLS: &options.TLS{ + Key: &ipv4KeyDataSource, + Cert: &ipv4CertDataSource, + CipherSuites: []string{ + "TLS_RSA_WITH_RC4_64_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + }, + }, + }, + expectedErr: errors.New("error setting up TLS listener: could not parse cipher suites: unknown TLS cipher suite name specified \"TLS_RSA_WITH_RC4_64_SHA\""), + expectHTTPListener: false, + expectTLSListener: true, + }), ) })