Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of token metadata when templating OIDC claims #22335

Closed
jackadamson opened this issue Aug 15, 2023 · 16 comments
Closed

Allow use of token metadata when templating OIDC claims #22335

jackadamson opened this issue Aug 15, 2023 · 16 comments

Comments

@jackadamson
Copy link

Is your feature request related to a problem? Please describe.
We store additional information in token metadata for tracing, such as the host that the token was issued to.
I'd like to reference this metadata in OIDC claims so, in the case of a compromised OIDC token, we can identify what host the OIDC token was issued to.
Presently, we can can see the identity the OIDC token was issued for, however we can't access more granular information about the token.

Describe the solution you'd like
When writing a template for usage in an OIDC role (eg. vault write identity/oidc/role/some-role -template=$TEMPLATE), it'd be useful to access both the whole token meta field, as well as individual fields on the token metadata.

Example

Given the following template

{
  "host": {{token.metadata.host}},
  "meta": {{token.metadata}}
}

A token created with --metadata=host=host-abc --metadata=somefield=blah could then have claims resembling:

{
  "host": "host-abc",
  "meta": {
    "host": "host-abc",
    "somefield": "blah"
  },
  "aud": ...,
  "exp": ...,
}

Describe alternatives you've considered

Alternative 1.
Each host could be a separate identity, however at our scale we would quickly exhaust the 250,000 identity storage limit

Alternative 2.
Create a secret-engine plugin that otherwise behaves the same as the Identity secret engine, but allows referencing token metadata. However, this would take a significant amount of maintenance for what would otherwise be a minor change.

Explain any additional use-cases

If updating the templating mechanism to allow token metadata, it could also be used for ACL templates, but in our case that is just a nice to have and can be a separate ticket.

@maxb
Copy link
Contributor

maxb commented Aug 15, 2023

The general issue raised in #7761 (comment) still applies... Both token display name and token metadata can often be set by relatively low privileged users, such as anyone given access to auth/token/create for the purposes of creating child tokens.

@jackadamson
Copy link
Author

That makes sense that in some (or many) environments, token metadata is relatively untrusted.

Do you think that this could be mitigated via documentation or naming, eg. having the field be named token.untrusted_metadata or similar?

@jackadamson
Copy link
Author

I'd also be happy if the change was just to claim metadata and not used for ACLs.

Then combined with renaming the field to token.untrusted_metadata, to prevent it from usage in authorization within vault, and communicating to users that it should not be trusted unless they adequately understand the consequences.

Do you think this could be possible avenue?

@VioletHynes
Copy link
Contributor

We discussed this internally, and this isn't something we're planning to support.

See also: #10460 + #10682

In particular, there are two parts of our responses to these that explain our position and hopefully offer an alternative. Hopefully the following answers satisfy you:

I think you can achieve the result you're aiming for by creating an entity and alias for each role that needs distinct policies, put the metadata into the entity, and bind the entity alias to the token role.

and

Thank you for the contribution. Unfortunately I'm afraid that we have decided to refuse it. We view this change as dangerous, since a user can create child tokens with whatever arbitrary metadata they like. This means policy templating using token metadata is vulnerable to any attacker who's aware of what policies are in use. I expect that you could come up with safeguards in your environment to protect against this (e.g. not granting access to the create token endpoints), but we as vault devs have to be vigilant against introducing features that allow for insecure by default behaviour.

Thanks for raising this, and we appreciate the contribution. However, we feel like the 'right' way to do this is using the identity system. Token metadata is quite low privilege.

@VioletHynes
Copy link
Contributor

All this being said, I've opened the discussion about specifically the OIDC use case with the team, and we'll be discussing that later today

@jackadamson
Copy link
Author

Thanks for the thorough and swift feedback.

Just for my own understanding, do you have any recommendations on how to include data about the host the OIDC claim was issued to?

