Add action to generate certificate against the PKI.
Created action to utilize the existing generate_certificate function for on demand certificates agains the existing vault PKI. Closes-Bug: #1948837 Change-Id: Ia1a169623c81d6aede7dc52eabd2de94007fde80
This commit is contained in:
parent
b797fcfcbf
commit
d8bfff76e4
|
@ -143,3 +143,25 @@ reload:
|
||||||
description: >-
|
description: >-
|
||||||
Reloads the vault unit. This allows for limited configuration options to be
|
Reloads the vault unit. This allows for limited configuration options to be
|
||||||
re-read. Vault will not become sealed.
|
re-read. Vault will not become sealed.
|
||||||
|
generate-certificate:
|
||||||
|
description: Generate a certificate agains the Vault PKI
|
||||||
|
properties:
|
||||||
|
ttl:
|
||||||
|
type: string
|
||||||
|
default: 87599h
|
||||||
|
description: >-
|
||||||
|
Specifies the Time To Live for the certificate
|
||||||
|
common-name:
|
||||||
|
type: string
|
||||||
|
description: >-
|
||||||
|
CN field of the new certificate
|
||||||
|
sans:
|
||||||
|
type: string
|
||||||
|
description: >-
|
||||||
|
Space delimited list of Subject Altername Name/IP addresse(s)
|
||||||
|
max-ttl:
|
||||||
|
type: string
|
||||||
|
default: 8760h
|
||||||
|
description: >-
|
||||||
|
Specifies the maximum Time To Live for generated certificates.
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,32 @@ def reload(args):
|
||||||
host.service_reload(service_name='vault')
|
host.service_reload(service_name='vault')
|
||||||
|
|
||||||
|
|
||||||
|
def generate_cert(*args):
|
||||||
|
"""Generates a certificate and sets it in the action output.
|
||||||
|
|
||||||
|
The certificate parameters are provided via the action parameters from
|
||||||
|
the user. If the current unit is not the leader or the vault calls fail,
|
||||||
|
this will result in a failed command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not hookenv.is_leader():
|
||||||
|
hookenv.action_fail('Please run action on lead unit')
|
||||||
|
return
|
||||||
|
|
||||||
|
action_config = hookenv.action_get()
|
||||||
|
sans_list = action_config.get('sans')
|
||||||
|
try:
|
||||||
|
new_crt = vault_pki.generate_certificate(
|
||||||
|
cert_type='server',
|
||||||
|
common_name=action_config.get('common-name'),
|
||||||
|
sans=list(sans_list.split()),
|
||||||
|
ttl=action_config.get('ttl'),
|
||||||
|
max_ttl=action_config.get('max-ttl'))
|
||||||
|
hookenv.action_set({'output': new_crt})
|
||||||
|
except vault.VaultError as e:
|
||||||
|
hookenv.action_fail(str(e))
|
||||||
|
|
||||||
|
|
||||||
# Actions to function mapping, to allow for illegal python action names that
|
# Actions to function mapping, to allow for illegal python action names that
|
||||||
# can map to a python function.
|
# can map to a python function.
|
||||||
ACTIONS = {
|
ACTIONS = {
|
||||||
|
@ -207,7 +233,8 @@ ACTIONS = {
|
||||||
"pause": pause,
|
"pause": pause,
|
||||||
"resume": resume,
|
"resume": resume,
|
||||||
"restart": restart,
|
"restart": restart,
|
||||||
"reload": reload
|
"reload": reload,
|
||||||
|
"generate-certificate": generate_cert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
actions.py
|
|
@ -0,0 +1,68 @@
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import src.actions.actions as actions
|
||||||
|
import unit_tests.test_utils
|
||||||
|
|
||||||
|
|
||||||
|
class TestActions(unit_tests.test_utils.CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestActions, self).setUp()
|
||||||
|
self.patches = []
|
||||||
|
self.patch_all()
|
||||||
|
self.patch_object(actions, 'hookenv', name='mock_hookenv')
|
||||||
|
|
||||||
|
def test_generate_cert_not_leader(self):
|
||||||
|
"""Test when not leader, action fails"""
|
||||||
|
self.mock_hookenv.is_leader.return_value = False
|
||||||
|
|
||||||
|
actions.generate_cert()
|
||||||
|
|
||||||
|
# Action should fail
|
||||||
|
self.mock_hookenv.action_fail.assert_called_with(
|
||||||
|
'Please run action on lead unit'
|
||||||
|
)
|
||||||
|
self.mock_hookenv.action_set.assert_not_called()
|
||||||
|
|
||||||
|
@patch.object(actions, 'vault_pki')
|
||||||
|
def test_generate_cert(self, mock_vault_pki):
|
||||||
|
self.mock_hookenv.is_leader.return_value = True
|
||||||
|
self.mock_hookenv.action_get.return_value = {
|
||||||
|
'sans': 'foobar 1.2.3.4',
|
||||||
|
'common-name': 'bazbuz',
|
||||||
|
'ttl': '5m',
|
||||||
|
'max-ttl': '5y',
|
||||||
|
}
|
||||||
|
mock_vault_pki.generate_certificate.return_value = 'shiny-cert'
|
||||||
|
|
||||||
|
actions.generate_cert()
|
||||||
|
|
||||||
|
# Validate the request for the cert was called
|
||||||
|
mock_vault_pki.generate_certificate.assert_called_with(
|
||||||
|
cert_type='server', common_name='bazbuz',
|
||||||
|
sans=['foobar', '1.2.3.4'], ttl='5m', max_ttl='5y',
|
||||||
|
)
|
||||||
|
self.mock_hookenv.action_set.assert_called_with({
|
||||||
|
'output': 'shiny-cert',
|
||||||
|
})
|
||||||
|
|
||||||
|
@patch.object(actions, 'vault_pki')
|
||||||
|
def test_generate_cert_vault_failure(self, mock_vault_pki):
|
||||||
|
"""Test failure interacting with vault_pki"""
|
||||||
|
self.mock_hookenv.is_leader.return_value = True
|
||||||
|
self.mock_hookenv.action_get.return_value = {
|
||||||
|
'sans': 'foobar',
|
||||||
|
'common-name': 'bazbuz',
|
||||||
|
'ttl': '5m',
|
||||||
|
'max-ttl': '5y',
|
||||||
|
}
|
||||||
|
mock_vault_pki.generate_certificate.side_effect = \
|
||||||
|
actions.vault.VaultNotReady(1)
|
||||||
|
|
||||||
|
actions.generate_cert()
|
||||||
|
|
||||||
|
# Validate the request for the cert was called
|
||||||
|
self.mock_hookenv.action_set.assert_not_called
|
||||||
|
self.mock_hookenv.action_fail.assert_called_with(
|
||||||
|
'Vault is not ready (1)'
|
||||||
|
)
|
Loading…
Reference in New Issue