elastic / elasticsearch

Free and Open Source, Distributed, RESTful Search Engine
https://www.elastic.co/products/elasticsearch
Other
69.66k stars 24.65k forks source link

JWT support #72060

Closed tvernum closed 2 years ago

tvernum commented 3 years ago

Overview

We would like to offer some form of support for JSON Web Tokens in Elasticsearch Security.

JWTs are a JSON representation of security claims, typically (in practice, universally) about a User (where the term "user" covers any concept of identity, include systems).

JWTs are frequently used as a means of passing a representation of a user's identity between layers or services in an application. For example, a micro services based architecture may authenticate a user at the periphery (e.g on the server side of a web application), and then use JWT as the container for attaching the authenticated user's identity to subsequent service calls. Encapsulating the user's claims in a JWT avoids the need for each service to connect to a central identity store (which may not exist), and the use of JWS provides some degree of security between the layers, and reduces1 the extent to which each service must trust the caller.

JWTs can also be the basis for a distributed authentication protocol - OpenID Connect uses JWTs as the structure in which claims pass from the authentication service to the relying party.

However, JWTs are not, by default, an authentication protocol. They are simply a structure for representing (usually verifiable) claims about an entity. Possession of a JWT does not, on its own, imply that the holder is the user in question, any more than a certified copy of a birth certificate proves that the holder is the person named on that certificate.
This is not simply a theoretical issue - OAuth2 is a JWT based authorization protocol, that is explicitly not an authentication protocol, and treating OAuth JWTs as proof of identity (rather than a means of access) has led to security vulnerabilities in other applications.

That said, there is wide spread use of JWT as the underlying structure of a Bearer Tokens within OAuth and more generally. This is not inherently bad. Elasticsearch Security supports Bearer Tokens that are not implemented using JWT, but could have been - the design and implementation of Elasticsearch's access tokens would not have been compromised if we have elected to build them on top of a JWT format.
If a JWT is issued to a client with the express purpose of being used as a Bearer Token, then using it for that purpose is sound (subject to design/implementation flaws). But an arbitrary JWT may not have been intended as a Bearer Token, and treating it as one (even if it is signed by a trusted party) could result in significant security vulnerabilities.

Consequently, there is value in enhancing Elasticsearch Security to allow user identity to be provided in the form of a JWT, but there are risks with accepting externally generated JWTs as proof of identity.

Options

The following options have been proposed as possibly ways that Elasticsearch Security could support JWT based identity

  1. Implement a "run as JWT" feature.
  2. Implement a JWT realm
  3. Extend the existing Token Service to support JWTs
  4. Extend the existing Token Service to external tokens
  5. Implement an API to exchange JWTs for ES tokens,

Run as JWT

Elasticsearch Security has a feature to allow a request to "run as" another user. In current versions of Elasticsearch, this user must exist in a user store (natively within Elasticsearch, or in an external store such as an LDAP directory) and be able to be "looked up" by username from a configured realm.
Permission to "run as" another user is granted via the run_as privilege within a role, which grants access by username.

This model could be extended to support "run as" with JWTs instead of usernames. The behaviour would resemble the following:

  1. A new header would be added (e.g. es-security-runas-jwt), the value of which would be a JWT (possibly base64 encoded).
  2. As per the existing run as support, the request itself would need to be authenticated by another user. This could be performed using basic authentication, or another supported method such as PKI (mutual TLS).
  3. The role model would be extended to include some method by which "run-as-jwt" permissions could be declared. Further discussion would be required to determine what these permissions would look like.
  4. If the header is present, and the authenticated user is permitted to run as the JWT then a ephemeral user would be created by role-mapping the JWT in a manner similar to that used for the existing OpenID Connect realm.
  5. The request would execute under the scope & permissions of the ephemeral JWT user, as is the case for the existing run-as feature.
  6. The features above would be supported by new configuration values that define which keys to trust when validating JWT signatures, and rules for how JWTs should be exposed in role mapping.

Conceptually this reflect the fact that a JWT, absent any additional context, is a description of a security entity (a user) rather than a credential for that user. Thus it is analogous to looking up a user record in an LDAP directory, rather than authenticating against that directory.

JWT Realm

It is technically possible to implement JWT Bearer Token authentication as a new realm type. Since it is not possible to know whether an arbitrary JWT is intended to be used as a Bearer Token, the onus would be on the Elasticsearch cluster administrator to only configure trust against issuers (keys) that exclusively issue Bearer Tokens. If an administrator configured a realm that trusted an key that was used to sign non-bearer tokens, then this would introduce a security vulnerability in their cluster, but Elasticsearch would have no way of detecting the problem, since it is impossible to distinguish Bearer JWTs from other JWT usage.

Implementation wise, this option would require:

  1. A new realm type, with configuration options that are a subset of the OIDC realm configuration.
  2. Parsing & Validating JWTs from Authorization: Bearer headers
  3. Mapping JWTs to users in a way that mimics the OIDC realm.

JWTs in the Token Service

