From bebe621f3e87f6e28138ba10e1d88ab5e132e2ba Mon Sep 17 00:00:00 2001 From: Adrien Waksberg Date: Sun, 8 Mar 2020 18:35:44 +0100 Subject: [PATCH] feat: manage users --- README.md | 2 + defaults/main.yml | 3 ++ library/elasticsearch_user.py | 81 +++++++++++++++++++++++++++++++ module_utils/elasticsearch_api.py | 14 +++++- molecule/default/converge.yml | 10 ++-- tasks/config.yml | 2 +- tasks/data.yml | 15 ++++++ 7 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 library/elasticsearch_user.py diff --git a/README.md b/README.md index 92a55e0..6fc904e 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Install and configure Elasticsearch format: YYYY-MM-dd ``` +* `elasticsearch_users` - hash to manage users + ## How to use ``` diff --git a/defaults/main.yml b/defaults/main.yml index ea82f64..12afd16 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,8 +1,11 @@ --- elasticsearch_heap_size: 1g +elasticsearch_api_user: elastic +elasticsearch_api_password: null elasticsearch_config: {} elasticsearch_default_config: path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch elasticsearch_full_config: '{{ elasticsearch_default_config|combine(elasticsearch_config) }}' elasticsearch_index_templates: {} +elasticsearch_users: {} diff --git a/library/elasticsearch_user.py b/library/elasticsearch_user.py new file mode 100644 index 0000000..c42e223 --- /dev/null +++ b/library/elasticsearch_user.py @@ -0,0 +1,81 @@ +#!/usr/bin/python + +from ansible.module_utils.basic import * +from ansible.module_utils.elasticsearch_api import * + +class ElasticsearchUser: + def __init__(self, api, name): + self.api = api + self.name = name + self.exist = False + self.data = {} + + def get_data(self): + status_code, data = self.api.get('_security/user/{}'.format(self.name)) + if status_code == 200: + self.exist = True + self.data = data[self.name] + + def has_changed(self, roles): + if roles.sort() != self.data['roles'].sort(): + return True + + return False + + def password_has_changed(self, password): + return not self.api.check_password(self.name, password) + + def change_password(self, password): + self.api.put( + '_security/user/{}/_password'.format(self.name), + { 'password': password } + ) + + def create(self, options): + self.api.put( + '_security/user/{}'.format(self.name), + options + ) + +def main(): + fields = { + 'name': { 'type': 'str', 'required': True }, + 'password': { 'type': 'str', 'required': True }, + 'roles': { 'type': 'list', 'default': [] }, + 'api_url': { 'type': 'str', 'default': 'http://127.0.0.1:9200' }, + 'api_user': { 'type': 'str', 'default': None }, + 'api_password': { 'type': 'str', 'default': None }, + } + module = AnsibleModule(argument_spec=fields) + changed = False + + options = { + 'roles': module.params['roles'], + 'password': module.params['password'], + } + + api = ElasticsearchApi( + module.params['api_url'], + module.params['api_user'], + module.params['api_password'] + ) + + user = ElasticsearchUser( + api, + module.params['name'], + ) + user.get_data() + + if module.params['name'] != 'elastic': + if not user.exist or user.has_changed(module.params['roles']): + user.create(options) + changed = True + + if user.password_has_changed(module.params['password']): + user.change_password(module.params['password']) + changed = True + + module.exit_json(changed=changed) + +if __name__ == '__main__': + main() diff --git a/module_utils/elasticsearch_api.py b/module_utils/elasticsearch_api.py index e8d858c..bf7f7f5 100644 --- a/module_utils/elasticsearch_api.py +++ b/module_utils/elasticsearch_api.py @@ -7,8 +7,18 @@ class ElasticsearchApi: self.url = url self.headers = {} if user and password: - token = base64.b64encode('{}:{}',) - self.headers = { 'Authorization': 'Basic ' + base64.b64encode({},) } + token = base64.b64encode('{}:{}'.format(user, password)) + self.headers = { 'Authorization': 'Basic {}'.format(token) } + + def check_password(self, user, password): + token = base64.b64encode('{}:{}'.format(user, password)) + headers = { 'Authorization': 'Basic {}'.format(token) } + + r = requests.get(self.url, headers=headers) + if r.status_code != 401: + return True + + return False def get(self, path): r = requests.get( diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index a098a8b..0b33cdc 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -1,7 +1,10 @@ --- - name: Converge hosts: all + roles: + - ansible-role-elasticsearch vars: + elasticsearch_api_password: secret elasticsearch_heap_size: 512m elasticsearch_index_templates: test: @@ -13,10 +16,3 @@ mappings: metrics: type: short - roles: - - ansible-role-elasticsearch -# -# tasks: -# - name: "Include ansible-role-elasticsearch" -# include_role: -# name: "ansible-role-elasticsearch" diff --git a/tasks/config.yml b/tasks/config.yml index 08db1da..d7ed8a6 100644 --- a/tasks/config.yml +++ b/tasks/config.yml @@ -12,7 +12,7 @@ - name: copy config file copy: - content: '{{ elasticsearch_full_config|to_yaml }}' + content: '{{ elasticsearch_full_config|to_nice_yaml }}' dest: /etc/elasticsearch/elasticsearch.yml owner: root group: elasticsearch diff --git a/tasks/data.yml b/tasks/data.yml index 6ee4fdc..b6ca4d2 100644 --- a/tasks/data.yml +++ b/tasks/data.yml @@ -1,10 +1,25 @@ --- +- name: manage users + elasticsearch_user: + name: '{{ item.key }}' + password: '{{ item.value.password }}' + api_user: '{{ elasticsearch_api_user }}' + api_password: '{{ elasticsearch_api_password }}' + loop: '{{ elasticsearch_users|dict2items }}' + when: | + 'xpack.security.enabled' in elasticsearch_full_config and + elasticsearch_full_config['xpack.security.enabled'] == True + run_once: true + tags: elasticsearch + - name: copy index templates elasticsearch_template: name: '{{ item.key }}' index_patterns: '{{ item.value.index_patterns }}' settings: '{{ item.value.settings|default({}) }}' mappings: '{{ item.value.mappings|default({}) }}' + api_user: '{{ elasticsearch_api_user }}' + api_password: '{{ elasticsearch_api_password }}' no_log: true loop: '{{ elasticsearch_index_templates|dict2items }}' run_once: true