diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ffc0807f..08bd6f728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm For Helidon 1.x releases please see [Helidon 1.x CHANGELOG.md](https://github.com/oracle/helidon/blob/helidon-1.x/CHANGELOG.md) +## [2.1.0] +- Security: To remove accidental propagation of identity, all security providers that support outbound + security were updated to only do outbound security when configured so. All of these providers + now have an `outbound` configuration section that can define outbound targets. Documentation of + providers was updated to match this new approach and is available in both MP and SE docs. + ## [2.0.3-SNAPSHOT] ### Changes diff --git a/docs/mp/security/02_providers.adoc b/docs/mp/security/02_providers.adoc new file mode 100644 index 000000000..6f55be188 --- /dev/null +++ b/docs/mp/security/02_providers.adoc @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2018, 2020 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + += Security Providers +:h1Prefix: MP +:description: Helidon Security providers +:keywords: helidon, security + +== Implemented Security Providers + +Helidon provides the following security providers for endpoint protection: + +[cols="3,2,^1,6"] +|=== +^|Provider ^|Type ^|Outbound supported ^|Description + +|<> |Authentication |✅ |Open ID Connect supporting JWT, Scopes, Groups and OIDC code flow +|<> |Authentication |✅ |HTTP Basic Authentication support +|<> |Authentication |🚫 |HTTP Digest Authentication support +|<
> |Authentication |✅ |Asserting a user based on a header value +|<> |Authentication |✅ |Protecting service to service communication through signatures +|<> |Role Mapping |🚫 |Retrieves roles from IDCS provider for authenticated user +|<> |Authorization |🚫 |Attribute based access control authorization policies +|=== + +The following providers are no longer evolved: +[cols="3,2,^1,6"] +|=== +^|Provider ^|Type ^|Outbound supported ^|Description + +|<> |Authentication |✅ |Authenticates a token from request against Google servers +|<> |Authentication |✅ |JWT tokens passed from frontend +|=== + +=== OIDC Provider +Open ID Connect security provider. + +==== Setup +[source,xml] +.Maven dependency +---- + + io.helidon.microprofile + helidon-microprofile-oidc + +---- +include::../../shared/security/providers/oidc.adoc[] + +include::../../shared/security/providers/http-basic-auth.adoc[] + +include::../../shared/security/providers/http-digest-auth.adoc[] + +include::../../shared/security/providers/header-assertion.adoc[] + +include::../../shared/security/providers/http-signatures.adoc[] + +include::../../shared/security/providers/idcs-role-mapper.adoc[] + +include::../../shared/security/providers/abac.adoc[] + +include::../../shared/security/providers/google-login.adoc[] + +include::../../shared/security/providers/jwt.adoc[] + diff --git a/docs/mp/security/02_configuration-secrets.adoc b/docs/mp/security/03_configuration-secrets.adoc similarity index 100% rename from docs/mp/security/02_configuration-secrets.adoc rename to docs/mp/security/03_configuration-secrets.adoc diff --git a/docs/se/security/01_introduction.adoc b/docs/se/security/01_introduction.adoc index 2af836aae..27430c6f2 100644 --- a/docs/se/security/01_introduction.adoc +++ b/docs/se/security/01_introduction.adoc @@ -120,33 +120,22 @@ See <<_tools,Secure config>> for details about encrypting passwords in Security security = Security.create(config); ---- -[source,conf] -.Security from configuration - configuration +[source,yaml] +.Security from configuration - application.yaml ---- # Uses config encryption filter to encrypt passwords -security.providers: [ - { - http-basic-auth { - realm = "helidon" - - user-store { - users: [ - { - login = "aUser" - password = "${CLEAR=somePassword}" - roles = ["theRole"] - - }, - { - login = "noRoles" - password = "${CLEAR=somePassword}" - roles = [] - } - ] - } - } - } -] +security: + providers: + - abac: + - http-basic-auth: + realm: "helidon" + users: + - login: "jack" + password: "${CLEAR=password}" + roles: ["user", "admin"] + - login: "jill" + password: "${CLEAR=password}" + roles: ["user"] ---- === Hybrid pattern (Builder & Configuration) diff --git a/docs/se/security/02_providers.adoc b/docs/se/security/02_providers.adoc index a865458ef..1882066c2 100644 --- a/docs/se/security/02_providers.adoc +++ b/docs/se/security/02_providers.adoc @@ -23,706 +23,72 @@ == Implemented Security Providers -The following providers are implemented: +Helidon provides the following security providers for endpoint protection: -* <> -* <> -* <> -* <
> -* <> -* <> -* Google Login Authentication Provider - - please see security example: https://github.com/oracle/helidon/tree/master/examples/security/google-login -* OIDC (Open ID Connect) Authentication provider - an OAuth extension for authentication - - please see security example: https://github.com/oracle/helidon/tree/master/examples/security/idcs-login -* IDCS Role Mapping Provider - a role mapper that can be used with any authentication provider, retrieves roles from IDCS - - please see security example: https://github.com/oracle/helidon/tree/master/examples/security/idcs-login - -=== JWT Provider - -JSON Web Token (JWT) provider has support for authentication and outbound security. - -Authentication is based on validating the token (signature, valid before etc.) and on asserting the subject -of the JWT subject claim. - -For outbound, we support either token propagation (e.g. the token from request is propagated further) or -support for generating a brand new token based on configuration of this provider. - -[cols="2,2,5"] +[cols="3,2,^1,6"] |=== -|Property |Value +^|Provider ^|Type ^|Outbound supported ^|Description -|Maven groupId |io.helidon.security.providers -|Maven artifactId |helidon-security-providers-jwt -|Provider package |io.helidon.security.providers.jwt -|Provider class |JwtProvider -|Provider key |jwt +|<> |Authentication |✅ |Open ID Connect supporting JWT, Scopes, Groups and OIDC code flow +|<> |Authentication |✅ |HTTP Basic Authentication support +|<> |Authentication |🚫 |HTTP Digest Authentication support +|<
> |Authentication |✅ |Asserting a user based on a header value +|<> |Authentication |✅ |Protecting service to service communication through signatures +|<> |Role Mapping |🚫 |Retrieves roles from IDCS provider for authenticated user +|<> |Authorization |🚫 |Attribute based access control authorization policies |=== -This provider is: +The following providers are no longer evolved: +[cols="3,2,^1,6"] +|=== +^|Provider ^|Type ^|Outbound supported ^|Description -* Authentication Provider -* Outbound Security Provider +|<> |Authentication |✅ |Authenticates a token from request against Google servers +|<> |Authentication |✅ |JWT tokens passed from frontend +|=== +=== OIDC Provider +Open ID Connect security provider. + +==== Setup [source,xml] -.Maven Dependency +.Maven dependency ---- io.helidon.security.providers - helidon-security-providers-jwt + helidon-security-providers-oidc ---- -==== Configuration Based Approach -All configuration options: - -[cols="2,2,5"] - -|=== -|key |default value |description - -|optional | false |If set to true, the provider will return "ABSTAIN" rather than "FAILURE" if token is not present in request -|authenticate |true |Whether to attempt authentication -|propagate |true |Whether to attempt identity propagation/JWT creation -|principal-type |USER |Whether we authenticate a user or a service (other option is SERVICE) -|atn-token | |A group for configuring authentication of the request -|atn-token.verify-signature |true |Whether to verify signature in incoming JWT. If disabled, _ANY_ JWT will be accepted -|atn-token.jwt-audience| |Expected audience of the JWT. If not defined, any audience is accepted (and we may accept JWT not inteded for us) -|atn-token/jwk.resource.* | |Configuration of the JWK to obtain key(s) to validate signatures of inbound token. The JWK should contain public keys. This may be: jwk.resource.path, jwk.resource.resource-path, jwk.resource.url, jwk.resource.content-plain (actual JSON string), jwk.resource.content (base64) -|atn-token/handler |Authorization bearer |A handler configuration for inbound token - e.g. how to extract it -|atn-token/handler/header |Authorization |Name of a header the token is expected in -|atn-token/handler/prefix |bearer |Prefix before the token value (optional) -|atn-token/handler/regexp | |Regular expression to obtain the token, first matching group is used (optional) -|sign-token | |A group for configuring outbound security -|sign-token/jwk.resource.* | |Configuration of the JWK to use when generating tokens (follows same rules as atn-token/jwk above), this JWK must contain private keys when using asymmetric ciphers -|sign-token/jwt-issuer | |When we issue a new token, this is the issuer to be placed into it (validated by target service) -|sign-token/outbound | |A group for configuring outbound rules (based on transport, host and/or path) -|sign-token/outbound/name | |A short descriptive name for configured target service(s) -|sign-token/outbound/transports |* |An array of transports this outbound matches (e.g. https) -|sign-token/outbound/hosts |* |An array of hosts this outbound matches, may use * as a a wild-card (e.g. *.oracle.com) -|sign-token/outbound/paths |* |An array of paths on the host this outbound matches, may use * as a wild-card (e.g. /some/path/*) -|sign-token/outbound/outbound-token |Authorization bearer |Configuration of outbound token handler (same as atn-token/handler) -|sign-token/outbound/outbound-token/format | |Java text format for generating the value of outbound token header (e.g. "bearer %1$s") -|sign-token/outbound/jwk-kid | |If this key is defined, we are generating a new token, otherwise we propagate existing. Defines the key id of a key definition in the JWK file to use for signing the outbound token -|sign-token/outbound/jwt-kid | |A key to use in the generated JWT - this is for the other service to locate the verification key in their JWK -|sign-token/outbound/jwt-audience | |Audience this key is generated for (e.g. http://www.example.org/api/myService) - validated by the other service -|sign-token/outbound/jwt-not-before-seconds |5 |Makes this key valid this amount of seconds into the past. Allows a certain time-skew for the generated token to be valid before current time (e.g. when we expect a certain misalignment of clocks) -|sign-token/outbound/jwt-validity-seconds |1 day |Token validity in seconds -|=== - -Example configuration with authentication and outbound security: - -[source,yaml] ----- -- jwt: - atn-token: - jwk.resource.path: "/config/securiy/verify-jwk.json" - jwt-audience: "my.service" - sign-token: - jwk.resource.path: "/config/security/sign-jwk.json" - jwt-issuer: "http://www.example.org/myservice" - outbound: - - name: "internal-services" - # create a new token - hosts: - - "*.example.org" - jwk-kid: "internal-key" - jwt-audience: "http://www.example.org/services" - - name: "b2b-service-49" - # create a new token and send it in a custom header - hosts: - - "b2b.partner.org" - paths: - - "/services/49" - jwk-kid: "partner-b2b" - jwt-audience: "http://b2b.partner.org" - outbound-token: - header: "X-Partner-Auth" - - name: "as-is" - # identity propagation (use existing token) - hosts: - - "*.internal.org" ----- - -=== HTTP Basic Authentication Provider - -Basic authentication support authentication of request and identity propagation for -outbound calls. -Outbound security with basic authentication only works if the request is authenticated -with basic authentication (e.g. we re-use the username and password from inbound request). - -Basic authentication is an HTTP header named `Authorization` -with value of `basic base64(username:password)`. - -This provider also supports "challenging" the client to provide basic -authentication if missing from request. - -See https://tools.ietf.org/html/rfc7617[https://tools.ietf.org/html/rfc7617]. - -These authentication schemes -should be _obsolete_, though they provide a very easy way to test a protected resource. -Note that basic authentication sends username and password unencrypted over the network! -[cols="3,3"] -|=== -|Property |Value - -|Maven groupId |io.helidon.security.providers -|Maven artifactId |helidon-security-providers-http-auth -|Provider package |io.helidon.security.providers.httpauth -|Provider class |HttpBasicAuthProvider -|Provider key |http-basic-auth -|=== - -This provider is: - -* Authentication Provider -* Outbound Security Provider - -[source,xml] -.Maven Dependency ----- - - io.helidon.security.providers - helidon-security-providers-http-auth - ----- - -==== Configuration Based Approach -All configuration options: - -[cols="2,2,5"] - -|=== -|key |default value |description - -|realm | helidon |Authentication realm - may be shown to user by browser -|principal-type |USER |Type of subject authenticated by this provider - USER or SERVICE -|users | none |A list of users (login, password and roles). Currently to externalize this you must use builder approach. -|=== - -Example configuration with a single user (may have more): - -[source,yaml] ----- -- http-basic-auth: - users: - - login: "jack" - password: "jackIsGreat" - roles: ["user", "admin"] ----- - -Example configuration with a single user (may have more) using secured config -filter (to encrypt passwords) - in this example, the password is intentionally in -clear text to show its value (see <>) - -[source,yaml] ----- -- http-basic-auth: - realm: "helidon" - users: - - login: "jack" - password: "${CLEAR=jackIsGreat}" - roles: ["user", "admin"] ----- - -==== Builder Based Approach -Example of builder with a user store (UserStore is an interface that must be implemented). -There is an existing implementation "ConfigUserStore" that can read configuration of users -from Helidon config instance (see "users" configuration key above). -The built instance can then be registered with security to be used for request authentication. +In Helidon SE, we need to register the redirection support with +routing (in addition to `WebSecurity` that integrates with `WebServer`). +This is not required when `redirect` is set to false. [source,java] +.Adding support for OIDC redirects ---- -HttpBasicAuthProvider.builder() - .realm("helidon") - .subjectType(SubjectType.SERVICE) - .userStore(aUserStore) - .build(); ----- - -=== HTTP Digest Authentication -Digest authentication provider supports only authentication of inbound requests (no outbound). - -This provider also supports "challenging" the client to provide digest -authentication if missing from request. - -See https://tools.ietf.org/html/rfc7616[https://tools.ietf.org/html/rfc7616]. - -These authentication schemes -should be _obsolete_, though they provide a very easy way to test a protected resource. -Note that basic authentication sends username and password unencrypted over the network! - -[cols="2,2"] -|=== -|Property |Value - -|Maven groupId |io.helidon.security.providers -|Maven artifactId |helidon-security-providers-http-auth -|Provider package |io.helidon.security.providers.httpauth -|Provider class |HttpDigestAuthProvider -|Provider key |http-digest-auth -|=== - -This provider is: - -* Authentication Provider - -[source,xml] -.Maven Dependency ----- - - io.helidon.security.providers - helidon-security-providers-http-auth - ----- - -==== Configuration based approach - -All configuration options: - -[cols="2,2,5"] - -|=== -|key |default value |description - -|realm |helidon |Authentication realm - may be shown to user by browser -|principal-type |USER |Type of subject authenticated by this provider - USER or SERVICE -|users |none |A list of users (login, password and roles). Currently to externalize this you must use builder approach. -|algorithm |MD5 |Only MD5 supported -|nonce-timeout-millis |1 day |Number of milliseconds for the nonce timeout -|server-secret |random |A string to use as a server secret - this is to use digest auth between multiple servers (e.g. when in a cluster). Used to encrypt nonce. This must not be known outside of this app, as others may create digest requests we would trust. -|qop |NONE |only AUTH supported. If left empty, uses the legacy approach (older RFC version). AUTH-INT is not supported. -|=== - -Example configuration with a single user (may have more): - -[source,yaml] ----- -- http-digest-auth: - realm: "helidon" - users: - - login: "jack" - password: "${CLEAR=jackIsGreat}" - roles: ["user", "admin"] ----- - -==== Builder based approach - -Example of builder with a user store (UserStore is an interface that must be implemented). -There is an existing implementation "ConfigUserStore" that can read configuration of users -from Helidon config instance (see "users" configuration key above). -The built instance can then be registered with security to be used for request authentication. - -[source,java] ----- -HttpDigestAuthProvider.builder() - .realm("helidon") - .digestServerSecret("aPassword".toCharArray()) - .userStore(buildUserStore()) ----- - -=== Header Authentication Provider - -This provider inspects a specified request header and extracts the username/service name from it and -asserts it as current subject's principal. - -This can be used when we use perimether authentication (e.g. there is a gateway that takes -care of authentication and propagates the user in a header). - -[cols="2,2"] -|=== -|Property |Value - -|Maven groupId |io.helidon.security.providers -|Maven artifactId |helidon-security-providers-header -|Provider package |io.helidon.security.providers.header -|Provider class |HeaderAtnProvider -|Provider key |header-atn -|=== - -This provider is: - -* Authentication Provider -* Outbound Security Provider - -[source,xml] -.Maven Dependency ----- - - io.helidon.security.providers - helidon-security-providers-header - ----- - -==== Configuration Based Approach -All configuration options: - -[cols="2,2,5"] - -|=== -|key |default value |description - -|optional | false |If set to true, provider will abstain rather then fail if header not available -|authenticate| true |If set to false, authentication will not be attempted -|propagate | true |If set to false, identity propagation will not be done -|principal-type | USER |Can be USER or SERVICE -|atn-token | none | Token extraction and propagation, you can define which header to use and how to extract it -|outbound-token | atn-token | If outbound token should be created differently than inbound -|=== - -Example configuration: - -[source,yaml] ----- -- header-atn: - optional: true - principal-type: SERVICE - atn-token: - header: "X-AUTH-USER" - outbound-token: - header: "Authorization" - format: "bearer %1$s" - ----- - -==== Builder Based Approach -Example of a builder that configures the provider the same way as the above configuration approach. - -[source,java] ----- -HeaderAtnProvider.builder() - .optional(true) - .subjectType(SubjectType.SERVICE) - .atnTokenHandler(TokenHandler.builder() - .tokenHeader("X-AUTH-USER") - .build()) - .outboundTokenHandler(TokenHandler.builder() - .tokenHeader("Authorization") - .tokenFormat("bearer %1$s") - .build()) - .build(); ----- - -=== HTTP Signatures - -Support for HTTP Signatures (both inbound and outbound). -[source,xml] -.Maven Dependency ----- - - io.helidon.security.providers - helidon-security-providers-http-sign - ----- - -==== Signature basics - -* standard: based on https://tools.ietf.org/html/draft-cavage-http-signatures-03 -* key-id: an arbitrary string used to locate signature configuration - when a - request is received the provider locates validation configuration based on this - id (e.g. HMAC shared secret or RSA public key). Commonly used meanings are: key - fingerprint (RSA); API Key - - -==== Inbound signatures -We act as a server and another party is calling us with a signed HTTP request. -We validate the signature and assume identity of the caller. - -Builder example, starting from inside out: -[source,java] -.Inbound signature configuration ----- -// Configuration of public key certificate to validate inbound requests - KeyConfig keyConfig = KeyConfig.keystoreBuilder() - .keystore(Resource.create(Paths.get("keystore.p12"))) - .keystorePassphrase("password".toCharArray()) - .certAlias("service_cert") - .build(); - - // Create inbound client definition (e.g. map key-id to a public key and principal name) - InboundClientDefinition rsaInbound = InboundClientDefinition.builder("service1-rsa") - .principalName("Service1") - .publicKeyConfig(keyConfig) - .build(); - - // Now create a HTTP signature provider with inbound support (with a single supported signature) - HttpSignProvider.builder() - .addInbound(rsaInbound) - .build(); ----- - -Configuration examples for hmac-sha256 and rsa-sha256 algorithms (as supported by - this provider): - -[source,conf] -.Inbound signature configuration ----- -http-signatures { - inbound { - keys: [ - { - key-id = "service1-hmac" - # name of principal of the connecting party - principal-name = "Service1" - # SERVICE or USER, defaults to SERVICE - principal-type = SERVICE - # defaults to the one we configure (e.g. if hmac.secret is configured - # it is hmac-sha256; if public-key is configured, it is rsa-sha256) - algorithm = "hmac-sha256" - # shared secret for symmetric signatures - hmac.secret = "${CLEAR=encryptMe}" - }, - { - key-id = "service1-rsa" - principal-name = "Service1" - # configuration of public key to validate signature - public-key { - # path to keystore - keystore-path = "src/main/resources/keystore.p12" - # defaults to PKCS12 - keystore-type = "PKCS12" - # password of the keystore - # the ${CLEAR=} is a feature of - keystore-passphrase = "${CLEAR=password}" - # alias of the certificate to get public key from - cert-alias = "service_cert" - } - } - ] - } -} - ----- - -==== Outbound signatures -We act as a client and we sign our outgoing requests. - -Builder example, starting from inside out (rsa only, as hmac is significantly - simpler): -[source,java] -.Outbound signature configuration ----- -// Configuration of private key to sign outbound requests -KeyConfig keyConfig = KeyConfig.keystoreBuilder() - .keystore(Resource.create(Paths.get("src/main/resources/keystore.p12"))) - .keystorePassphrase("password".toCharArray()) - .keyAlias("myPrivateKey") - .build(); - -OutboundTarget rsaTarget = OutboundTarget.builder("service2-rsa") - .addHost("service2") // considering service registry - .addPath("/service2-rsa") - .customObject(OutboundTargetDefinition.class, - OutboundTargetDefinition.builder("service1-rsa") - .privateKeyConfig(keyConfig) - .build()) - .build(); - -// Now create a HTTP signature provider with outbound support (with a single supported signature) -HttpSignProvider.builder() - .outbound(OutboundConfig.builder() - .addTarget(rsaTarget) - .build()) +Routing routing = Routing.builder() + .register(WebSecurity.create(config.get("security"))) + .register(OidcSupport.create(config)) + ... .build(); ---- -Configuration examples for hmac-sha256 and rsa-sha256 algorithms (as supported by - this provider): +include::../../shared/security/providers/oidc.adoc[] -[source,conf] -.Inbound signature configuration ----- -http-signatures { -outbound: [ - { - # Logical name of this outbound configuration - name = "service2-trust-circle" - # If ommited or one value is "*", all are supported - transports = ["http", "https"] - # If ommited or one value is "*", all are supported, may contain * as a sequence "any" characters/nubmers - hosts = ["service2"] - # If ommited, all are supported - regular expression - paths = ["/service2"] +include::../../shared/security/providers/http-basic-auth.adoc[] - # Configuration of signature (signing the request) - signature { - key-id = "service2-shared-secret" - # HMAC shared secret (algorithm hmac-sha256) - hmac.secret = "${CLEAR=somePasswordForHmacShouldBeEncrypted}" - } - }, - { - name = "service2-rsa" - hosts = ["service2"] - paths = ["/service2-rsa"] +include::../../shared/security/providers/http-digest-auth.adoc[] - signature { - key-id = "service1-rsa" - # RSA private key (algorithm rsa-sha256) - private-key { - # path to keystore - keystore-path = "src/main/resources/keystore.p12" - # Keystore type - # PKCS12, JSK or RSA (not really a keystore, but directly the linux style private key unencrypted) - # defaults to jdk default - keystore-type = "PKCS12" - # password of the keystore - keystore-passphrase = "password" - # alias of the key to sign request - key-alias = "myPrivateKey" - } - } - } -] -} ----- +include::../../shared/security/providers/header-assertion.adoc[] -=== ABAC (Attribute based access control) Authorization Provider +include::../../shared/security/providers/http-signatures.adoc[] -This provider is an authorization provider validating various attributes against -configured validators. +include::../../shared/security/providers/idcs-role-mapper.adoc[] -Any attribute of the following objects can be used: +include::../../shared/security/providers/abac.adoc[] - - environment (such as time of request) - e.g. env.time.year - - subject (user) - e.g. subject.principal.id - - subject (service) - e.g. service.principal.id - - object (must be explicitly invoked by developer in code, as object cannot be automatically added to security context) - e.g. object.owner +include::../../shared/security/providers/google-login.adoc[] -This provider checks that all defined ABAC validators are validated. -If there is a definition for a validator (e.g. an annotation) that is not checked, -the request is denied. +include::../../shared/security/providers/jwt.adoc[] -[source,xml] -.Maven Dependency ----- - - io.helidon.security.providers - helidon-security-providers-abac - ----- - -The following validators are implemented: - -* <> -* <> -* <> -* <