From 98769717c451f8c253de695dd23f0614d7341b29 Mon Sep 17 00:00:00 2001 From: Adrien Waksberg Date: Thu, 16 Nov 2023 16:05:55 +0100 Subject: [PATCH] feat: manage role --- CHANGELOG.md | 1 + README.md | 19 +++- defaults/main.yml | 2 + library/elasticsearch_role.py | 126 +++++++++++++++++++++++++ molecule/default/converge.yml | 9 ++ molecule/default/tests/test_default.py | 5 + tasks/data.yml | 14 +++ 7 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 library/elasticsearch_role.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a2693b..1fe6c93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/) - feat: add variable to set master - feat: manage user +- feat: manage role - feat: add variable to set major version - feat: add ilm policy - test: add support debian 12 diff --git a/README.md b/README.md index 47b10c5..19767e4 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,26 @@ Install and configure Elasticsearch * `elasticsearch_api_password` - set the password for api * `elasticsearch_config` - hash with the configuration (see [elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html)) -``` +```yaml path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch ``` * `elasticsearch_ssl_key`: - string contain ssl private key if `xpack.security.transport.ssl.key` is defined in elasticsearch_config * `elasticsearch_ssl_certificate`: - string contain ssl certificate if `xpack.security.transport.certificate.key` is defined in elasticsearch_config +* `elasticsearch_roles` - hash with the roles to managed + +```yaml + myrole: + cluster: + - all + indices: + - names: ["logstash*"] + privileges: + - create + - write +``` + * `elasticsearch_users` - hash with the users to managed ```yaml @@ -41,7 +54,7 @@ Install and configure Elasticsearch * `elasticsearch_index_templates` - hash with the index templates configuration -``` +```yaml logstash: index_patterns: - 'logstash-*' @@ -58,7 +71,7 @@ Install and configure Elasticsearch * `elasticsearch_ilm_policies` - hash with the ilm policies configuration -``` +```yaml autoclean: delete: min_age: 30d diff --git a/defaults/main.yml b/defaults/main.yml index 3482ece..3a77b30 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -12,3 +12,5 @@ elasticsearch_default_config: elasticsearch_full_config: "{{ elasticsearch_default_config | combine(elasticsearch_config) }}" elasticsearch_index_templates: {} elasticsearch_ilm_policies: {} +elasticsearch_roles: {} +elasticsearch_users: {} diff --git a/library/elasticsearch_role.py b/library/elasticsearch_role.py new file mode 100644 index 0000000..31e68a1 --- /dev/null +++ b/library/elasticsearch_role.py @@ -0,0 +1,126 @@ +#!/usr/bin/python + +from ansible.module_utils.basic import * +from ansible.module_utils.elasticsearch_api import * + +class ElasticsearchRole: + def __init__(self, api_url, api_user, api_password, name, cluster, indices): + self.api = ElasticsearchApi( + api_url, + api_user, + api_password + ) + self.api_url = api_url + self.name = name + self.cluster = cluster + self.indices = indices + self.exist = False + self.data = {} + + def get_data(self): + status_code, data = self.api.get('_security/role/{}'.format(self.name)) + if status_code == 200: + self.exist = True + self.data = data[self.name] + + def array_has_changed(self, list1, list2): + for item in list1: + if item not in list2: + return True + + for item in list2: + if item not in list1: + return True + + return False + + def cluster_has_changed(self): + return self.array_has_changed(self.cluster, self.data['cluster']) + + def same_indice(self, indice1, indice2): + if self.array_has_changed(indice1['names'], indice2['names']): + return False + + if self.array_has_changed(indice1['privileges'], indice2['privileges']): + return False + + return True + + def indices_have_changed(self): + for indice1 in self.indices: + exist = False + for indice2 in self.data['indices']: + if self.same_indice(indice1, indice2): + exist = True + break + if not exist: + return True + + for indice1 in self.data['indices']: + exist = False + for indice2 in self.indices: + if self.same_indice(indice1, indice2): + exist = True + break + if not exist: + return True + + return False + + def has_changed(self): + if self.cluster_has_changed(): + return True + + if self.indices_have_changed(): + return True + + return False + + def create(self): + self.api.put( + '_security/role/{}'.format(self.name), + { + 'cluster': self.cluster, + 'indices': self.indices + } + ) + + def delete(self): + self.api.delete('_security/role/{}'.format(self.name)) + + +def main(): + fields = { + 'name': { 'type': 'str', 'required': True }, + 'indices': { 'type': 'list', 'default': [] }, + 'cluster': { '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, 'no_log': True }, + 'state': { 'type': 'str', 'default': 'present', 'choice': ['present', 'absent'] }, + } + module = AnsibleModule(argument_spec=fields) + changed = False + + role = ElasticsearchRole( + module.params['api_url'], + module.params['api_user'], + module.params['api_password'], + module.params['name'], + module.params['cluster'], + module.params['indices'], + ) + role.get_data() + + if module.params['state'] == 'present': + if not role.exist or role.has_changed(): + role.create() + changed = True + elif user.exist: + role.delete() + changed = True + + module.exit_json(changed=changed) + +if __name__ == '__main__': + main() diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index c99069d..46728b3 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -60,6 +60,15 @@ F3OGK/Tmx3MW5O+lq5O+2oRmUSfcIUgnrjgUeevj6Rgt1qx33WEoKBM2rVBIBqOn ZKrzDBkVG/H+H0hwiV219PLE -----END PRIVATE KEY----- + elasticsearch_roles: + myrole: + cluster: + - all + indices: + - names: ["logstash*"] + privileges: + - create + - write elasticsearch_users: toto: password: supers3cret diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py index 6a7ea77..c01dd13 100644 --- a/molecule/default/tests/test_default.py +++ b/molecule/default/tests/test_default.py @@ -45,6 +45,11 @@ def test_elasticsearch_template(host): result = host.check_output('curl -v -u elastic:mysecret http://127.0.0.1:9200/_template/test') assert '"number_of_replicas":"1"' in result +def test_elasticsearch_role(host): + result = host.check_output('curl -v -u elastic:mysecret http://127.0.0.1:9200/_security/role/myrole') + assert '"names":["logstash*"]' in result + assert '"privileges":["create","write"]' in result + def test_elasticsearch_user(host): result = host.check_output('curl -v -u elastic:mysecret http://127.0.0.1:9200/_security/user/toto') assert '"username":"toto"' in result diff --git a/tasks/data.yml b/tasks/data.yml index ddf8481..fa3c9ff 100644 --- a/tasks/data.yml +++ b/tasks/data.yml @@ -6,6 +6,20 @@ run_once: true tags: elasticsearch +- name: Manage roles + elasticsearch_role: + name: "{{ item.key }}" + cluster: "{{ item.value.cluster | default(omit) }}" + indices: "{{ item.value.indices | default(omit) }}" + api_user: "{{ elasticsearch_api_user }}" + api_password: "{{ elasticsearch_password }}" + state: "{{ item.value.state | default('present') }}" + loop: "{{ elasticsearch_roles | dict2items }}" + loop_control: + label: "{{ item.key }}" + run_once: true + tags: elasticsearch + - name: Manage users elasticsearch_user: name: "{{ item.key }}"