From 26cf81ec3c813c6efaeb3e1a071af0a43c7735e2 Mon Sep 17 00:00:00 2001 From: Sasha Chernomordik Date: Thu, 13 Jan 2022 15:21:27 +0200 Subject: [PATCH] Integrate `ca-cert` variable into full JWT Authentication flow The ca-cert value contains the X.509 public key certificate or certificate bundle. Each certificate in the bundle should be in PEM (RFC7468) format. The certificate(s) from the variable is/are replacing default operating system CA certificates bundle during fetching JWK Set from remote URI. Use the variable in order to establish TLS connection and validate server identity when the server is using self-signed certificate or certificate is signed by 3rd party CA. --- CHANGELOG.md | 11 ++++ .../create_signing_key_provider.rb | 1 + .../signing_key/fetch_jwks_uri_signing_key.rb | 10 +-- .../features/authn_jwt_ca_cert.feature | 64 ++++++++++--------- .../fetch_jwks_signing_key_spec.rb | 4 +- 5 files changed, 54 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 227f95ec78..1cc1530686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added +- Added an ability to fetch signing keys from JWKS endpoints is using self-signed + certificate or certificate signed by 3rd party CA to JWT generic vendor configuration. + [#2462](https://github.com/cyberark/conjur/pull/2462) + [#2461](https://github.com/cyberark/conjur/pull/2461) + [#2456](https://github.com/cyberark/conjur/pull/2456) + [#2455](https://github.com/cyberark/conjur/pull/2455) + [#2457](https://github.com/cyberark/conjur/pull/2457) + [#2452](https://github.com/cyberark/conjur/pull/2452) + [#2447](https://github.com/cyberark/conjur/pull/2447) + ## [1.15.0] - 2021-12-21 ### Added diff --git a/app/domain/authentication/authn_jwt/signing_key/create_signing_key_provider.rb b/app/domain/authentication/authn_jwt/signing_key/create_signing_key_provider.rb index e738da07b9..8d136da9dc 100644 --- a/app/domain/authentication/authn_jwt/signing_key/create_signing_key_provider.rb +++ b/app/domain/authentication/authn_jwt/signing_key/create_signing_key_provider.rb @@ -73,6 +73,7 @@ def fetch_jwks_uri_signing_key ) @fetch_jwks_uri_signing_key ||= @fetch_jwks_uri_signing_key_class.new( jwks_uri: signing_key_settings.uri, + cert_store: signing_key_settings.cert_store, fetch_signing_key: @fetch_signing_key ) end diff --git a/app/domain/authentication/authn_jwt/signing_key/fetch_jwks_uri_signing_key.rb b/app/domain/authentication/authn_jwt/signing_key/fetch_jwks_uri_signing_key.rb index 7e2e92d96b..df5026f2ea 100644 --- a/app/domain/authentication/authn_jwt/signing_key/fetch_jwks_uri_signing_key.rb +++ b/app/domain/authentication/authn_jwt/signing_key/fetch_jwks_uri_signing_key.rb @@ -11,7 +11,7 @@ class FetchJwksUriSigningKey def initialize( jwks_uri:, fetch_signing_key:, - ca_cert: nil, + cert_store: nil, http_lib: Net::HTTP, create_jwks_from_http_response: CreateJwksFromHttpResponse.new, logger: Rails.logger @@ -22,7 +22,7 @@ def initialize( @jwks_uri = jwks_uri @fetch_signing_key = fetch_signing_key - @ca_cert = ca_cert + @cert_store = cert_store end def call(force_fetch:) @@ -63,14 +63,14 @@ def jwks_keys end def net_http_start(host, port, use_ssl, &block) - if @ca_cert && !use_ssl + if @cert_store && !use_ssl raise Errors::Authentication::AuthnJwt::FetchJwksKeysFailed.new( @jwks_uri, "TLS misconfiguration - ca-cert is provided but jwks-uri URI scheme is http" ) end - if @ca_cert + if @cert_store net_http_start_with_ca_cert(host, port, use_ssl, &block) else net_http_start_without_ca_cert(host, port, use_ssl, &block) @@ -82,7 +82,7 @@ def net_http_start_with_ca_cert(host, port, use_ssl, &block) host, port, use_ssl: use_ssl, - cert_store: @ca_cert, + cert_store: @cert_store, &block ) end diff --git a/cucumber/authenticators_jwt/features/authn_jwt_ca_cert.feature b/cucumber/authenticators_jwt/features/authn_jwt_ca_cert.feature index 9216bcc945..5c27a5d637 100644 --- a/cucumber/authenticators_jwt/features/authn_jwt_ca_cert.feature +++ b/cucumber/authenticators_jwt/features/authn_jwt_ca_cert.feature @@ -4,8 +4,7 @@ Feature: JWT Authenticator - ca-cert variable tests All tests are using status API for validation. Background: - Given I initialize JWKS endpoint with file "ca-cert.json" - And I load a policy: + Given I load a policy: """ - !policy id: conjur/authn-jwt/raw @@ -16,21 +15,22 @@ Feature: JWT Authenticator - ca-cert variable tests """ Scenario: ONYX-15311: Self-signed jwks-uri no ca-cert variable - Given I am the super-user - And I successfully set authn-jwt "jwks-uri" variable to value "https://jwks/ca-cert.json" + Given I initialize JWKS endpoint with file "ca-cert-ONYX-15311.json" + And I am the super-user + And I successfully set authn-jwt "jwks-uri" variable to value "https://jwks/ca-cert-ONYX-15311.json" When I GET "/authn-jwt/raw/cucumber/status" Then the HTTP response status code is 500 - And the authenticator status check fails with error "CONJ00087E Failed to fetch JWKS from 'https://jwks/ca-cert.json'. Reason: '#'>" + And the authenticator status check fails with error "CONJ00087E Failed to fetch JWKS from 'https://jwks/ca-cert-ONYX-15311.json'. Reason: '#'>" - @skip @sanity Scenario: ONYX-15312: Self-signed jwks-uri with valid ca-cert variable value - Given I am the super-user + Given I initialize JWKS endpoint with file "ca-cert-ONYX-15312.json" + And I am the super-user And I extend the policy with: """ - !variable conjur/authn-jwt/raw/ca-cert """ - And I successfully set authn-jwt "jwks-uri" variable to value "https://jwks/ca-cert.json" + And I successfully set authn-jwt "jwks-uri" variable to value "https://jwks/ca-cert-ONYX-15312.json" And I fetch root certificate from https://jwks endpoint as "self" And I successfully set authn-jwt "ca-cert" variable value to the "self" certificate When I GET "/authn-jwt/raw/cucumber/status" @@ -38,9 +38,10 @@ Feature: JWT Authenticator - ca-cert variable tests And the HTTP response content type is "application/json" And the authenticator status check succeeds - @skip Scenario Outline: ONYX-15313/6: Self-signed jwks-uri with ca-cert contains bundle includes the valid certificate - Given I am the super-user + Given I initialize JWKS endpoint with file "ca-cert-ONYX-15313.json" + And I initialize JWKS endpoint with file "ca-cert-ONYX-15316.json" + And I am the super-user And I extend the policy with: """ - !variable conjur/authn-jwt/raw/ca-cert @@ -59,26 +60,27 @@ Feature: JWT Authenticator - ca-cert variable tests And the HTTP response content type is "application/json" And the authenticator status check succeeds Examples: - | jwks-uri | - | https://jwks/ca-cert.json | - | https://chained.mycompany.local/ca-cert.json | + | jwks-uri | + | https://jwks/ca-cert-ONYX-15313.json | + | https://chained.mycompany.local/ca-cert-ONYX-15316.json | Scenario: ONYX-15314: Chained jwks-uri no ca-cert variable - Given I am the super-user - And I successfully set authn-jwt "jwks-uri" variable to value "https://chained.mycompany.local/ca-cert.json" + Given I initialize JWKS endpoint with file "ca-cert-ONYX-15314.json" + And I am the super-user + And I successfully set authn-jwt "jwks-uri" variable to value "https://chained.mycompany.local/ca-cert-ONYX-15314.json" When I GET "/authn-jwt/raw/cucumber/status" Then the HTTP response status code is 500 - And the authenticator status check fails with error "CONJ00087E Failed to fetch JWKS from 'https://chained.mycompany.local/ca-cert.json'. Reason: '#'>" + And the authenticator status check fails with error "CONJ00087E Failed to fetch JWKS from 'https://chained.mycompany.local/ca-cert-ONYX-15314.json'. Reason: '#'>" - @skip @sanity Scenario: ONYX-15315: Self-signed jwks-uri with valid ca-cert variable value - Given I am the super-user + Given I initialize JWKS endpoint with file "ca-cert-ONYX-15315.json" + And I am the super-user And I extend the policy with: """ - !variable conjur/authn-jwt/raw/ca-cert """ - And I successfully set authn-jwt "jwks-uri" variable to value "https://chained.mycompany.local/ca-cert.json" + And I successfully set authn-jwt "jwks-uri" variable to value "https://chained.mycompany.local/ca-cert-ONYX-15315.json" And I fetch root certificate from https://chained.mycompany.local endpoint as "chained" And I successfully set authn-jwt "ca-cert" variable value to the "chained" certificate When I GET "/authn-jwt/raw/cucumber/status" @@ -86,15 +88,10 @@ Feature: JWT Authenticator - ca-cert variable tests And the HTTP response content type is "application/json" And the authenticator status check succeeds - Scenario: ONYX-15317: Google's jwks-uri no ca-cert variable - Given I am the super-user - And I successfully set authn-jwt "jwks-uri" variable to value "https://www.googleapis.com/oauth2/v3/certs" - When I GET "/authn-jwt/raw/cucumber/status" - Then the HTTP response status code is 200 - And the HTTP response content type is "application/json" - And the authenticator status check succeeds - - @skip + # ONYX-15318 and ONYX-15317 are order sensitive tests + # ONYX-15317 stores keys in cash and ONYX-15318 will fail + # if it's running after ONYX-15317 + # ONYX-15318 demands conjur restart once ONYX-15317 passed @sanity Scenario: ONYX-15318: Google's jwks-uri with invalid ca-cert variable value Given I am the super-user @@ -107,4 +104,13 @@ Feature: JWT Authenticator - ca-cert variable tests And I successfully set authn-jwt "ca-cert" variable value to the "chained" certificate When I GET "/authn-jwt/raw/cucumber/status" Then the HTTP response status code is 500 - And the authenticator status check fails with error "CONJ00087E Failed to fetch JWKS from 'https://www.googleapis.com/oauth2/v3/certs'. Reason: '#'>" + And the authenticator status check fails with error "CONJ00087E Failed to fetch JWKS from 'https://www.googleapis.com/oauth2/v3/certs'. Reason: '#'>" + + Scenario: ONYX-15317: Google's jwks-uri no ca-cert variable + Given I am the super-user + And I successfully set authn-jwt "jwks-uri" variable to value "https://www.googleapis.com/oauth2/v3/certs" + When I GET "/authn-jwt/raw/cucumber/status" + Then the HTTP response status code is 200 + And the HTTP response content type is "application/json" + And the authenticator status check succeeds + diff --git a/spec/app/domain/authentication/authn-jwt/signing_key/fetch_jwks_signing_key_spec.rb b/spec/app/domain/authentication/authn-jwt/signing_key/fetch_jwks_signing_key_spec.rb index 55d2353cc4..49b19617a2 100644 --- a/spec/app/domain/authentication/authn-jwt/signing_key/fetch_jwks_signing_key_spec.rb +++ b/spec/app/domain/authentication/authn-jwt/signing_key/fetch_jwks_signing_key_spec.rb @@ -126,7 +126,7 @@ context "when it present" do subject do ::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(jwks_uri: jwks_uri_https, - ca_cert: cert_store_present, + cert_store: cert_store_present, fetch_signing_key: mocked_fetch_signing_key, logger: mocked_logger, http_lib: mocked_http_response_ca_cert_present, @@ -142,7 +142,7 @@ context "when it present but uri is http" do subject do ::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(jwks_uri: jwks_uri_http, - ca_cert: cert_store_present, + cert_store: cert_store_present, fetch_signing_key: mocked_fetch_signing_key, logger: mocked_logger, http_lib: mocked_http_response_ca_cert_present,