Advices on AppRole use with Ansible

Hi !

I set up a Vault server mainly to store secrets and to enable access to a dedicated server (an Ansible server, which can only access, read secrets and then use them inside a playbook).

I manually succeed to create a Policy, an AppRole and link them together from vault CLI.

My policy is quite easy, it just allows read and list capabilities on a path.
My AppRole is quite easy too as it just restrict the use from a single IP address (my ansible server) and set token ttl to 1h.

Then, i’m manually able to retrieve RoleID, write a SecretID and then generate a token that i can use from Ansible server, inside a playbook.

Here’s some interrogation :
At the moment, to generate token for my AppRole, i must manually connect to my Vault server (and use root token which isn’t a good thing i guess) then launch those commands :

vault read auth/approle/role/My-approle/role-id
vault write -force auth/approle/role/My-approle/secret-id

vault write auth/approle/login role_id="xxxxxx" secret_id="YYYYYYY"

First of all, is it possible to avoid to use root token to authenticate to my vault server ?

In a second time, is it possible to automatically retrieve (if allowed) an approle token from my Ansible server ?

Thanks a lot for your help and ideas.

Gael

Hi @motorbass,

Yes, you’re right. You shouldn’t be using your root token for normal daily operation. Ideally, you should have an admin role with create/read/update/delete/list/patch/sudo access to most configuration endpoints but not all of them (which is essentially what the root token’s policy does).

Now, you may want to start with a restricted admin policy and add capabilities to it as you go (you’ll need the root token for that) or just try to figure out 99% of all possible use-cases that you may have and configure that policy right from the start.

Check out this great Vault policy guide for more info.

Specifically about your use case above, to generate a new secret-id for your AppRole, you’ll need to know its role_name and have the permissions create and update on the path /auth/approle/role/:role_name/secret-id.

If you’re gonna be managing multiple AppRoles, you may want to include the below in your admin policy (you may also want to tweak it a little bit):

# List all available AppRoles
path "auth/approle/role" {
  capabilities = ["list"]
}

# Manage all AppRoles and Secrets IDs
path "auth/approle/role/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

You may also consider Secret ID response wrapping as described in this blog for a more secure approach (but it’s not mandatory for this to work).

Once you have the role-id and the secret-id, the app that will be authenticating against Vault (Ansible), will need to POST those to /auth/approle/login to get a client token (Reference).

One way of achieving that is to run Vault agent on your Ansible server with auto-auth to retrieve the client token and store it somewhere locally where Ansible would have access to.

Alternatively, you could try adding Vault lookups to your playbooks.

1 Like

Hi @macmiranda
Wow that is a great explanation, thanks a lot , it’s really clear, thanks a lot !

(One thing i may not understand 100% is that, token retrieved by an approle should still be generated manually ? for instance i create an admin role which i retrieve a token to make common action. i still have to get it manually before login into vault)

Just a confirmation about the Secret ID part: as the SecretID act as a password, and as you need to “write” it once you get the RoleID, does it mean that I have to “write” it thanks to admin token/account in order to retrieve an approle token that i can use in app ?
What i mean is, i still need a manual part to generate token for my approle ?

I usually use “lookup” in Ansible, but the auto-auth feature with Vault agent seems quite interesting, i wasn’t aware of that.

You can think of the role-id and the secret-id as an OAuth app’s client_id and client_secret. They basically tell Vault, if a client is authenticating using these IDs, it’s ok to issue them a token (with the configured policy attached to it). You still need to store those IDs secretly even though they are not the token that you’ll be using in your following requests.

The token that is generated by the login operation has a few constraints that may require you to re-login (after a while). They are:

That’s something the Vault agent manages automatically for you. It’s able to renew a certain token until it reaches token_max_ttl and re-authenticate using the role-id and secret-id though you may need to set remove_secret_id_file_after_reading to false (I’m not particularly endorsing this though).

1 Like

You only need the second part of this policy, that covers listing AppRoles on its own, without the first part.

I would suggest also that you don’t include sudo in this policy, as it is redundant as well. The function of sudo is to make policy authors take extra notice when enabling access to special endpoints, so it loses its value if you add it in places where it does nothing.

