From dbd297670d1c282f274d0cbadcde88111f143417 Mon Sep 17 00:00:00 2001 From: Mart Aarma Date: Mon, 29 Nov 2021 09:22:44 +0200 Subject: [PATCH] feat: backchannel logout request client tls configuration --- consent/strategy_default.go | 28 ++++++++++----- driver/config/tls.go | 37 +++++++++++++++++++ driver/config/tls_test.go | 71 +++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 8 +++++ spec/config.json | 57 +++++++++++++++++++++++++++++ 6 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 driver/config/tls_test.go diff --git a/consent/strategy_default.go b/consent/strategy_default.go index 6f67951fc05..a803f93e8a3 100644 --- a/consent/strategy_default.go +++ b/consent/strategy_default.go @@ -22,6 +22,7 @@ package consent import ( "context" + "crypto/tls" "net/http" "net/url" "strconv" @@ -67,17 +68,20 @@ const ( ) type DefaultStrategy struct { - c *config.Provider - r InternalRegistry + c *config.Provider + r InternalRegistry + tlsClientConfig *tls.Config } -func NewStrategy( - r InternalRegistry, - c *config.Provider, -) *DefaultStrategy { +func NewStrategy(r InternalRegistry, c *config.Provider) *DefaultStrategy { + clientConfig, err := c.TLSClientConfig() + if err != nil { + r.Logger().WithError(err).Fatalf("Unable to setup backchannel logout request client TLS configuration.") + } return &DefaultStrategy{ - c: c, - r: r, + c: c, + r: r, + tlsClientConfig: clientConfig, } } @@ -697,7 +701,13 @@ func (s *DefaultStrategy) executeBackChannelLogout(ctx context.Context, r *http. } var wg sync.WaitGroup - hc := httpx.NewResilientClient() + hc := httpx.NewResilientClient( + httpx.ResilientClientWithClient(&http.Client{ + Timeout: time.Minute, + Transport: &http.Transport{ + TLSClientConfig: s.tlsClientConfig, + }, + })) wg.Add(len(tasks)) var execute = func(t task) { diff --git a/driver/config/tls.go b/driver/config/tls.go index 5fe5ef21b81..d5f618670f5 100644 --- a/driver/config/tls.go +++ b/driver/config/tls.go @@ -2,6 +2,10 @@ package config import ( "crypto/tls" + "strings" + + "github.com/hashicorp/go-secure-stdlib/tlsutil" + "github.com/pkg/errors" "github.com/ory/x/tlsx" ) @@ -19,6 +23,10 @@ const ( KeyTLSKeyString = "serve." + KeySuffixTLSKeyString KeyTLSCertPath = "serve." + KeySuffixTLSCertPath KeyTLSKeyPath = "serve." + KeySuffixTLSKeyPath + + KeyClientTLSCipherSuites = "client.tls.cipher_suites" + KeyClientTLSMinVer = "client.tls.min_version" + KeyClientTLSMaxVer = "client.tls.max_version" ) type TLSConfig interface { @@ -47,6 +55,35 @@ func (p *Provider) TLS(iface ServeInterface) TLSConfig { } } +func (p *Provider) TLSClientConfig() (*tls.Config, error) { + tlsClientConfig := new(tls.Config) + + if p.p.Exists(KeyClientTLSCipherSuites) { + keyCipherSuites := p.p.Strings(KeyClientTLSCipherSuites) + cipherSuites, err := tlsutil.ParseCiphers(strings.Join(keyCipherSuites[:], ",")) + if err != nil { + return nil, errors.WithMessage(err, "Unable to setup client TLS configuration") + } + tlsClientConfig.CipherSuites = cipherSuites + } + + keyMinVer := p.p.StringF(KeyClientTLSMinVer, "tls10") + if tlsMinVer, found := tlsutil.TLSLookup[keyMinVer]; !found { + return nil, errors.Errorf("Unable to setup client TLS configuration. Invalid minimum TLS version: %s", keyMinVer) + } else { + tlsClientConfig.MinVersion = tlsMinVer + } + + keyMaxVer := p.p.StringF(KeyClientTLSMaxVer, "tls13") + if tlsMaxVer, found := tlsutil.TLSLookup[keyMaxVer]; !found { + return nil, errors.Errorf("Unable to setup client TLS configuration. Invalid maximum TLS version: %s", keyMaxVer) + } else { + tlsClientConfig.MaxVersion = tlsMaxVer + } + + return tlsClientConfig, nil +} + type tlsConfig struct { enabled bool allowTerminationFrom []string diff --git a/driver/config/tls_test.go b/driver/config/tls_test.go new file mode 100644 index 00000000000..18ac7e350a0 --- /dev/null +++ b/driver/config/tls_test.go @@ -0,0 +1,71 @@ +package config + +import ( + "crypto/tls" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/ory/x/configx" + "github.com/ory/x/logrusx" +) + +func TestTLSClientConfig_CipherSuite(t *testing.T) { + l := logrusx.New("", "") + c := MustNew(l, configx.WithValue("client.tls.cipher_suites", []string{"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384"})) + + tlsClientConfig, err := c.TLSClientConfig() + assert.NoError(t, err) + cipherSuites := tlsClientConfig.CipherSuites + + assert.Len(t, cipherSuites, 2) + assert.Equal(t, tls.TLS_AES_128_GCM_SHA256, cipherSuites[0]) + assert.Equal(t, tls.TLS_AES_256_GCM_SHA384, cipherSuites[1]) +} + +func TestTLSClientConfig_InvalidCipherSuite(t *testing.T) { + l := logrusx.New("", "") + c := MustNew(l, configx.WithValue("client.tls.cipher_suites", []string{"TLS_AES_128_GCM_SHA256", "TLS_INVALID_CIPHER_SUITE"})) + + _, err := c.TLSClientConfig() + + assert.EqualError(t, err, "Unable to setup client TLS configuration: unsupported cipher \"TLS_INVALID_CIPHER_SUITE\"") +} + +func TestTLSClientConfig_MinVersion(t *testing.T) { + l := logrusx.New("", "") + c := MustNew(l, configx.WithValue("client.tls.min_version", "tls13")) + + tlsClientConfig, err := c.TLSClientConfig() + + assert.NoError(t, err) + assert.Equal(t, uint16(tls.VersionTLS13), tlsClientConfig.MinVersion) +} + +func TestTLSClientConfig_InvalidMinVersion(t *testing.T) { + l := logrusx.New("", "") + c := MustNew(l, configx.WithValue("client.tls.min_version", "tlsx")) + + _, err := c.TLSClientConfig() + + assert.EqualError(t, err, "Unable to setup client TLS configuration. Invalid minimum TLS version: tlsx") +} + +func TestTLSClientConfig_MaxVersion(t *testing.T) { + l := logrusx.New("", "") + c := MustNew(l, configx.WithValue("client.tls.max_version", "tls10")) + + tlsClientConfig, err := c.TLSClientConfig() + + assert.NoError(t, err) + assert.Equal(t, uint16(tls.VersionTLS10), tlsClientConfig.MaxVersion) +} + +func TestTLSClientConfig_InvalidMaxTlsVersion(t *testing.T) { + l := logrusx.New("", "") + c := MustNew(l, configx.WithValue("client.tls.max_version", "tlsx")) + + _, err := c.TLSClientConfig() + + assert.EqualError(t, err, "Unable to setup client TLS configuration. Invalid maximum TLS version: tlsx") +} diff --git a/go.mod b/go.mod index 80cf4f449c7..2ad63a5681a 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/gorilla/sessions v1.2.0 github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 github.com/hashicorp/go-cleanhttp v0.5.2 + github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 github.com/jackc/pgx/v4 v4.13.0 github.com/jmoiron/sqlx v1.3.4 github.com/julienschmidt/httprouter v1.3.0 diff --git a/go.sum b/go.sum index a9b5c45ac74..e0124f3e1d8 100644 --- a/go.sum +++ b/go.sum @@ -899,7 +899,14 @@ github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiw github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 h1:Yc026VyMyIpq1UWRnakHRG01U8fJm+nEfEmjoAb00n8= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -1497,6 +1504,7 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfF github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= diff --git a/spec/config.json b/spec/config.json index 67ba2ce6f88..002947c119b 100644 --- a/spec/config.json +++ b/spec/config.json @@ -421,6 +421,63 @@ } } }, + "client.tls": { + "type": "object", + "additionalProperties": false, + "description": "Configures http client TLS settings.", + "properties": { + "min_version": { + "type": "string", + "description": "Minimum supported TLS version.", + "default": "tls10", + "examples": [ + "tls10","tls11","tls12","tls13" + ] + }, + "max_version": { + "type": "string", + "description": "Maximum supported TLS version.", + "default": "tls13", + "examples": [ + "tls10","tls11","tls12","tls13" + ] + }, + "cipher_suites": { + "type": "array", + "description": "A list of supported cipher suites.", + "items": { + "type": "string" + }, + "examples": [ + "TLS_RSA_WITH_RC4_128_SHA", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256" + ] + } + } + }, "dsn": { "type": "string", "description": "Sets the data source name. This configures the backend where ORY Hydra persists data. If dsn is \"memory\", data will be written to memory and is lost when you restart this instance. ORY Hydra supports popular SQL databases. For more detailed configuration information go to: https://www.ory.sh/docs/hydra/dependencies-environment#sql"