Use secret_id's with vault-kv relation

In order to tighten the security around access to secrets stored
in a Vault KV secrets backend, generate a secret_id for each
accessing unit, using a response wrapping token which is passed
over the relation to the consuming application.

The consuming application will then use this token out-of-band of
Juju to retrieve the secret_id associated with the AppRole ID
directly from Vault.

Add a new action 'refresh-secrets' to force a renewal of secret_id's
and associated one-shot retrieval tokens across a deployment.

A token is only issued when a new approle is created or when
a refresh is initiated via the 'refresh-secrets' action.

Change-Id: I2cd173514377d65542ea4fa67ccf700ea4b6ab89
This commit is contained in:
James Page
2018-05-04 12:18:15 +02:00
parent 28fb89a44b
commit 3b0e793feb
7 changed files with 77 additions and 10 deletions

View File

@@ -44,6 +44,7 @@ from charms.reactive import (
when,
when_file_changed,
when_not,
when_any,
)
from charms.reactive.relations import (
@@ -370,7 +371,7 @@ def file_change_auto_unlock_mode():
@when('leadership.is_leader')
@when('endpoint.secrets.new-request')
@when_any('endpoint.secrets.new-request', 'secrets.refresh')
def configure_secrets_backend():
""" Process requests for setup and access to simple kv secret backends """
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=10),
@@ -400,7 +401,8 @@ def configure_secrets_backend():
return
client.auth_approle(charm_role_id)
secrets = endpoint_from_flag('endpoint.secrets.new-request')
secrets = (endpoint_from_flag('endpoint.secrets.new-request') or
endpoint_from_flag('secrets.connected'))
requests = secrets.requests()
# Configure KV secret backends
@@ -411,6 +413,8 @@ def configure_secrets_backend():
continue
vault.configure_secret_backend(client, name=backend)
refresh_secrets = is_flag_set('secrets.refresh')
# Configure AppRoles for application unit access
for request in requests:
# NOTE: backends must start with charm-
@@ -437,16 +441,27 @@ def configure_secrets_backend():
hostname=hostname)
)
cidr = '{}/32'.format(access_address)
new_role = (approle_name not in client.list_roles())
approle_id = vault.configure_approle(
client,
name=approle_name,
cidr='{}/32'.format(access_address),
cidr=cidr,
policies=[policy_name])
secrets.set_role_id(unit=unit,
role_id=approle_id)
if new_role or refresh_secrets:
wrapped_secret = vault.generate_role_secret_id(
client,
name=approle_name,
cidr=cidr
)
secrets.set_role_id(unit=unit,
role_id=approle_id,
token=wrapped_secret)
clear_flag('endpoint.secrets.new-request')
clear_flag('secrets.refresh')
@when('secrets.connected')