Since Elasticsearch Security already has Bearer token support, it would be possible to implement JWT Bearer Tokens as an extension to the token service, rather than a new realm.
However, the implementation (and risks) would be roughly equivalent.

External tokens

Elasticsearch supports OAuth-style access tokens (which are self issued) and Service Account Tokens.
We could extend this to also support "externally validated tokens". These would be tokens that are passed to Elasticsearch using an Authorization: Bearer header, but where the validation of the token is delegated to an external service.

Strictly speaking this would not add JWT support to Elasticsearch, but it would provide the foundation that allows users of Elasticsearch to relatively easily implement JWT support themselves, using their own validation rule.

This could be implemented by:

  1. Adding new configuration settings to Elasticsearch to support (one or more) token validation service URLs.
  2. When a Bearer token is received in Elasticsearch, and it does not match an Elasticsearch issued access token, or a Service Account token, then Elasticsearch would make an HTTP call to the external validation service(s), passing the token value.
  3. The external HTTP service would be required to validate the token and, if successful, return a representation of the user that was authenticated by the token.

This solution allows us to support any sort of bearer token that administrators need, but has some downsides:

  1. It simply pushes the validation problem to cluster administrators
  2. It adds new HTTP calls into the authentication flow
  3. It requires defining a new web service schema
  4. It is not clear what the service should return - is it a full user with role names? Or is it an object that can be passed into role mapping?

JWT / Access Token Exchange

This option is described here: https://github.com/elastic/elasticsearch/issues/69996

It is based on the premise that another application is performing a full OIDC authentication process, and Elasticsearch is simply a downstream consumer of that OIDC JWT.

This assumption has the following consequences:

  1. It is only truly applicable for OIDC use cases, but there will be the temptation for administrators to try and adapt it for other JWT scenarios.
  2. It assume that the caller is able to orchestrate the exchange of a JWT for an access token and use that on subsequent requests, which may not be the case.

Footnotes

(1) "Reduces" because there is no guarantee that the user who is described in the JWT actually initiated the operation in question.
Assume that there are 3 processes: WebServer, AuthcService & BackendService, all connected using mutual-TLS.
A user provides some set of credentials to the WebServer. WebServer passes those credentials to AuthcService which validates them and returns a signed JWT. WebServer then makes a call to BackendService and provides this JWT as the identity of the originating user on which the call should operate. The BackendService can verify that such as user exists (by validating the JWT signature) and can verify that the request originated from the WebServer (by virtue of the mTLS connection) but it required to trust the WebServer's assertion that this operation was triggered by the User. It has no way of verifying that this is the case.

elasticmachine commented 3 years ago

Pinging @elastic/es-security (Team:Security)

jkakavas commented 3 years ago

In my view, the options that would be close to satisfying the use cases I've seen while making sense technically are the two first ones: run-as and a jwt realm. I'm biased towards the realm solution as this is the one I've been thinking about but I don't dislike the run-as based one, in fact I like it a lot. Some points:

Would it be worth to add a summary of the use cases that we know of for how this solution would be used in order to help us come to a conclusion?

albertzaharovits commented 3 years ago

I think historically we (at least it is my impression that this is shared) held the right stance that we cannot validate a JWT, outside of a protocol, as a credential, hence JWT support is not an authentication method. JWT support is about trusting authorization decisions. I also think that there's a lot of confusion around the support for this feature because of the multitude of roles ES can take: ES can authenticate users, act as a Service Provider in a Single Sign-On flow (also as an Idp), and now it's also being asked to function as a Resource Server.

But, pragmatically, I think the ask is clear: pass a value in a request header (which discounts the JWT / Access Token Exchange option) to let the caller access the resource on ES. Assuming this, I think the best we can do is to try to distinguish the JWT support, where ES acts as the Resource Server, from the authentication role that ES can otherwise take. Concretely, I can think of:

I'm getting a bit extreme, but my overarching point is separation from authentication in terms of configuration, usage and documentation. That being said, Run as JWT would work as well, as it is acknowledges that a JWT is more akin to an username than a password, but I fear there is opportunity for confusion.

tvernum commented 3 years ago

The run-as solution presumes that the JWTs are validated externally before submitted to ES?

