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..cb96b602ce4 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,39 @@ 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 + } + + if p.p.Exists(KeyClientTLSMinVer) { + keyMinVer := p.p.String(KeyClientTLSMinVer) + 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 + } + } + + if p.p.Exists(KeyClientTLSMaxVer) { + keyMaxVer := p.p.String(KeyClientTLSMaxVer) + 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 ffbb3f0de61..d2bf8e6aa08 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,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 6561d65bb80..f6da9ff5e45 100644 --- a/go.sum +++ b/go.sum @@ -922,8 +922,15 @@ 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-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 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= @@ -1501,6 +1508,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/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= diff --git a/spec/config.json b/spec/config.json index 3915f1d70a4..7b50a14b6ed 100644 --- a/spec/config.json +++ b/spec/config.json @@ -452,6 +452,61 @@ } } }, + "client.tls": { + "type": "object", + "additionalProperties": false, + "description": "Configures http client TLS settings.", + "properties": { + "min_version": { + "type": "string", + "description": "Minimum supported TLS version.", + "examples": [ + "tls10","tls11","tls12","tls13" + ] + }, + "max_version": { + "type": "string", + "description": "Maximum supported TLS version.", + "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"