Merge "Use secret_id's with vault-kv relation"
This commit is contained in:
commit
9c78a515e1
|
@ -6,3 +6,5 @@ authorize-charm:
|
|||
description: Token to use to authorize charm
|
||||
required:
|
||||
- token
|
||||
refresh-secrets:
|
||||
description: Refresh secret_id's and re-issue retrieval tokens for secrets endpoints
|
||||
|
|
|
@ -29,6 +29,8 @@ import charm.vault as vault
|
|||
|
||||
import charms.reactive
|
||||
|
||||
from charms.reactive.flags import set_flag
|
||||
|
||||
|
||||
def authorize_charm_action(*args):
|
||||
"""Create a role allowing the charm to perform certain vault actions.
|
||||
|
@ -39,10 +41,20 @@ def authorize_charm_action(*args):
|
|||
role_id = vault.setup_charm_vault_access(action_config['token'])
|
||||
hookenv.leader_set({vault.CHARM_ACCESS_ROLE_ID: role_id})
|
||||
|
||||
|
||||
def refresh_secrets(*args):
|
||||
"""Refresh secret_id's and re-issue tokens for secret_id retrieval
|
||||
on secrets end-points"""
|
||||
if not hookenv.is_leader():
|
||||
hookenv.action_fail('Please run action on lead unit')
|
||||
set_flag('secrets.refresh')
|
||||
|
||||
|
||||
# Actions to function mapping, to allow for illegal python action names that
|
||||
# can map to a python function.
|
||||
ACTIONS = {
|
||||
"authorize-charm": authorize_charm_action,
|
||||
"refresh-secrets": refresh_secrets,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
actions.py
|
|
@ -300,6 +300,22 @@ def configure_approle(client, name, cidr, policies):
|
|||
token_ttl='60s',
|
||||
token_max_ttl='60s',
|
||||
policies=policies,
|
||||
bind_secret_id='false',
|
||||
bind_secret_id='true',
|
||||
bound_cidr_list=cidr)
|
||||
return client.get_role_id(name)
|
||||
|
||||
|
||||
def generate_role_secret_id(client, name, cidr):
|
||||
"""Generate a new secret_id for an AppRole
|
||||
|
||||
:param client: Vault client
|
||||
:ptype client: hvac.Client
|
||||
:param name: Name of role
|
||||
:ptype name: str
|
||||
:param cidr: Network address of remote unit
|
||||
:ptype cidr: str
|
||||
:returns: Vault token to retrieve the response-wrapped response
|
||||
:rtype: str"""
|
||||
response = client.write('auth/approle/role/{}/secret-id'.format(name),
|
||||
wrap_ttl='1h', cidr_list=cidr)
|
||||
return response['wrap_info']['token']
|
||||
|
|
|
@ -45,6 +45,7 @@ from charms.reactive import (
|
|||
when,
|
||||
when_file_changed,
|
||||
when_not,
|
||||
when_any,
|
||||
)
|
||||
|
||||
from charms.reactive.relations import (
|
||||
|
@ -371,7 +372,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),
|
||||
|
@ -401,7 +402,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
|
||||
|
@ -412,6 +414,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-
|
||||
|
@ -438,16 +442,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')
|
||||
|
|
|
@ -342,6 +342,20 @@ class TestLibCharmVault(unit_tests.test_utils.CharmTestCase):
|
|||
vault.configure_secret_backend(hvac_client, 'secrets')
|
||||
hvac_client.enable_secret_backend.assert_not_called()
|
||||
|
||||
def test_generate_role_secret_id(self):
|
||||
hvac_client = mock.MagicMock()
|
||||
hvac_client.write.return_value = {'wrap_info': {'token': 'foo'}}
|
||||
self.assertEqual(
|
||||
vault.generate_role_secret_id(hvac_client,
|
||||
'testrole',
|
||||
'10.5.10.10/32'),
|
||||
'foo'
|
||||
)
|
||||
hvac_client.write.assert_called_with(
|
||||
'auth/approle/role/testrole/secret-id',
|
||||
wrap_ttl='1h', cidr_list='10.5.10.10/32'
|
||||
)
|
||||
|
||||
def test_configure_policy(self):
|
||||
hvac_client = mock.MagicMock()
|
||||
vault.configure_policy(hvac_client, 'test-policy', 'test-hcl')
|
||||
|
@ -365,7 +379,7 @@ class TestLibCharmVault(unit_tests.test_utils.CharmTestCase):
|
|||
token_ttl='60s',
|
||||
token_max_ttl='60s',
|
||||
policies=['test-policy'],
|
||||
bind_secret_id='false',
|
||||
bind_secret_id='true',
|
||||
bound_cidr_list='10.5.0.20/32'
|
||||
)
|
||||
hvac_client.get_role_id.assert_called_with('test-role')
|
||||
|
|
|
@ -535,6 +535,8 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
_vault.configure_approle.side_effect = ['role_a', 'role_b']
|
||||
self.is_flag_set.return_value = False
|
||||
_vault.get_api_url.return_value = "http://vault:8200"
|
||||
hvac_client.list_roles.return_value = []
|
||||
_vault.generate_role_secret_id.return_value = 'mysecret'
|
||||
|
||||
handlers.configure_secrets_backend()
|
||||
|
||||
|
@ -560,12 +562,17 @@ class TestHandlers(unit_tests.test_utils.CharmTestCase):
|
|||
|
||||
secrets_interface.set_role_id.assert_has_calls([
|
||||
mock.call(unit=mock.ANY,
|
||||
role_id='role_a'),
|
||||
role_id='role_a',
|
||||
token='mysecret'),
|
||||
mock.call(unit=mock.ANY,
|
||||
role_id='role_b'),
|
||||
role_id='role_b',
|
||||
token='mysecret'),
|
||||
])
|
||||
|
||||
self.clear_flag.assert_called_once_with('endpoint.secrets.new-request')
|
||||
self.clear_flag.assert_has_calls([
|
||||
mock.call('endpoint.secrets.new-request'),
|
||||
mock.call('secrets.refresh'),
|
||||
])
|
||||
|
||||
@mock.patch.object(handlers, 'vault')
|
||||
def send_vault_url_and_ca(self, _vault):
|
||||
|
|
Loading…
Reference in New Issue