My expectation was that we would absolutely do that validation in ES (I make no presumptions about whether they are validated before they're passed to ES - though I would expect that they should be).

The issue is that JWTs fall into 3 rough categories:

  1. JWTs that are used for authentication (e.g. OIDC)
  2. JWTs that are used for authorization (e.g OAuth2)
  3. JWTs that are used for something else entirely.

A realm is reasonable for (1), but has no real way to know distinguish between those cases, other than indirectly by signing key (that is, an admin knows that a key is only used to generate keys that are intended for authc, and configures the realm to trust that key).

Category (2) is really a run-as scenario. I am this system identity (logically equivalent to an OAuth client) authenticated by this credential (e.g. PKI cert/key), and I have a JWT that authorizes me to do something on behalf of this user.
However, we can reasonably expect that people will try to use the realm for (2) as well. That's not automatically a vulnerability (assuming the realm is performing role mapping, then it will map against the claims in the JWT, which is the intention of something like OAuth), but can be (if you use authorization_realms and lookup by userid) and it loses information from an audit point of view - we only audit the authz user and don't track the system identity (client) that is running as the user.

Category (3) should be entirely avoided. Trusting a JWT that is neither an authc credential nor an authz token is always going to be a problem. In the realm case the only protection you have against that is the keys that you trust. In the run-as case you have a second layer of protection because you validate that the request came from an authorized client (system account) and your trust model assumes that the client is restrictive about which JWTs it passes through.

ywangd commented 3 years ago

I'd like to propose another option which is a variant of JWT Realm. Instead of just using the JWT as the bearer token, we could configure a secret value for the JWT realm and requires it to be passed along with the JWT. So the bearer header would be something like:

Authorization: secret_value:{JWT}

The secret value basically acts as a system password, which prevents malicious usages of arbitrary JWTs. I think it has all the advantages of the JWT Realm while adding extra protection like the run-as option. I'd also assume that this header variant is something that the customer side can easily handle with its current setup.

bytebilly commented 3 years ago

From a user perspective, I see the JWT realm as the best option for various reasons. It's simple to communicate, it is more consistent with other authentication/authorization options we already provide, and it would be more complete.

Said that, I'm +1 to evaluate security implications and protect our users from common misconfiguration cases. What Yang proposed could be a good option.

colings86 commented 3 years ago

@ywangd Since JWT already assumes a secret that the server knows what is the function of the additional secret in your suggestion?

Personally I am in favour of the run-as option. It makes it clear that we intend JWTs to be used for Authorization and enforces that the client sending the request is a genuine client allowed to connect to that Elasticsearch Cluster. For me this option is the easiest for users to ensure they are implementing correctly and not leaving themselves open to unknown clients connecting

ywangd commented 3 years ago

Since JWT already assumes a secret that the server knows what is the function of the additional secret in your suggestion?

@colings86 The extra secret is to prevent authentication using an arbitrary JWT signed by the same issuer. Specifically this is to solve the following problem listed for the JWT Realm option:

Since it is not possible to know whether an arbitrary JWT is intended to be used as a Bearer Token, the onus would be on the Elasticsearch cluster administrator to only configure trust against issuers (keys) that exclusively issue Bearer Tokens. If an administrator configured a realm that trusted an key that was used to sign non-bearer tokens, then this would introduce a security vulnerability in their cluster, but Elasticsearch would have no way of detecting the problem, since it is impossible to distinguish Bearer JWTs from other JWT usage.

You can think of this additional secret as the actual credential used for authentication and the JWT is for run-as. So it is kinda a cross between the Run As option and the JWT Realm option. Its advantages compared to the pure Run As option are:

  1. Itself support run as, i.e. one can authenticate using JWT and also run as another user
  2. It likely means less changes for clients
tvernum commented 2 years ago

Implemented, see:

owainrutherford commented 1 year ago

Hello, since elasticsearch has moved on in minor versions since this was released, is this feature still considered beta? If not, is there a rough timescale as to when it will become a GA feature?

bytebilly commented 1 year ago

Hi @owainrutherford, the feature is still in beta even if we haven't got negative feedback so far. We are planning to release other iterations in the near future that will eventually bring the status to GA.

owainrutherford commented 1 year ago

Thank you for the quick response. Just to clarify, would this be a future minor version of elasticsearch 8, and there are no (planned) breaking changes to this feature?

bytebilly commented 1 year ago

would this be a future minor version of elasticsearch 8

We're planning to release improved support in one of the next few minor releases for 8.x. However, our plans may change because of other factors so we cannot commit to any specific version at the moment.

there are no (planned) breaking changes to this feature

Currently we don't have any planned breaking changes on the table. However, since it's still a beta feature, it may be subject to breaking changes before it goes GA.

owainrutherford commented 1 year ago

Hi there, I see with elasticsearch 8.8 the beta warning is removed from the documentation. As of 8.8 is the jwt feature now considered GA? Thanks

bytebilly commented 1 year ago

Yes, the JWT realm is now GA.

miriam-orion commented 10 months ago

Hello, Another question regarding availability: Does the description on JWT authentication refer to the feature "Single sign-on (SAML, OpenID Connect, Kerberos, JWT)" that requires the paid subscription? Or are the JWT realm and authentication part of the basic version?

[Update] Found the answer: it is part of paid subscriptions.

bytebilly commented 10 months ago

Hi @miriam-orion, the JWT realm is included in the Platinum and Enterprise subscriptions and it's not available in the Basic tier. You can see it listed along with other Platinum SSO realms in the subscriptions page.

miriam-orion commented 10 months ago

Hi @miriam-orion, the JWT realm is included in the Platinum and Enterprise subscriptions and it's not available in the Basic tier. You can see it listed along with other Platinum SSO realms in the subscriptions page.

Thanks for the reply. @ elastic team: In general, it would save lot of researching time if requirements for a feature were listed at the top of its feature docs page.