Skip to content

Commit 5972e87

Browse files
author
Tobias Bauriedel
committed
Add elasticsearch_role module
1 parent 0305dfa commit 5972e87

File tree

7 files changed

+344
-0
lines changed

7 files changed

+344
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
# The workaround for arbitrarily named role directory is important because the git repo has one name and the role within it another
3+
# Found at: https://github.com/ansible-community/molecule/issues/1567#issuecomment-436876722
4+
- name: Converge
5+
collections:
6+
- netways.elasticstack
7+
hosts: all
8+
vars:
9+
#elasticsearch_security: true # needed for tests of > 7 releases
10+
elasticstack_full_stack: false
11+
elasticsearch_jna_workaround: true
12+
elasticsearch_disable_systemcallfilterchecks: true
13+
elasticstack_release: 8
14+
elasticsearch_heap: "1"
15+
elasticstack_no_log: false
16+
tasks:
17+
- name: Include Elastics repos role
18+
ansible.builtin.include_role:
19+
name: repos
20+
- name: Include Elasticsearch
21+
ansible.builtin.include_role:
22+
name: elasticsearch
23+
24+
- name: Fetch Elastic password # noqa: risky-shell-pipe
25+
ansible.builtin.shell: >
26+
if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi;
27+
grep "PASSWORD elastic" /usr/share/elasticsearch/initial_passwords |
28+
awk {' print $4 '}
29+
register: elasticstack_password
30+
changed_when: false
31+
32+
- name: Create role
33+
netways.elasticstack.elasticsearch_role:
34+
name: new-role3
35+
cluster:
36+
- manage_own_api_key
37+
- delegate_pki
38+
indicies:
39+
- names:
40+
- foobar321
41+
- barfoo123
42+
privileges:
43+
- read
44+
- write
45+
state: present
46+
host: https://localhost:9200
47+
auth_user: elastic
48+
auth_pass: "{{ elasticstack_password.stdout }}"
49+
verify_certs: false
50+
ca_certs: /etc/elasticsearch/certs/http_ca.crt
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
dependency:
3+
name: galaxy
4+
options:
5+
requirements-file: requirements.yml
6+
driver:
7+
name: docker
8+
platforms:
9+
- name: elasticsearch_default1
10+
groups:
11+
- elasticsearch
12+
image: "geerlingguy/docker-${MOLECULE_DISTRO:-debian11}-ansible:latest"
13+
command: ${MOLECULE_DOCKER_COMMAND:-""}
14+
volumes:
15+
- /sys/fs/cgroup:/sys/fs/cgroup:rw
16+
cgroupns_mode: host
17+
privileged: true
18+
pre_build_image: true
19+
#- name: elasticsearch_default2
20+
# groups:
21+
# - elasticsearch
22+
# image: "geerlingguy/docker-${MOLECULE_DISTRO:-debian11}-ansible:latest"
23+
# command: ${MOLECULE_DOCKER_COMMAND:-""}
24+
# volumes:
25+
# - /sys/fs/cgroup:/sys/fs/cgroup:rw
26+
# cgroupns_mode: host
27+
# privileged: true
28+
# pre_build_image: true
29+
provisioner:
30+
name: ansible
31+
env:
32+
ANSIBLE_VERBOSITY: 3
33+
verifier:
34+
name: ansible
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
- name: Prepare
3+
hosts: all
4+
tasks:
5+
- name: Install packages for Debian
6+
ansible.builtin.apt:
7+
name:
8+
- gpg
9+
- gpg-agent
10+
- procps
11+
- curl
12+
- iproute2
13+
- git
14+
- openssl
15+
- python3
16+
update_cache: yes
17+
18+
- name: Install python module dependencies
19+
ansible.builtin.pip:
20+
name: "{{ item }}"
21+
loop:
22+
- elasticsearch
23+
- certifi
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
collections:
3+
- community.general