1 Like

Think of it this way: The RoleID is a username. The SecretID is a password. All the AppRole auth method is really, is fancy usernames and passwords. Specifically, built-in ways to have the usernames and passwords auto-generated as random data, and support for multiple passwords on the same account. (Every time you write to the auth/approle/role/<rolename>/secret-id endpoint, a new password/SecretID is created. Do be aware of this, otherwise you can unwittingly end up with thousands of valid credentials, which is bad security hygiene.)

This means that something in your overall Ansible environment is going to have to perform an auth/approle/login operation when it starts running, use the resulting token for the duration of the playbook, and then discard it.

Bear in mind that the default kind of Vault token uses a small amount of storage on the Vault server side, until it expires. Perhaps not a big deal at first - but if you start having hundreds of playbook runs per hour, it can become a problem. In which case, revoke your tokens when they are no longer required, or use batch tokens (which are cryptographically signed certificates that aren’t stored on the Vault server side at all).

1 Like

Hi @maxb thanks a lot for all those details.

I better understand that my ansible server should authenticate first to Vault, and if authentication is successful, Vault give it a token corresponding to the AppRole (actually i’m able to do this/test manually at the moment)

I succeed to do it with curl cli and parsing with jq, it works pretty well.

The main interrogation, is how to manage the SecretID.
I mean, basically I got my playbook hosted on GitLab, with empty RoleID/SecretID. So it means everytime i want to execute it (after cloning) i have to tell Ansible to write a new SecretID, (thanks to my admin role for example) before requesting a token, and this step should be automatic ?

  • Should you generate a new SecretID (password) for each operation?

  • Or just do it once and re-use it forever?

That’s entirely up to you - whatever suits your environment and security properties.

