rundeck-plugins / vault-storage

Storage plugin for Rundeck for storing data in Vault
http://rundeck.org/plugins/vault/2017/11/25/vault-storage.html
MIT License
18 stars 11 forks source link

AppRole configuration example #15

Closed rlueckl closed 3 years ago

rlueckl commented 5 years ago

Hi,

I need some help configuring this plugin for authentication with AppRoles. The following config with token auth works great:

rundeck.storage.provider."1".type = "vault-storage"
rundeck.storage.provider."1".path = "keys"
rundeck.storage.provider."1".config.prefix = "all/qa/test"
rundeck.storage.provider."1".config.secretBackend = "custom/path/secrets"
rundeck.storage.provider."1".config.address = "https://******:8200"
rundeck.storage.provider."1".config.storageBehaviour = "vault"

rundeck.storage.provider."1".config.token = "******"

But no matter what I do, AppRole based auth doesn't work. As far as I understood, the config should look something like this:

rundeck.storage.provider."1".type = "vault-storage"
rundeck.storage.provider."1".path = "keys"
rundeck.storage.provider."1".config.prefix = "all/qa/test"
rundeck.storage.provider."1".config.secretBackend = "custom/path/secrets"
rundeck.storage.provider."1".config.address = "https://******:8200"
rundeck.storage.provider."1".config.storageBehaviour = "vault"

rundeck.storage.provider."1".config.authBackend = "approle"
rundeck.storage.provider."1".config.approleId = "APPROLE_ID"
rundeck.storage.provider."1".config.approleSecretId = "SECRET_ID"
rundeck.storage.provider."1".config.approleAuthMount = "???"

APPROLE_ID is from: $ vault read auth/foobar/approle/role/rundeck/role-id SECRET_ID was generated with: $ vault write -f auth/foobar/approle/role/rundeck/secret-id

The AppRole is restricted to the IP of the Rundeck host and uses a policy which is only able to read and list all/qa/*:

$ vault read auth/foobar/approle/role/rundeck 
Key                      Value
---                      -----
bind_secret_id           true
bound_cidr_list          [10.*.*.*/32] # IP censored for safety reasons
local_secret_ids         false
period                   0s
policies                 [policy-qa-lr]
secret_id_bound_cidrs    [10.*.*.*/32] # IP censored for safety reasons
secret_id_num_uses       0
secret_id_ttl            0s
token_bound_cidrs        <nil>
token_max_ttl            30m
token_num_uses           10
token_ttl                30m
token_type               default

The policy looks like this:

path "custom/path/secrets/all/qa"   { capabilities = [ "list" ] }
path "custom/path/secrets/all/qa/*" { capabilities = [ "list", "read" ] }

As you can see we use a custom path in vault for AppRoles (auth/foobar/approle/role). I've tried putting different parts of this into approleAuthMount (auth/foobar, auth/foobar/approle), but it still doesn't work. I get the following exception every time:

2019-07-15 12:58:14.590 ERROR --- [tp1975736398-26] StackTrace                               : Full Stack Trace:

java.lang.NullPointerException: null
    at io.github.valfadeev.rundeck.plugin.vault.VaultStoragePlugin.hasDirectory(VaultStoragePlugin.java:300)
    at org.rundeck.storage.impl.DelegateTree.hasDirectory(DelegateTree.java:46)
    at org.rundeck.storage.conf.SubPathTree.getPath(SubPathTree.java:118)
    at org.rundeck.storage.conf.TreeStack.getPath(TreeStack.java:71)
    at org.rundeck.storage.impl.DelegateTree.getPath(DelegateTree.java:51)
    at org.rundeck.storage.conf.ListenerTree.getPath(ListenerTree.java:57)
    at org.rundeck.storage.impl.DelegateTree.getPath(DelegateTree.java:51)
    at org.rundeck.storage.conf.ConverterTree.getPath(ConverterTree.java:78)
    at org.rundeck.storage.impl.DelegateTree.getPath(DelegateTree.java:51)
    at org.rundeck.storage.conf.ConverterTree.getPath(ConverterTree.java:78)
    at org.rundeck.storage.impl.DelegateTree.getPath(DelegateTree.java:51)
    at com.dtolabs.rundeck.core.storage.AuthRundeckStorageTree.getPath(AuthRundeckStorageTree.java:131)
    at com.dtolabs.rundeck.core.storage.AuthRundeckStorageTree$getPath$0.call(Unknown Source)
[...snip...]

(Full trace attached -> rundeck_approle_exception.log)

rlueckl commented 5 years ago

Okay, after some digging around in the Vault source code I've found the answer here: https://github.com/BetterCloud/vault-java-driver/blob/master/src/main/java/com/bettercloud/vault/api/Auth.java#L411

approleAuthMount has to contain the path to the AppRoles including "approle" (in the very least), but without "/v1/auth" or anything else in front of it!

So basically if you have a custom setup (like we do) and your AppRole is auth/foobar/approle/role/rundeck (/v1/auth/foobar/approle/role/rundeck if you're using the API) then you have to set approleAuthMount to foobar/approle.

@gschueler : maybe you could add this info to the readme so the next person doesn't waste 2 hours reading Java source code while trying to configure the plugin. Generally more extensive descriptions and more examples would be nice... For example: how secretBackend + prefix + path come together to form the final path in Vault.

ltamaster commented 5 years ago

Hi @rlueckl,

I have been trying to use the plugin with approle authentication and it worked for me. But I noticed that the approle authentication generate a token that lives by token_num_uses calls. So in my case, I just was able to check the key storage page ones, and any other call to the key storage gives me an error (permission deny). Did you get this problem?

I suppose that the parameter can be configurated (not very familiar with it). I would like to know further about your use case, Do you normally refresh the token generated by the approle login?

Thanks Luis

rlueckl commented 5 years ago

Hi @ltamaster,

you can configure the number of uses in your AppRole (see my example above: token_num_uses = 10). I don't know how often plugin refreshes the token, but it seems to work for now. After token_num_uses or token_max_ttl have been reached (whichever comes first) a new token has to be generated (by logging in again with the AppRole).

I don't know if the plugin can detect this. For example: the plugin has been using token X for some time. Then it gets a 403 from Vault, because the token expired. The plugin could retry the request, get another 403 again or it could try to get a new token by logging in with the approle again (and then retry the request).

ltamaster commented 5 years ago

Hi @rlueckl ,

Yes, I think the plugin needs a way to refresh the token if it is expired. I will work on that and I will add what you mentioned about the docs.

Luis

rlueckl commented 5 years ago

Thank you very much!

linuxmail commented 5 years ago

hi,

we have also a similar setup:

rundeck.storage.provider.'1'.type = 'vault-storage'
rundeck.storage.provider.'1'.path = 'keys'
rundeck.storage.provider.'1'.config.prefix = 'rundeck'
rundeck.storage.provider.'1'.config.address = 'https://fra-corp-vault.example.com'
rundeck.storage.provider.'1'.config.storageBehaviour = 'rundeck'
rundeck.storage.provider.'1'.config.secretBackend = 'kv'
rundeck.storage.provider.'1'.config.approleId = '8...'
rundeck.storage.provider.'1'.config.approleSecretId = '...df'
rundeck.storage.provider.'1'.config.approleAuthMount = 'approle'
rundeck.storage.provider.'1'.config.authBackend = 'approle'
rundeck.storage.provider.'1'.config.engineVersion = 2
rundeck.storage.provider.'1'.removePathPrefix=true

After 30 minutes, I have create a new secred_id on the vault host:

$ vault write -f auth/approle/role/rundeck/secret-id
Key                   Value
---                   -----
secret_id             7....
secret_id_accessor    6...85a

then copy the secret_id and replace the value rundeck.storage.provider.'1'.config.approleSecretId with the new one and do a service rundeck restart, so that Rundeck has access again. So it seems, I'm doing it in the wrong way :-) because a Rundeck restart takes a bit time (there is no reload). Is there any workaround for it ?

cu denny

rlueckl commented 5 years ago

Hi @linuxmail,

You don't need to generate a new secret ID. Just restart Rundeck. That's enough to trigger a new login to Vault and the freshly generated token can be used again for as long as your approle TTL or num_uses are configured.

linuxmail commented 5 years ago

hi,

hmm, Ok, I would try it out, but then, I have to restart rundeck every hour :-) I tried a small script to solve it:

