feat: manage users
This commit is contained in:
parent
aaeea21e54
commit
f1ba54d2ad
10 changed files with 228 additions and 26 deletions
|
@ -7,6 +7,7 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- feat: manage user
|
||||||
- feat: add variable to set major version
|
- feat: add variable to set major version
|
||||||
- feat: add ilm policy
|
- feat: add ilm policy
|
||||||
- test: add support debian 12
|
- test: add support debian 12
|
||||||
|
@ -17,6 +18,7 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- major default version is 8
|
||||||
- replace kitchen to molecule
|
- replace kitchen to molecule
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -16,6 +16,8 @@ Install and configure Elasticsearch
|
||||||
|
|
||||||
* `elasticsearch_major_version` - set the major version (default: `7`)
|
* `elasticsearch_major_version` - set the major version (default: `7`)
|
||||||
* `elasticsearch_heap_size` - set the heap size (default: `1g`)
|
* `elasticsearch_heap_size` - set the heap size (default: `1g`)
|
||||||
|
* `elasticsearch_api_user` - set the admin user (default: `elastic`)
|
||||||
|
* `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))
|
* `elasticsearch_config` - hash with the configuration (see [elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html))
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
---
|
---
|
||||||
elasticsearch_major_version: 7
|
elasticsearch_major_version: 8
|
||||||
|
elasticsearch_api_user: elastic
|
||||||
elasticsearch_heap_size: 1g
|
elasticsearch_heap_size: 1g
|
||||||
elasticsearch_config: {}
|
elasticsearch_config: {}
|
||||||
elasticsearch_default_config:
|
elasticsearch_default_config:
|
||||||
path.data: /var/lib/elasticsearch
|
path.data: /var/lib/elasticsearch
|
||||||
path.logs: /var/log/elasticsearch
|
path.logs: /var/log/elasticsearch
|
||||||
|
xpack.security.transport.ssl.enabled: false
|
||||||
|
xpack.security.http.ssl.enabled: false
|
||||||
elasticsearch_full_config: "{{ elasticsearch_default_config | combine(elasticsearch_config) }}"
|
elasticsearch_full_config: "{{ elasticsearch_default_config | combine(elasticsearch_config) }}"
|
||||||
elasticsearch_index_templates: {}
|
elasticsearch_index_templates: {}
|
||||||
elasticsearch_ilm_policies: {}
|
elasticsearch_ilm_policies: {}
|
||||||
|
|
52
library/elasticsearch_init_password.py
Normal file
52
library/elasticsearch_init_password.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import *
|
||||||
|
from ansible.module_utils.elasticsearch_api import *
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class ElasticsearchInitPassword:
|
||||||
|
def __init__(self, api_url, api_user, api_password):
|
||||||
|
self.api = ElasticsearchApi(
|
||||||
|
api_url,
|
||||||
|
api_user,
|
||||||
|
api_password
|
||||||
|
)
|
||||||
|
self.user = api_user
|
||||||
|
self.password = api_password
|
||||||
|
|
||||||
|
def is_set(self):
|
||||||
|
status_code, _ = self.api.get('_cluster/health')
|
||||||
|
if status_code == 401:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def change(self):
|
||||||
|
subprocess.run(
|
||||||
|
['/usr/share/elasticsearch/bin/elasticsearch-reset-password', '-u', self.user, '-b', '-i'],
|
||||||
|
input='{}\n{}'.format(self.password, self.password).encode()
|
||||||
|
)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
fields = {
|
||||||
|
'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 },
|
||||||
|
}
|
||||||
|
module = AnsibleModule(argument_spec=fields)
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
init = ElasticsearchInitPassword(
|
||||||
|
module.params['api_url'],
|
||||||
|
module.params['api_user'],
|
||||||
|
module.params['api_password'],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not init.is_set():
|
||||||
|
init.change()
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
module.exit_json(changed=changed)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
106
library/elasticsearch_user.py
Normal file
106
library/elasticsearch_user.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import *
|
||||||
|
from ansible.module_utils.elasticsearch_api import *
|
||||||
|
|
||||||
|
class ElasticsearchUser:
|
||||||
|
def __init__(self, api_url, api_user, api_password, name, password, roles):
|
||||||
|
self.api = ElasticsearchApi(
|
||||||
|
api_url,
|
||||||
|
api_user,
|
||||||
|
api_password
|
||||||
|
)
|
||||||
|
self.api_url = api_url
|
||||||
|
self.name = name
|
||||||
|
self.password = password
|
||||||
|
self.roles = roles
|
||||||
|
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 roles_have_changed(self):
|
||||||
|
for role in self.roles:
|
||||||
|
if role not in self.data['roles']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for role in self.data['roles']:
|
||||||
|
if role not in self.roles:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def password_has_changed(self):
|
||||||
|
api = ElasticsearchApi(
|
||||||
|
self.api_url,
|
||||||
|
self.name,
|
||||||
|
self.password
|
||||||
|
)
|
||||||
|
status_code, _ = api.get('_cluster/health')
|
||||||
|
if status_code == 401:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_changed(self):
|
||||||
|
if self.roles_have_changed():
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.password_has_changed():
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
self.api.put(
|
||||||
|
'_security/user/{}'.format(self.name),
|
||||||
|
{
|
||||||
|
'password': self.password,
|
||||||
|
'roles': self.roles
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.api.delete('_security/user/{}'.format(self.name))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
fields = {
|
||||||
|
'name': { 'type': 'str', 'required': True },
|
||||||
|
'password': { 'type': 'str', 'required': True, 'no_log': 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, 'no_log': True },
|
||||||
|
'state': { 'type': 'str', 'default': 'present', 'choice': ['present', 'absent'] },
|
||||||
|
}
|
||||||
|
module = AnsibleModule(argument_spec=fields)
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
user = ElasticsearchUser(
|
||||||
|
module.params['api_url'],
|
||||||
|
module.params['api_user'],
|
||||||
|
module.params['api_password'],
|
||||||
|
module.params['name'],
|
||||||
|
module.params['password'],
|
||||||
|
module.params['roles'],
|
||||||
|
)
|
||||||
|
user.get_data()
|
||||||
|
|
||||||
|
if module.params['state'] == 'present':
|
||||||
|
if not user.exist or user.has_changed():
|
||||||
|
user.create()
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
if user.exist:
|
||||||
|
user.delete()
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
module.exit_json(changed=changed)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -5,28 +5,25 @@ import base64
|
||||||
class ElasticsearchApi:
|
class ElasticsearchApi:
|
||||||
def __init__(self, url, user, password):
|
def __init__(self, url, user, password):
|
||||||
self.url = url
|
self.url = url
|
||||||
self.headers = {}
|
self.basic = None
|
||||||
if user and password:
|
if user and password:
|
||||||
token = base64.b64encode('{}:{}',)
|
self.basic = requests.auth.HTTPBasicAuth(user, password)
|
||||||
self.headers = { 'Authorization': 'Basic ' + base64.b64encode({},) }
|
|
||||||
|
|
||||||
def get(self, path):
|
def get(self, path):
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
'{}/{}'.format(self.url, path),
|
'{}/{}'.format(self.url, path),
|
||||||
headers=self.headers
|
auth=self.basic
|
||||||
)
|
)
|
||||||
|
|
||||||
if r.status_code == 500:
|
if r.status_code == 500:
|
||||||
raise Exception('Server return 500 error: {}'.format(r.text))
|
raise Exception('Server return 500 error: {}'.format(r.text))
|
||||||
elif r.status_code == 401:
|
|
||||||
raise Exception('Authentification has failed')
|
|
||||||
|
|
||||||
return r.status_code, r.json()
|
return r.status_code, r.json()
|
||||||
|
|
||||||
def put(self, path, data):
|
def put(self, path, data):
|
||||||
r = requests.put(
|
r = requests.put(
|
||||||
'{}/{}'.format(self.url, path),
|
'{}/{}'.format(self.url, path),
|
||||||
headers=self.headers,
|
auth=self.basic,
|
||||||
json=data
|
json=data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,13 @@
|
||||||
roles:
|
roles:
|
||||||
- ansible-role-elasticsearch
|
- ansible-role-elasticsearch
|
||||||
vars:
|
vars:
|
||||||
|
elasticsearch_password: mysecret
|
||||||
elasticsearch_heap_size: 512m
|
elasticsearch_heap_size: 512m
|
||||||
|
elasticsearch_users:
|
||||||
|
toto:
|
||||||
|
password: supers3cret
|
||||||
|
roles:
|
||||||
|
- viewer
|
||||||
elasticsearch_index_templates:
|
elasticsearch_index_templates:
|
||||||
test:
|
test:
|
||||||
index_patterns:
|
index_patterns:
|
||||||
|
|
|
@ -11,6 +11,8 @@ platforms:
|
||||||
command: /bin/systemd
|
command: /bin/systemd
|
||||||
capabilities:
|
capabilities:
|
||||||
- SYS_ADMIN
|
- SYS_ADMIN
|
||||||
|
published_ports:
|
||||||
|
- 127.0.0.1:5601:5601
|
||||||
lint: |
|
lint: |
|
||||||
set -e
|
set -e
|
||||||
yamllint .
|
yamllint .
|
||||||
|
|
|
@ -2,31 +2,36 @@ import os
|
||||||
import testinfra.utils.ansible_runner
|
import testinfra.utils.ansible_runner
|
||||||
|
|
||||||
def test_packages(host):
|
def test_packages(host):
|
||||||
package = host.package('elasticsearch')
|
package = host.package('elasticsearch')
|
||||||
assert package.is_installed
|
assert package.is_installed
|
||||||
|
|
||||||
def test_config_file(host):
|
def test_config_file(host):
|
||||||
config = host.file('/etc/elasticsearch/elasticsearch.yml')
|
config = host.file('/etc/elasticsearch/elasticsearch.yml')
|
||||||
assert config.user == 'root'
|
assert config.user == 'root'
|
||||||
assert config.group == 'elasticsearch'
|
assert config.group == 'elasticsearch'
|
||||||
assert config.mode == 0o640
|
assert config.mode == 0o640
|
||||||
assert config.contains('path.data: /var/lib/elasticsearch')
|
assert config.contains('path.data: /var/lib/elasticsearch')
|
||||||
|
|
||||||
def test_service(host):
|
def test_service(host):
|
||||||
service = host.service('elasticsearch')
|
service = host.service('elasticsearch')
|
||||||
assert service.is_running
|
assert service.is_running
|
||||||
assert service.is_enabled
|
assert service.is_enabled
|
||||||
|
|
||||||
def test_socket(host):
|
def test_socket(host):
|
||||||
for port in [9200, 9300]:
|
for port in [9200, 9300]:
|
||||||
socket = host.socket('tcp://127.0.0.1:%d' % (port))
|
socket = host.socket('tcp://127.0.0.1:%d' % (port))
|
||||||
assert socket.is_listening
|
assert socket.is_listening
|
||||||
|
|
||||||
def test_java_memory(host):
|
def test_java_memory(host):
|
||||||
process = host.process.get(user='elasticsearch', comm='java')
|
process = host.process.filter(user='elasticsearch', comm='java')
|
||||||
assert '-Xms512m' in process.args
|
assert '-Xms512m' in process[1].args
|
||||||
assert '-Xmx512m' in process.args
|
assert '-Xmx512m' in process[1].args
|
||||||
|
|
||||||
def test_elasticsearch_template(host):
|
def test_elasticsearch_template(host):
|
||||||
result = host.check_output('curl -v http://127.0.0.1:9200/_template/test')
|
result = host.check_output('curl -v -u elastic:mysecret http://127.0.0.1:9200/_template/test')
|
||||||
assert '"number_of_replicas":"1"' in result
|
assert '"number_of_replicas":"1"' 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
|
||||||
|
assert '"roles":["viewer"]' in result
|
||||||
|
|
|
@ -1,8 +1,31 @@
|
||||||
---
|
---
|
||||||
|
- name: Init elastic password
|
||||||
|
elasticsearch_init_password:
|
||||||
|
api_user: "{{ elasticsearch_api_user }}"
|
||||||
|
api_password: "{{ elasticsearch_password }}"
|
||||||
|
run_once: true
|
||||||
|
tags: elasticsearch
|
||||||
|
|
||||||
|
- name: Manage users
|
||||||
|
elasticsearch_user:
|
||||||
|
name: "{{ item.key }}"
|
||||||
|
password: "{{ item.value.password }}"
|
||||||
|
roles: "{{ item.value.roles | default(omit) }}"
|
||||||
|
api_user: "{{ elasticsearch_api_user }}"
|
||||||
|
api_password: "{{ elasticsearch_password }}"
|
||||||
|
state: "{{ item.value.state | default('present') }}"
|
||||||
|
loop: "{{ elasticsearch_users | dict2items }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.key }}"
|
||||||
|
run_once: true
|
||||||
|
tags: elasticsearch
|
||||||
|
|
||||||
- name: Copy ilm policies
|
- name: Copy ilm policies
|
||||||
elasticsearch_ilm_policy:
|
elasticsearch_ilm_policy:
|
||||||
name: "{{ item.key }}"
|
name: "{{ item.key }}"
|
||||||
phases: "{{ item.value | default({}) }}"
|
phases: "{{ item.value | default({}) }}"
|
||||||
|
api_user: "{{ elasticsearch_api_user }}"
|
||||||
|
api_password: "{{ elasticsearch_password }}"
|
||||||
loop: "{{ elasticsearch_ilm_policies | dict2items }}"
|
loop: "{{ elasticsearch_ilm_policies | dict2items }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.key }}"
|
label: "{{ item.key }}"
|
||||||
|
@ -15,6 +38,10 @@
|
||||||
index_patterns: "{{ item.value.index_patterns }}"
|
index_patterns: "{{ item.value.index_patterns }}"
|
||||||
settings: "{{ item.value.settings | default({}) }}"
|
settings: "{{ item.value.settings | default({}) }}"
|
||||||
mappings: "{{ item.value.mappings | default({}) }}"
|
mappings: "{{ item.value.mappings | default({}) }}"
|
||||||
|
api_user: "{{ elasticsearch_api_user }}"
|
||||||
|
api_password: "{{ elasticsearch_password }}"
|
||||||
loop: "{{ elasticsearch_index_templates | dict2items }}"
|
loop: "{{ elasticsearch_index_templates | dict2items }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.key }}"
|
||||||
run_once: true
|
run_once: true
|
||||||
tags: elasticsearch
|
tags: elasticsearch
|
||||||
|
|
Loading…
Reference in a new issue