Specifically for cases where the number of hosts is >250,000 (which to my understanding rules out a distinct identity per host due to storage limits.

One idea was to have an alias per host, but my later understanding was that an identity can have one alias per auth mount, so that wouldn't be feasible.

@jackadamson
Copy link
Author

Maybe an alternative would be to expose fields from req.Auth.InternalData, which as best as I can tell can be set by auth plugins, but not during manual token creation.

Do you know whether using that could be acceptable?

@austingebauer
Copy link
Contributor

austingebauer commented Aug 16, 2023

Hi @jackadamson. I don't think using req.Auth.InternalData would be the right approach. That data is meant to be internal to auth methods for token revoke and renew decisions.

I understand this could be less than desirable with the number of hosts you have, but have you considered created an identity token role per host? The host information could be a part of the claims template.

I understand the security concern around supporting token metadata in ACL policy path templating. I'm a bit undecided, but I don't see why it would be a security concern (from Vault's perspective) to allow templating token metadata into identity token and OIDC provider claims. These JWTs don't provide authn/authz to Vault. I suppose a malicious Vault token creator could generate a JWT that provides authorization to a different system, but that seems like it would require a good bit of Vault authorization and knowledge of how those claims are being used for authorization within the different system. Would you be able to give us a little more information on how you're using these identity token JWTs?

@jackadamson
Copy link
Author

Thanks for the response @austingebauer, I suspect that for our use case, creating an identity token role per host would be hard due to our high rate of host creation and deletion (well over 1000 hosts per day)

Completely understand the issues around usage of token metadata in ACL, and very happy leaving that out.

Our use case of the JWTs is for machine authentication to internal services. The claims that we want to use the token metadata would primarily be used for logging purposes, so we can track what individual hosts are accessing internal services, rather than just what class of host.

Do you think that allowing the token metadata in OIDC claims could be feasible? (possibly named token.untrusted_metadata as a further mitigation against users accidentally trusting the data too much)

@austingebauer
Copy link
Contributor

Thanks for the context, @jackadamson! That all makes sense to me. I'm curious which auth method you are using to create these tokens with the additional metadata?

I've brought this up internally for discussion. I don't have any concrete answers yet but will follow up with you.

@jackadamson
Copy link
Author

I highly appreciate it @austingebauer, in our case, we have an in-house auth plugin that uses certificate details from our VPN connections (with Nebula VPN)

@maxb
Copy link
Contributor

maxb commented Aug 26, 2023

Using token metadata in ACL policies, and using it in OIDC claims, are both expression of security properties. One is just a bit more indirect than the other. To me, it seems what is needed is to be able to refer to token metadata, with a guarantee that it came from a particular auth method or group of auth methods, or just a specific type of auth method. Though, the Vault templating language is currently a very simple thing, and it might be difficult to allow for that without a more sophisticated parser.

@jackadamson
Copy link
Author

Being able to refer to token metadata from a particular auth method sounds like an interesting proposal.

Possibly it could be presented similarly to how you can currently use identity info from a given auth mount {{identity.entity.aliases.auth_token_45ccd648.name}}

So perhaps it could be accessed with eg. {{token.AUTH_ACCESSOR.metadata.FIELD}}, which only exposes metadata set by the given auth mount?

I'm aware this would require changes beyond just the templating to track the information, so could be a large body of work.

(apologies if I am mixing up terminology regarding auth mounts/auth plugins/accessors)

@maxb
Copy link
Contributor

maxb commented Aug 28, 2023

I'm aware this would require changes beyond just the templating to track the information, so could be a large body of work.

Actually that part is pretty easy. The information is already tracked. Token metadata is only set at token creation time. The auth method that created a token is already saved into the token as the token's path attribute (in Enterprise, this needs to be qualified by the token's namespace ID).

The harder part IMO is figuring out a design that accounts for people having several of the same or similar auth methods mounted, and wanting to resolve token metadata from any within that set - whilst excluding others, such as the token auth method, which AFAIK is fairly unique in allowing the requester to supply their own metadata. One might imagine, for instance, a collection of kubernetes auth methods each bound to a different cluster, and wanting to refer to the role metadata set by any one of them.

@jackadamson
Copy link
Author

A few options which might suit:

A - Globbing

Allow specifying the auth accessor as a glob,
eg.

{{token.auth_kubernetes_*.metadata.somefield}}
{{token.auth_kubernetes_{aaaaaa,bbbbbb}.metadata.somefield}}

B - Specify in a list and filter nulls

eg. when templating a JSON document, assuming that the given token was issued by a mount with accessor auth_kubernetes_aaaaaa, which set `somefield = "somevalue"

{
  "somefield": [
    {{token.auth_kubernetes_aaaaaa.metadata.somefield}},
    {{token.auth_kubernetes_bbbbbb.metadata.somefield}}
  ]
}

renders to

{
  "somefield": [
    "somevalue"
  ]
}

C - Define a "coalesce" function

Syntax could be like

{{token.auth_kubernetes_aaaaaa.metadata.somefield || token.auth_kubernetes_bbbbbb.metadata.somefield}}

or

{{coalesce(token.auth_kubernetes_aaaaaa.metadata.somefield, token.auth_kubernetes_bbbbbb.metadata.somefield)}}

@jackadamson
Copy link
Author

Or maybe, token.metadata.somefield and token auth providers are just not accepted for providing metadata fields?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants