mbieren
mbieren

Reputation: 1120

HashiCorp Vault AppRole based authentication Unwrap secret_id got permission denied

I am using this code as an example to use AppRole based authentification to Vault. For the secret_id I wanna use an wrapped token to be more secure

    import unittest
    from hvac import Client

    URL = "https://p.vault.myfine-company.de"
    JENKINS_TOKEN = "mylovelytoken"

    def test_ci_startup(self):

      # Jenkins authentifies with token as secure instance
      jenkins_client = Client(url=URL, token=JENKINS_TOKEN)

      # fetch the role_id and stores this somewhere in the image of the app
      resp = jenkins_client.auth.approle.read_role_id(role_name='workshop')
      role_id = resp["data"]["role_id"]

      # get a wrapped secret_id and passes this to the starting app
      result = jenkins_client.write(path='auth/approle/role/workshop/secret-id',wrap_ttl="2s")
      unwrap_token = result['wrap_info']['token']

      # No the app comes in place
      app_client = Client(url=URL) # , token=JENKINS_TOKEN)

      # unwrap the secret_id
      unwrap_response = app_client.sys.unwrap(unwrap_token) # !!! Here I get permission denied
      secret_id = unwrap_response['data']['secret_id']

      # use role_id and secret_id to login
      login_result = app_client.auth.approle.login(role_id=role_id, secret_id=secret_id)
      client_token = login_result['auth']['client_token']

      # Read the database credential
      read_response = app_client.secrets.kv.v2.read_secret_version(path='test/webapp')
      self.assertEqual("users", read_response['data']['data']['db_name'])

      return

Unfortunatly when try to unwrap the secret_id with app_client.sys.unwrap(unwrap_token) there is an 403 "permission denied" When I use the app_client-Connection with app_client = Client(url=URL), token=JENKINS_TOKEN) everything works fine. But this of course this not the way the AppRole based authentication should be used. All this is bases on the following Tutorials and Best Practices :

https://developer.hashicorp.com/vault/tutorials/recommended-patterns/pattern-approle https://developer.hashicorp.com/vault/tutorials/auth-methods/approle?in=vault%2Fauth-methods

I think is somewhat related to policies. But I did not find the solution yet.

Upvotes: 0

Views: 1815

Answers (1)

mbieren
mbieren

Reputation: 1120

Bash window 1:

$ export VAULT_ADDR="https://p.vault.myfine-company.de"
$ export VAULT_TOKEN="my-fine-token"
$ # This creates a secret 
$ vault kv put secret/mysql/webapp db_name="users" username="admin" password="passw0rd" 
$ # this writes the policy to access the secret
$ vault policy write jenkins -<<EOF\n# Read-only permission on secrets stored at 'secret/data/mysql/webapp'\npath "secret/data/mysql/webapp" {\n  capabilities = [ "read" ]\n}\nEOF\n
$ # This creates an approle jenkins
$ vault write auth/approle/role/jenkins token_policies="jenkins" \\n    token_ttl=1h token_max_ttl=4h\n
$ # this reads the role-id
$ vault read auth/approle/role/jenkins/role-id
  Key        Value
  ---        -----
  role_id    fcff5e13-wonderfull-my-fine-role-id
$ This creates a wrapping token with TTL 120s 
$ vault write -wrap-ttl=120s -force auth/approle/role/jenkins/secret-id
  Key                              Value
  ---                              -----
  wrapping_token:                  hvs.wonderfull-msgiIp9nu91c1fLrcwGh4KHGh2cy5HMmw4bkh2-my-fine-wrapping-token
  wrapping_accessor:               ddXZLPFmy-fine-accessor
  wrapping_token_ttl:              2m
  wrapping_token_creation_time:    2022-11-23 12:19:46.958503493 +0000 UTC
  wrapping_token_creation_path:    auth/approle/role/jenkins/secret-id
  wrapped_accessor:                5superp-6-my-fine-wrapped-accessor

role_id and wrapping_token is now used in bash window 2:

$ # unwrap token to obtain the secret-id
$ VAULT_TOKEN=hvs.wonderfull-msgiIp9nu91c1fLrcwGh4KHGh2cy5HMmw4bkh2-my-fine-wrapping-token vault unwrap
  Key                   Value
  ---                   -----
  secret_id             ac8b3594-my-wundervolles-secret-id
  secret_id_accessor    485423my-wundervolles-secret-accessor
  secret_id_ttl         0s
$ # login to vault and get a APP_TOKEN to be used to read secrets
$ vault write auth/approle/login role_id=fcff5e13-wonderfull-my-fine-role-id secret_id=ac8b3594-my-wundervolles-secret-id
  Key                     Value
  ---                     -----
  token                   hvs.CAESIEW-so-a-nice-token-lEgXb9ZIf-my-wonderfull-token
  token_accessor          MehfK-my-wonderfull-accessor
  token_duration          1h
  token_renewable         true
  token_policies          ["default" "jenkins"]
  identity_policies       []
  policies                ["default" "jenkins"]
  token_meta_role_name    jenkins
$ export APP_TOKEN=hvs.CAESIEW-so-a-nice-token-lEgXb9ZIf-my-wonderfull-token
$ # Read a secret
$ VAULT_TOKEN=$APP_TOKEN vault kv get secret/mysql/webapp
  ====== Secret Path ======
  secret/data/mysql/webapp
  ======= Metadata =======
  Key                Value
  ---                -----
  created_time       2022-11-23T09:44:24.429733013Z
  custom_metadata    <nil>
  deletion_time      n/a
  destroyed          false
  version            1
  ====== Data ======
  Key         Value
  ---         -----
  db_name     users
  password    passw0rd
  username    admin

The advantage with this wrapped_token is that the CI Pipeline passes this short lived token to the app. The app uses this token to retrieve the secret-id (by unwrapping it) and performs the login together with the role-idto obtain the access token to read the database secret.

Solution :

The Problem is solved by providing the unwrap_token to app_client = Client(url=URL) token=unwrap_token as mentioned in a still open pull-request to hvac (as of 2022-11-30) :

import hvac

client = hvac.Client(url='https://127.0.0.1:8200')
client.token = "hvs.wonderfull-msgiIp9nu91c1fLrcwGh4KHGh2cy5HMmw4bkh2-my-fine-wrapping-token"

# When authenticating with just the wrapping token, should not pass token into unwrap call
unwrap_response = client.sys.unwrap()
print('Unwrapped approle role token secret id accessor: "%s"' % unwrap_response['data']['secret_id_accessor'])

Upvotes: 0

Related Questions