I normally like to separate the paths. One could use a single auth/approle/* path though.
If you need to LIST auth/approle/role (no trailing slash), the path auth/approle/role/* isn’t enough.

Can’t remember why I thought it was needed for tidying up secret-ids but, in any case, it would have a different path: /auth/approle/tidy/secret-id. Will remove it from the original comment. Thanks.

1 Like

But it is, though, because Vault implicitly and automatically forces a trailing slash on all list operations before they reach the policy layer. (And then it also tries chopping the trailing slash off again if no policy matches… but it only does this if the path is matched without + wildcards… which can be very confusing.)

Here is some more details I wrote previously about this topic: Documentation: unclear meaning of "list" capability in policy page · Issue #13011 · hashicorp/vault · GitHub

1 Like

:scream:

And no one at Hashicorp thought for a second of documenting that!?

Sadly, more often than I would like, the best documentation for Vault is reading the source code itself.

@macmiranda @maxb thanks a lot for your insights and your help guys, i do really appreciate !

Week end is over and everything is working (and almost crystal clear to me) on monday :slight_smile:

1 Like

@macmiranda @maxb just dig up once again this topic cause after all, some stuff still not clear to me.

At the moment here’s how Vault is configured and used :
2 approle are created, “admin-approle” and “ansible-approle” and are both associated to policy “admin-policy” and “ansible-policy”

The admin stuff is here to manage kv/approle without being root (so without using root token), its policy is configured that way:

# Manage all kv within mount
path "kv/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
# Manage all AppRoles and Secrets IDs
path "auth/approle/role/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

Also, approle has been created like this :

vault write auth/approle/role/admin \
    token_policies="admin-policy" \
    token_ttl=720h \
    token_max_ttl=720h \

Then, the ansible stuff is here to allow only one server to read and retrieve secrets and it’s configured that way :

vault policy write ansible-policy -<<EOF
# Read-only permission on secrets stored at 'kv/data/blablabla'
path "kv/data/blablabla" {
  capabilities = [ "read", "list"]
}
EOF

#AppRole to be used only by ansible server
vault write auth/approle/role/ansible-approle \
    token_policies="ansible-policy" \
    token_ttl=720h \
    token_max_ttl=720h \
    secret_id_bound_cidrs="X.X.X.X/32" \
    token_bound_cidrs="X.X.X.X/32" \

At the moment everything run fine manually, it means I use admin token to perform these command to retrieve a token within the ansible-approle and then use the token inside my ansible playbook.

vault read auth/approle/role/ansible-approle/role-id
vault write -force auth/approle/role/ansible-approle/secret-id
vault write auth/approle/login role_id="XXXXXXX" secret_id="YYYYYYY"

Here’s some question where i need advices :

1st question : i set 720h so 30 days for token duration on both policy, but sometimes it doesn’t last more than few days and i got a permission denied when i want to use it. Does a token is like “implicitly” revoked once someone renew it for instance ?

2nd question : when i manually do the 3 commands above, there’s a VAULT_TOKEN env variable in my server, is it ok to say that this token never expires so it means i won’t have to manually perform these operations to retrieve admin token everytime ?

3rd question: i’m trying to- include the 3 commands above inside my playbook to automatically retrieve token in my playbooks , at the moment i use this which works pretty well except when token are outdated, it means playbook have to be manually updated.

vault_var: "{{ lookup('community.hashi_vault.vault_kv2_get', 'my_secret', engine_mount_point='kv/', url='https://vault-xxxx:8200/', token='aaaaabbbbbbb') }}"

secret_value: "{{ vault_var.secret.my_secret }}"

is it a good or bad way to say : VAULT_TOKEN env var has to be set with admin token , and then, i may add within my playbook the 3 commands above to automatically authenticate and retrieve token using ansible-policy ?

(In a general way, i’m not 100% confident about the token management I set (token time to live and token max time to live) and also how to securely deal with token/approle in an ansible context)

(PS: also tried to set up vault agent but i prefer to becomemore confident in my token management first of all)

Thanks a lot for your advices dudes !

This policy is incredibly powerful. It lets the owner create new AppRoles and assign them any named policy that exists in Vault. It’s not quite as powerful as having a root token, but if there is any other highly privileged policy defined - such as one that allows configuration of policies themselves - this is enough to allow escalation to higher privileges.

list is ineffective/irrelevant on a KVv2 data subpath.

No.

This question doesn’t make sense. The commands in question do not store a token into VAULT_TOKEN. Neither do they save the token they obtain anywhere.

It is good to have credentials provided as environment variables, or external files, or just about anything rather than hardcoded in the playbook source code.

It is bad to do this because you have already given your playbook a Vault token - what is the point of having the playbook use one Vault token to get another Vault token, with an AppRole in the middle just for extra complexity?

On top of everything else, this command:

creates a new secret ID (i.e. password-equivalent) every single time you run it, and by default these last forever and are never cleaned up.

Consider this: would you want to use a banking app which let you re-use any past security code again as many times as you wanted to? Of course not - this is the equivalent of that.

Based on what you have shared to this point, I think what you should actually do is:

  • Create one AppRole only
  • Give it the policy you want your Ansible script to use
  • Manually, using whatever highest administrative login you have configured on your Vault server, retrieve the role_id, generate just one secret_id, and store these on your Ansible server
  • Write a wrapper script that uses those to log in to Vault, outside of Ansible, and publish the token to Ansible via the VAULT_TOKEN environment variable or ~/.vault-token file
  • Do not specify any Vault credentials inside the Ansible code at all. Let it pick up the token from the above-mentioned default locations.

I’d strongly suggest creating a new thread for further questions. The old thread was 4 months ago, I certainly don’t remember the details, and I don’t want to wade through all that history - it would be better if you asked new questions in a new thread, including whatever edited context is relevant to the question.

First of all thanks of lot once again for your precious help, (and sorry me for updating this thread instead of create a new one.)

Some settings become clearer. By reading some articles and after reading again my old posts, i was convinced that if i wanted to use an approle, i have to get a role-id/secret-id everytime, that was false.

Now i understand the thing to store a vault token based on the policy i need (and so, generated from a script with role-id/secret id combination )

I understand the “Vault philosophy” , by reading again my previous post (and my morning work) I was going into too much complexity. It’s (almost) crystal clear !
Once again thank you very very much @maxb !