Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ in order to authenticate to your HashiCorp Vault instance:

* `VAULT_ADDR`: url for vault
* `VAULT_SKIP_VERIFY=true`: if set, do not verify presented TLS certificate before communicating with Vault server. Setting this variable is not recommended except during testing
* `VAULT_AUTHTYPE`: authentication type to use: `token`, `userpass`, `github`, `ldap`, `approle`
* `VAULT_AUTHTYPE`: authentication type to use: `token`, `userpass`, `github`, `ldap`, `radius`, `approle`
* `VAULT_LOGIN_MOUNT_POINT`: mount point for login defaults to auth type
* `VAULT_TOKEN`: token for vault
* `VAULT_ROLE_ID`: (required by `approle`)
Expand Down
2 changes: 2 additions & 0 deletions ansible/module_utils/hashivault.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def hashivault_auth(client, params):
client.auth.userpass.login(username, password, mount_point=login_mount_point)
elif authtype == 'ldap':
client.auth.ldap.login(username, password, mount_point=login_mount_point)
elif authtype == 'radius':
client.auth.radius.login(username, password, mount_point=login_mount_point)
elif authtype == 'approle':
client = AppRoleClient(client, role_id, secret_id, mount_point=login_mount_point)
elif authtype == 'tls':
Expand Down
132 changes: 132 additions & 0 deletions ansible/modules/hashivault/hashivault_radius_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ansible.module_utils.hashivault import hashivault_argspec
from ansible.module_utils.hashivault import hashivault_auth_client
from ansible.module_utils.hashivault import hashivault_init
from ansible.module_utils.hashivault import hashiwrapper
from hvac.exceptions import InvalidPath

ANSIBLE_METADATA = {'status': ['stableinterface'], 'supported_by': 'community', 'version': '1.1'}
DOCUMENTATION = '''
---
module: hashivault_radius_config
version_added: "4.7.0"
short_description: Hashicorp Vault RADIUS configuration module
description:
- Module to configure the RADIUS authentication method in Hashicorp Vault.
options:
mount_point:
description:
- location where this auth_method is mounted. also known as "path"
default: radius
host:
description:
- The RADIUS server to connect to. Examples: radius.myorg.com, 127.0.0.1
required: True
secret:
description:
- The RADIUS shared secret
required: True
port:
description:
- The UDP port where the RADIUS server is listening on. Defaults is 1812.
default: 1812
unregistered_user_policies:
description:
- A comma-separated list of policies to be granted to unregistered users.
default: []
dial_timeout:
description:
- Number of seconds to wait for a backend connection before timing out. Default is 10.
default: 10
nas_port:
description:
- The NAS-Port attribute of the RADIUS request. Defaults is 10.
default: 10
extends_documentation_fragment: hashivault
'''
EXAMPLES = '''
---
- hosts: localhost
tasks:
- hashivault_radius_config:
host: "radius.myorg.com"
secret: "my_radius_secret"
port: 1812
dial_timeout: 10
nas_port: 10
unregistered_user_policies:
- default
- hosts: localhost
tasks:
- hashivault_radius_config:
host: "127.0.0.1"
secret: "{{ radius_secret }}"
mount_point: "radius"
'''


def main():
argspec = hashivault_argspec()
argspec['mount_point'] = dict(required=False, type='str', default='radius')
argspec['host'] = dict(required=True, type='str')
argspec['secret'] = dict(required=True, type='str', no_log=True)
argspec['port'] = dict(required=False, type='int', default=1812)
argspec['unregistered_user_policies'] = dict(required=False, type='list', default=[])
argspec['dial_timeout'] = dict(required=False, type='int', default=10)
argspec['nas_port'] = dict(required=False, type='int', default=10)

module = hashivault_init(argspec, supports_check_mode=True)
result = hashivault_radius_config(module)
if result.get('failed'):
module.fail_json(**result)
else:
module.exit_json(**result)


@hashiwrapper
def hashivault_radius_config(module):
params = module.params
client = hashivault_auth_client(params)
changed = False
desired_state = dict()
desired_state['mount_point'] = params.get('mount_point')
desired_state['host'] = params.get('host')
desired_state['secret'] = params.get('secret')
desired_state['port'] = params.get('port')
desired_state['unregistered_user_policies'] = params.get('unregistered_user_policies')
desired_state['dial_timeout'] = params.get('dial_timeout')
desired_state['nas_port'] = params.get('nas_port')

# if secret is None, remove it from desired state since we can't compare
if desired_state['secret'] is None:
del desired_state['secret']

# check current config
current_state = dict()
try:
result = client.auth.radius.read_configuration(
mount_point=desired_state['mount_point'])['data']
# map keys from Vault response to desired state keys
current_state['host'] = result.get('host', '')
current_state['port'] = result.get('port', 1812)
current_state['unregistered_user_policies'] = result.get('unregistered_user_policies', [])
current_state['dial_timeout'] = result.get('dial_timeout', 10)
current_state['nas_port'] = result.get('nas_port', 10)
except InvalidPath:
pass

# check if current config matches desired config values, if they match, set changed to false to prevent action
for k, v in current_state.items():
if k in desired_state and v != desired_state[k]:
changed = True

# if configs dont match and checkmode is off, complete the change
if changed and not module.check_mode:
client.auth.radius.configure(**desired_state)

return {'changed': changed}


