Merge pull request #11787 from liortamari/hashicorp-vault-kubernetes-auth

Add Hashicorp Vault Kubernetes authentication method
This commit is contained in:
Shane McDonald
2022-03-23 11:02:38 -04:00
committed by GitHub
2 changed files with 102 additions and 11 deletions

View File

@@ -47,15 +47,24 @@ base_inputs = {
'multiline': False, 'multiline': False,
'help_text': _('Name of the namespace to use when authenticate and retrieve secrets'), 'help_text': _('Name of the namespace to use when authenticate and retrieve secrets'),
}, },
{
'id': 'kubernetes_role',
'label': _('Kubernetes role'),
'type': 'string',
'multiline': False,
'help_text': _(
'The Role for Kubernetes Authentication.'
' This is the named role, configured in Vault server, for AWX pod auth policies.'
' see https://www.vaultproject.io/docs/auth/kubernetes#configuration'
),
},
{ {
'id': 'default_auth_path', 'id': 'default_auth_path',
'label': _('Path to Approle Auth'), 'label': _('Path to Auth'),
'type': 'string', 'type': 'string',
'multiline': False, 'multiline': False,
'default': 'approle', 'default': 'approle',
'help_text': _( 'help_text': _('The Authentication path to use if one isn\'t provided in the metadata when linking to an input field. Defaults to \'approle\''),
'The AppRole Authentication path to use if one isn\'t provided in the metadata when linking to an input field. Defaults to \'approle\''
),
}, },
], ],
'metadata': [ 'metadata': [
@@ -151,16 +160,30 @@ def handle_auth(**kwargs):
if kwargs.get('token'): if kwargs.get('token'):
token = kwargs['token'] token = kwargs['token']
elif kwargs.get('role_id') and kwargs.get('secret_id'): elif kwargs.get('role_id') and kwargs.get('secret_id'):
token = approle_auth(**kwargs) token = method_auth(**kwargs, auth_param=approle_auth(**kwargs))
elif kwargs.get('kubernetes_role'):
token = method_auth(**kwargs, auth_param=kubernetes_auth(**kwargs))
else: else:
raise Exception('Either token or AppRole parameters must be set') raise Exception('Either token or AppRole/Kubernetes authentication parameters must be set')
return token return token
def approle_auth(**kwargs): def approle_auth(**kwargs):
role_id = kwargs['role_id'] return {'role_id': kwargs['role_id'], 'secret_id': kwargs['secret_id']}
secret_id = kwargs['secret_id']
def kubernetes_auth(**kwargs):
jwt_file = pathlib.Path('/var/run/secrets/kubernetes.io/serviceaccount/token')
with jwt_file.open('r') as jwt_fo:
jwt = jwt_fo.read().rstrip()
return {'role': kwargs['kubernetes_role'], 'jwt': jwt}
def method_auth(**kwargs):
# get auth method specific params
request_kwargs = {'json': kwargs['auth_param'], 'timeout': 30}
# we first try to use the 'auth_path' from the metadata # we first try to use the 'auth_path' from the metadata
# if not found we try to fetch the 'default_auth_path' from inputs # if not found we try to fetch the 'default_auth_path' from inputs
auth_path = kwargs.get('auth_path') or kwargs['default_auth_path'] auth_path = kwargs.get('auth_path') or kwargs['default_auth_path']
@@ -168,9 +191,6 @@ def approle_auth(**kwargs):
url = urljoin(kwargs['url'], 'v1') url = urljoin(kwargs['url'], 'v1')
cacert = kwargs.get('cacert', None) cacert = kwargs.get('cacert', None)
request_kwargs = {'timeout': 30}
# AppRole Login
request_kwargs['json'] = {'role_id': role_id, 'secret_id': secret_id}
sess = requests.Session() sess = requests.Session()
# Namespace support # Namespace support
if kwargs.get('namespace'): if kwargs.get('namespace'):

View File

@@ -1,3 +1,8 @@
import pytest
from unittest import mock
from awx.main.credential_plugins import hashivault
def test_imported_azure_cloud_sdk_vars(): def test_imported_azure_cloud_sdk_vars():
from awx.main.credential_plugins import azure_kv from awx.main.credential_plugins import azure_kv
@@ -5,3 +10,69 @@ def test_imported_azure_cloud_sdk_vars():
assert all([hasattr(c, 'name') for c in azure_kv.clouds]) assert all([hasattr(c, 'name') for c in azure_kv.clouds])
assert all([hasattr(c, 'suffixes') for c in azure_kv.clouds]) assert all([hasattr(c, 'suffixes') for c in azure_kv.clouds])
assert all([hasattr(c.suffixes, 'keyvault_dns') for c in azure_kv.clouds]) assert all([hasattr(c.suffixes, 'keyvault_dns') for c in azure_kv.clouds])
def test_hashivault_approle_auth():
kwargs = {
'role_id': 'the_role_id',
'secret_id': 'the_secret_id',
}
expected_res = {
'role_id': 'the_role_id',
'secret_id': 'the_secret_id',
}
res = hashivault.approle_auth(**kwargs)
assert res == expected_res
def test_hashivault_kubernetes_auth():
kwargs = {
'kubernetes_role': 'the_kubernetes_role',
}
expected_res = {
'role': 'the_kubernetes_role',
'jwt': 'the_jwt',
}
with mock.patch('pathlib.Path') as path_mock:
mock.mock_open(path_mock.return_value.open, read_data='the_jwt')
res = hashivault.kubernetes_auth(**kwargs)
path_mock.assert_called_with('/var/run/secrets/kubernetes.io/serviceaccount/token')
assert res == expected_res
def test_hashivault_handle_auth_token():
kwargs = {
'token': 'the_token',
}
token = hashivault.handle_auth(**kwargs)
assert token == kwargs['token']
def test_hashivault_handle_auth_approle():
kwargs = {
'role_id': 'the_role_id',
'secret_id': 'the_secret_id',
}
with mock.patch.object(hashivault, 'method_auth') as method_mock:
method_mock.return_value = 'the_token'
token = hashivault.handle_auth(**kwargs)
method_mock.assert_called_with(**kwargs, auth_param=kwargs)
assert token == 'the_token'
def test_hashivault_handle_auth_kubernetes():
kwargs = {
'kubernetes_role': 'the_kubernetes_role',
}
with mock.patch.object(hashivault, 'method_auth') as method_mock:
with mock.patch('pathlib.Path') as path_mock:
mock.mock_open(path_mock.return_value.open, read_data='the_jwt')
method_mock.return_value = 'the_token'
token = hashivault.handle_auth(**kwargs)
method_mock.assert_called_with(**kwargs, auth_param={'role': 'the_kubernetes_role', 'jwt': 'the_jwt'})
assert token == 'the_token'
def test_hashivault_handle_auth_not_enough_args():
with pytest.raises(Exception):
hashivault.handle_auth()