plugins/module_utils/api.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# !/usr/bin/python3
2+
3+
# Copyright (c) 2024, Tobias Bauriedel <tobias.bauriedel@netways.de>
4+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
5+
# https://www.gnu.org/licenses/gpl-3.0.txt)
6+
7+
from asyncio.constants import SENDFILE_FALLBACK_READBUFFER_SIZE
8+
from elasticsearch import Elasticsearch
9+
import ssl
10+
11+
def new_client_basic_auth(host, auth_user, auth_pass, ca_certs, verify_certs) -> Elasticsearch:
12+
ctx = ssl.create_default_context(cafile=ca_certs)
13+
ctx.check_hostname = False
14+
ctx.verify_mode = False
15+
return Elasticsearch(hosts=[host], basic_auth=(auth_user, auth_pass), ssl_context=ctx, verify_certs=verify_certs)
16+
17+
18+
class Role():
19+
def __init__(self, result, role_name, cluster, indicies, state, host, auth_user, auth_pass, verify_certs, ca_certs):
20+
self.role_name = role_name
21+
self.cluster = cluster
22+
self.indicies = indicies
23+
self.state = state
24+
self.result = result
25+
26+
if auth_user == "" or auth_pass == "":
27+
result['stderr'] = "'basic_auth' for authentication defined but 'auth_user' or auth_pass' is empty"
28+
return
29+
self.client = new_client_basic_auth(host=host, auth_user=auth_user, auth_pass=auth_pass, verify_certs=verify_certs, ca_certs=ca_certs)
30+
31+
self.handle()
32+
33+
34+
def return_result(self) -> dict:
35+
return self.result
36+
37+
38+
def handle(self):
39+
if self.state == 'absent':
40+
res = self.delete()
41+
if res['found'] == True:
42+
self.result['changed'] = True
43+
self.result['msg'] = self.role_name + " has been deleted."
44+
return
45+
46+
elif self.state == 'present':
47+
pre_role = self.get()
48+
self.result['foo1'] = pre_role.raw
49+
res = self.put()
50+
51+
if res.raw['role']['created'] == True:
52+
self.result['changed'] = True
53+
self.result['msg'] = self.role_name + " has been created."
54+
return
55+
56+
self.result['foo2'] = self.get().raw
57+
if pre_role.raw != self.get().raw:
58+
self.result['changed'] = True
59+
self.result['msg'] = self.role_name + " has been updated"
60+
return
61+
62+
return
63+
64+
65+
def get(self):
66+
return self.client.security.get_role(name=self.role_name)
67+
68+
69+
def put(self):
70+
return self.client.security.put_role(name=self.role_name, cluster=self.cluster, indices=self.indicies)
71+
72+
73+
def delete(self):
74+
return self.client.security.delete_role(name=self.role_name)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/python
2+
3+
from __future__ import (absolute_import, division, print_function)
4+
__metaclass__ = type
5+
6+
from ansible.module_utils.basic import AnsibleModule
7+
from ansible_collections.netways.elasticstack.plugins.module_utils.api import (
8+
Role
9+
)
10+
11+
def run_module():
12+
'''
13+
Elasticsearch user management.
14+
15+
```
16+
netways.elasticstack.elasticsearch_role:
17+
name: new-role
18+
cluster:
19+
- manage_own_api_key
20+
- delegate_pki
21+
indicies:
22+
- names:
23+
- foobar
24+
privileges:
25+
- read
26+
- write
27+
state: present
28+
host: https://localhost:9200
29+
auth_user: elastic
30+
auth_pass: changeMe123!
31+
verify_certs: false
32+
ca_certs: /etc/elasticsearch/certs/http_ca.crt
33+
```
34+
'''
35+
36+
# get role
37+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-role.html
38+
39+
# create or update role
40+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-role.html
41+
42+
module = AnsibleModule(
43+
argument_spec=dict(
44+
name=dict(type=str, required=True),
45+
cluster=dict(type=list, required=False),
46+
indicies=dict(type=list, required=False), # indicies.{n}.name, indicies.{n}.privileges
47+
state=dict(type=str, required=False, default='present'),
48+
host=dict(type=str, required=True),
49+
auth_user=dict(type=str, required=True),
50+
auth_pass=dict(type=str, required=True),
51+
ca_certs=dict(type=str, required=False),
52+
verify_certs=dict(type=bool, required=False, default=True)
53+
)
54+
)
55+
56+
result = dict(
57+
failed=False,
58+
changed=False
59+
)
60+
61+
role = Role(
62+
result=result,
63+
role_name=module.params['name'],
64+
cluster=module.params['cluster'],
65+
indicies=module.params['indicies'],
66+
state=module.params['state'],
67+
host=module.params['host'],
68+
auth_user=module.params['auth_user'],
69+
auth_pass=module.params['auth_pass'],
70+
ca_certs=module.params['ca_certs'],
71+
verify_certs=module.params['verify_certs'],
72+
)
73+
74+
result = role.return_result()
75+
76+
module.exit_json(**result)
77+
78+
79+
if __name__ == "__main__":
80+
run_module()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/python
2+
3+
# Copyright (c) 2024, Tobias Bauriedel <tobias.bauriedel@netways.de>
4+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
5+
# https://www.gnu.org/licenses/gpl-3.0.txt)
6+
7+
from ansible.module_utils.basic import AnsibleModule
8+
from ansible_collections.netways.elasticstack.plugins.module_utils.elasticsearch_api import (
9+
UserObject,
10+
)
11+
from __future__ import (absolute_import, division, print_function)
12+
__metaclass__ = type
13+
14+
15+
def run_module():
16+
'''
17+
Elasticsearch user management.
18+
'''
19+
20+
# https://github.com/NETWAYS/ansible-collection-elasticstack/blob/main/roles/logstash/tasks/logstash-security.yml#L405-L472
21+
22+
# get user
23+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-user.html
24+
25+
# create or update user
26+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-user.html
27+
28+
# delete user
29+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-user.html
30+
31+
# enable user
32+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-enable-user.html
33+
34+
# disable user
35+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-disable-user.html
36+
37+
module = AnsibleModule(
38+
argument_spec=dict(
39+
name=dict(type=str, required=True),
40+
fullname=dict(type=str, required=False),
41+
password=dict(type=str, required=True),
42+
email=dict(type=str, required=False),
43+
roles=dict(type=list, required=True),
44+
metadata=dict(type=dict, required=False),
45+
state=dict(type=str, required=False, default="present"), # 'present' or 'absent'
46+
enabled=dict(type=bool, required=False, default=True), # True=enabled & False=disabled
47+
endpoint=dict(type=str, required=False, default="https://localhost:9200"),
48+
ca=dict(type=str, required=True), # Path to ca to authenticate API requests
49+
es_version=dict(type=int, required=False, default=8), # Elasticsearch version
50+
)
51+
)
52+
53+
# Check if provided state is valid
54+
valid_states = list("present", "absent")
55+
if module.params['state'] not in valid_states:
56+
module.exit_json(
57+
failed=True,
58+
changed=False,
59+
stderr="Invalid state provided. Use 'present' or 'absent."
60+
)
61+
62+
user = UserObject(module)
63+
64+
module.exit_json(
65+
debug=user
66+
)
67+
68+
# Block 1
69+
# if (current state == configured state) && (current properties == configured properties) -> exit
70+
71+
# if (current enabled != configured enable) -> change
72+
73+
# if current state != configured state -> create or delete (based on configured state)
74+
75+
# if (current state == configured state) && (current properties != configured properties) -> update user properties
76+
77+
# Block 2
78+
79+
if __name__ == "__main__":
80+
run_module()

0 commit comments

Comments
 (0)