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

request.tokenentry.meta should be accessible in role.template during oidc token creation #11803

Open
dlamotte opened this issue Jun 9, 2021 · 4 comments

Comments

@dlamotte
Copy link

dlamotte commented Jun 9, 2021

Is your feature request related to a problem? Please describe.

Multiple secret-id's per approle could be more useful if a secret-id's meta was exposed so that JWTs could encode details from the secret-id meta.

Describe the solution you'd like

I'd like to hand a service an role-id+secret-id that was minted with a specific metadata field such that future requests to vault with the token could have secret-id specific authorization.

that could be enforced by applications based on the contents of the JWT. ie: JWT with field scope: /objects/subset1 which ensures clients with the JWT cannot modify all /objects but only objects below subset1 (the rules here are opaque and application dependent).

There are two aspects here:

  • secret-id.meta in policy (a specific approle/secret-id combo gets a unique policy to it which could limit access within vault)
  • secret-id.meta in JWTs via an oidc role template accessing secret-id.meta (services receiving the JWT could authorize behavior based on the secret-id.meta)

The initial attempt here utilized querying into entity aliases in templates like identity.entity.aliases[$accessor].metadata[$secret-id-field] but this was found to work only intermittently (see #11798).

Describe alternatives you've considered

It seems the alternative is creating an approle per scope with a policy/role.template associated specifically with it.

The issue here is that approle/secret-id scale 1:1 with each other here vs the ideal solution is a single approle with a secret-id per variation.

Explain any additional use-cases

Any use cases where applications authenticating to vault with secret-id's that have some limited scope within an approle.

Additional context

Terraform to setup a vault server -dev:

resource "vault_identity_oidc" "server" {
  issuer = "http://localhost:8200"
}

resource "vault_identity_oidc_key" "key" {
  name      = "key"
  algorithm = "ES256"
}

resource "vault_identity_oidc_role" "role" {
  name = "role"
  key  = vault_identity_oidc_key.key.name
  ttl  = 86400

  template = <<EOF
{
  "groups": {{identity.entity.groups.names}},
  "nbf": {{time.now}},
  "alias.metadata.hello": {{identity.entity.aliases.${vault_auth_backend.app.accessor}.metadata.hello}},
  "entity_data": {
    "id": {{identity.entity.id}},
    "name": {{identity.entity.name}}
  }
}
EOF
}

resource "vault_identity_oidc_key_allowed_client_id" "role" {
  key_name          = vault_identity_oidc_key.key.name
  allowed_client_id = vault_identity_oidc_role.role.client_id
}

resource "vault_auth_backend" "app" {
  type = "approle"
  path = "app"
}

resource "vault_policy" "app" {
  name   = "app"
  policy = <<EOF
path "identity/oidc/token/role"
{
  capabilities = ["read"]
}
EOF
}

resource "vault_approle_auth_backend_role" "app" {
  backend        = vault_auth_backend.app.path
  role_name      = "role"
  token_policies = [vault_policy.app.name]
}

resource "vault_identity_entity" "app" {
  name     = "role"
  policies = ["default"]
}

resource "vault_identity_entity_alias" "app" {
  name           = vault_approle_auth_backend_role.app.role_id
  mount_accessor = vault_auth_backend.app.accessor
  canonical_id   = vault_identity_entity.app.id
}

resource "vault_approle_auth_backend_role_secret_id" "app" {
  backend   = vault_auth_backend.app.path
  role_name = vault_approle_auth_backend_role.app.role_name

  metadata = <<EOT
{
  "hello": "world"
}
EOT
}

resource "vault_approle_auth_backend_role_secret_id" "app2" {
  backend   = vault_auth_backend.app.path
  role_name = vault_approle_auth_backend_role.app.role_name

  metadata = <<EOT
{
  "hello": "world2"
}
EOT
}

After running vault server -dev:

export VAULT_ADDR='http://127.0.0.1:8200'
terraform init
terraform apply
$EDITOR terraform.tfstate # look for role_id/secret_id to use for auth, save them
vault write auth/app/login role_id=... secret_id=...
export VAULT_TOKEN=...
vault read identity/oidc/token/role
jwt decode ...
# token I saw
Token header
------------
{
  "alg": "ES256",
  "kid": "0d82af57-b5de-f643-a9ca-c22f347e3407"
}
Token claims
------------
{
  "alias.metadata.hello": "world",
  "aud": "UFgEjAkJuqeK74JOTgZfVXiyQE",
  "entity_data": {
    "id": "09311491-6d57-e4ac-0b59-8a9c44a13dd7",
    "name": "role"
  },
  "exp": 1621102577,
  "groups": null,
  "iat": 1621016177,
  "iss": "http://localhost:8200/v1/identity/oidc",
  "namespace": "root",
  "nbf": 1621016177,
  "sub": "09311491-6d57-e4ac-0b59-8a9c44a13dd7"
}

Unfortunately, accessing in this way "alias.metadata.hello": {{identity.entity.aliases.${vault_auth_backend.app.accessor}.metadata.hello}}, appears to yield intermittent results whereas I assume if tokenentry.meta were exposed, it could be identity.tokenentry.meta.hello (or some similar pattern).

@UXabre
Copy link

UXabre commented Jan 6, 2022

Hi, if I understood correctly; you want the token's metadata to be accessible in the template; meaning, the metadata that was copied from the OIDC auth backend?
If so, you're having the same issue as I have and for which I actually created a PR (which was rejected) which did just that what you wanted. However, it was rejected because it is apparently possible to create child tokens which for which one could set their own metadata. A knowledgable person could then figure out which metadata does what in these templated policies.

But it is an alive and well enough topic that this does not mean the end of that; but, the concept needs to be fleshed out better I guess.
Proposed solutions (but rejected):

  • Block the access to the token creation endpoint; but this means that one could forget to do so and thus leave a wide security hole
  • Limit the interpolation only to tokens that are "parents" and thus ignore "child" tokens; but this gives different meanings to the same concepts

Perhaps we could:

  • Add an identifier to a token generated by an auth backend to mark it as "authorative", meaning the metadata is trustworthy and could be interpolated
  • Other ideas from your end?

If my assumption is wrong that token metadata is what you'd like to see interpolated, please, if you could elaborate what it is you'd like, then perhaps that different point-of-view could be the key to this problem

@ncabatoff
Copy link
Collaborator

Hi @dlamotte,

As you saw in #11798, trying to use different metadata for different secretids is unreliable, and the Vault team decided that we don't want different secretids for the same approle to be treated differently or to have different permissions.

Can you explain more about why

It seems the alternative is creating an approle per scope with a policy/role.template associated specifically with it.

is not a good solution for you? The intent of an approle role is that it will only be shared by different instances of the same application, which to my mind means that all of them will need the same permissions.

@dlamotte
Copy link
Author

dlamotte commented Feb 1, 2022

@UXabre ya, we're aligned AFAICT.

@ncabatoff It's simply that it becomes a maintenance burden. You can imagine many instances of the similar class but scoped to its local resources. That's parameterized by the name of the instance. ie: instance ABC can only access resources below path/ABC/* but the patterns of usage match because they are all instances of the same class (I'm speaking very generically here). Since this cannot be templated as such, it means for each instance, we must manage the lifecycle of a correlated approle and policy dedicated specifically for the instance. It means that destruction of the instance also mandates destruction of the correlating vault resources or it leaks. The extent of the resources that need to be managed is what is in discussion here. Should a secretid alone be enough to be deleted? Or is it a secretid + approle + policy? It also means that whatever is controlling the lifecycle of the instance also needs elevated privileges in order to create/manage approles and policies whereas with this it would only need secretid on a specific role.

I can see your point about the loophole here. And I'm not too familiar with vault internals, so forgive me... but perhaps there should be clear delineation between "server-side metadata" and "client-side metadata". Server-side metadata can always be trusted and used in policy, but client-side metadata cannot be trusted (but may be useful for other reasons) and therefore cannot be used in policy.

@UXabre
Copy link

UXabre commented Feb 2, 2022

@dlamotte To be complete, I'll link to a closed PR of mine as this also has the same discussion topic: #10682
Perhaps this might give some new ideas or at least shows what wasn't deemed a "possible solution".
I myself kinda struggle to understand the use case of sub-token metadata and as what I understand, this might be the only thing that one uses as auhthorization (but then again, those might not be the people that are going to need variable interpolation in their policies).
Anyway, perhaps there should be a way to configure an auth backend to flag tokens as interpolatable or not (by default not) and check in the code to only allow an interpolatable token to be used in policies.
This does gives some extra work towards patching all auth methods that would be needed but as it's off by default it shouldn't be needed for all.

Back to you @ncabatoff (sorry if I just ran in a big circle in my comment above as you metioned that you're not a fan of making distinctions between tokens)

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

No branches or pull requests

5 participants