curl --output /dev/null -s --request POST --data '{"role_id":"eee-ffff-aaaa-bbbb-ccccdddeeeeffff","secret_id":"2eaaa-aaa-cccc-dddd"}' https://fra-corp-vault-01.example.com:8200/v1/auth/approle/login

For the Vault app role:

 vault read  auth/approle/role/rundeck
Key                        Value
---                        -----
bind_secret_id             true
local_secret_ids           false
policies                   [rundeck]
secret_id_bound_cidrs      [192.168.1.10/32]
secret_id_num_uses         0
secret_id_ttl              2h
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              1h
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [rundeck]
token_ttl                  1h
token_type                 default

but ... it has not the same affect ... As I understand .. role id and secret id just username and password for login into Vault and have access to the tokens. In that case, to have the new tokens in my curl doesn't help the Vaul Rundeck plugin. Is there a way, to push the new token somewhere to the plugin or inject it ?

rlueckl commented 5 years ago

2 things:

If you use the first method you don't need a new role_id or secret_id (ever). You just need a new token every time token_max_ttl or token_num_uses have been reached.

The Rundeck Vault plugin doesn't check if the token has expired and doesn't get a new token automatically. The easiest way to get a new token is to restart Rundeck, so the plugin has to login again to Vault and get a new token. I don't know if it's possible to push/inject the token to the plugin (probably not).

If you use the second method (inputting a token directly into the config) you have to generate the token and put into the config then restart Rundeck every time the token has expired.

You could set your approle to have a longer token_max_ttl (depending on your security requirements) so you don't have to restart Rundeck that often. But a better solution would be if the plugin would check for an expired token and automatically log in again if the token is expired.

linuxmail commented 5 years ago

hi @rlueckl

after a few days and trying .. no success. I let Rundeck restarting every 30min via Cron (service rundeckd restart) and if I visit the keystorage (for example) on the next day .. Rundeck has no access anymore. The only way to get it working again, is to recreate the secred_id.

I don't know, if it helps to recreate the approle for rundeck, because I think, it always has a ttl for the secred_id.

linuxmail commented 5 years ago

hi, I've deleted the approle and did a recreate:

Key                        Value
---                        -----
bind_secret_id             true
local_secret_ids           false
policies                   [rundeck]
secret_id_bound_cidrs      [192.168.43.46/32]
secret_id_num_uses         0
secret_id_ttl              0s
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              0s
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [rundeck]
token_ttl                  12h
token_type                 default

Looks better now. Hopefully it works on the next day too :-)

rlueckl commented 5 years ago

Sorry, I didn't have time to test the plugin in the last weeks. Maybe @ltamaster can help here? (RE: token renewal)

linuxmail commented 5 years ago

hi,

I can tell you: it works. I created a cron Job that is restarting Rundeck after several hours and I don't have to do anything further. The only important thing you have to keep in mind: don't let Rundeck restart, while a job is running.

rlueckl commented 5 years ago

Hi @ltamaster,

any new about the token refresh functionality? (https://github.com/rundeck-plugins/vault-storage/issues/15#issuecomment-512815828)

PowerSchill commented 5 years ago

I had the issue and even recreating the secret-id didn't work. I had to completely recreate the role.