if __name__ == '__main__':
main()
142 changes: 142 additions & 0 deletions ansible/modules/hashivault/hashivault_radius_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ansible.module_utils.hashivault import hashivault_argspec
from ansible.module_utils.hashivault import hashivault_auth_client
from ansible.module_utils.hashivault import hashivault_init
from ansible.module_utils.hashivault import hashiwrapper

ANSIBLE_METADATA = {'status': ['stableinterface'], 'supported_by': 'community', 'version': '1.1'}
DOCUMENTATION = '''
---
module: hashivault_radius_user
version_added: "4.7.0"
short_description: Hashicorp Vault RADIUS user management module
description:
- Module to manage RADIUS users in Hashicorp Vault.
options:
name:
description:
- username to create/update/delete
required: True
policies:
description:
- List of policies associated with the user
default: []
state:
description:
- whether to create/update or delete the user
choices: ['present', 'absent']
default: present
mount_point:
description:
- The "path" the auth backend is mounted on
default: radius
extends_documentation_fragment: hashivault
'''
EXAMPLES = '''
---
- hosts: localhost
tasks:
- hashivault_radius_user:
name: 'bob'
policies:
- 'default'
- 'bob-policy'
- hosts: localhost
tasks:
- hashivault_radius_user:
name: 'alice'
policies:
- 'alice-policy'
state: present
- hosts: localhost
tasks:
- hashivault_radius_user:
name: 'old-user'
state: absent
'''


def main():
argspec = hashivault_argspec()
argspec['name'] = dict(required=True, type='str')
argspec['policies'] = dict(required=False, type='list', default=[])
argspec['state'] = dict(required=False, choices=['present', 'absent'], default='present')
argspec['mount_point'] = dict(required=False, type='str', default='radius')
module = hashivault_init(argspec)
result = hashivault_radius_user(module.params)
if result.get('failed'):
module.fail_json(**result)
else:
module.exit_json(**result)


def hashivault_radius_user_update(user_details, client, user_name, user_policies, mount_point):
changed = False

# existing policies
if user_details['policies'] is not None:
if set(user_details['policies']) != set(user_policies):
changed = True
# new policies and none existing
elif len(user_policies) > 0:
changed = True

if changed:
try:
response = client.auth.radius.register_user(
username=user_name,
policies=user_policies,
mount_point=mount_point
)
except Exception as e:
return {'failed': True, 'msg': str(e)}
if response.status_code == 204:
return {'changed': True}
return {'changed': True, 'data': response}
return {'changed': False}


def hashivault_radius_user_create_or_update(params):
client = hashivault_auth_client(params)
user_name = params.get('name')
mount_point = params.get('mount_point')
user_policies = params.get('policies')
try:
user_details = client.auth.radius.read_user(username=user_name, mount_point=mount_point)
except Exception:
client.auth.radius.register_user(
username=user_name,
policies=user_policies,
mount_point=mount_point
)
return {'changed': True}
return hashivault_radius_user_update(user_details['data'], client, user_name=user_name,
user_policies=user_policies,
mount_point=mount_point)


def hashivault_radius_user_delete(params):
client = hashivault_auth_client(params)
user_name = params.get('name')
mount_point = params.get('mount_point')

try:
client.auth.radius.read_user(username=user_name, mount_point=mount_point)
except Exception:
return {'changed': False}
client.auth.radius.delete_user(username=user_name, mount_point=mount_point)
return {'changed': True}


@hashiwrapper
def hashivault_radius_user(params):
state = params.get('state')
if state == 'present':
return hashivault_radius_user_create_or_update(params)
elif state == 'absent':
return hashivault_radius_user_delete(params)


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions functional/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ ansible-playbook -v test_unseal.yml
ansible-playbook -v test_identity_group.yml
ansible-playbook -v test_identity_entity.yml
ansible-playbook -v test_ldap_group.yml
ansible-playbook -v test_radius_config.yml
ansible-playbook -v test_radius_user.yml
ansible-playbook -v test_pki.yml
ansible-playbook -v test_rekey.yml
# test_rekey.yml changes unseal keys, need to update VAULT_KEYS
Expand Down
52 changes: 52 additions & 0 deletions functional/test_radius_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
- hosts: localhost
gather_facts: no
tasks:
- name: enable radius authentication
hashivault_auth_method:
method_type: radius

- name: radius configuration
hashivault_radius_config:
host: localhost
secret: test_secret
port: 1812
dial_timeout: 10
nas_port: 10
register: radius_module
- assert: { that: "radius_module is changed" }
- assert: { that: "radius_module.rc == 0" }

- name: radius configuration is idempotent
hashivault_radius_config:
host: localhost
secret: test_secret
port: 1812
dial_timeout: 10
nas_port: 10
register: radius_module_idempotent
- assert: { that: "radius_module_idempotent is not changed" }

- name: radius configuration with unregistered_user_policies
hashivault_radius_config:
host: localhost
secret: test_secret
port: 1812
dial_timeout: 10
nas_port: 10
unregistered_user_policies:
- default
register: radius_module_policies
- assert: { that: "radius_module_policies is changed" }

- name: radius configuration with unregistered_user_policies is idempotent
hashivault_radius_config:
host: localhost
secret: test_secret
port: 1812
dial_timeout: 10
nas_port: 10
unregistered_user_policies:
- default
register: radius_module_policies_idempotent
- assert: { that: "radius_module_policies_idempotent is not changed" }
Loading
Loading