diff --git a/docs/role-logstash.md b/docs/role-logstash.md index 8e33259a..55187128 100644 --- a/docs/role-logstash.md +++ b/docs/role-logstash.md @@ -70,12 +70,13 @@ Aside from `logstash.yml` we can manage Logstashs pipelines. * *logstash_elasticsearch*: Address of Elasticsearch instance for default output (default: list of Elasticsearch nodes from `elasticsearch` role or `localhost` when used standalone) * *logstash_security*: Enable X-Security (No default set, but will be activated when in full stack mode) * *logstash_user*: Name of the user to connect to Elasticsearch (Default: `logstash_writer`) -* *logstash_password_hash*: Generate and use a hash from your `logstash_password` (default: `true`) -* *logstash_password_hash_algorithm*: Password hashing algorithms. Value must be same as `xpack.security.authc.password_hashing.algorithm` (default: `bcrypt`) -* *logstash_password_salt_length*: base64 encoded Salt character lenght. This value must be integer and must be compatible to the selected password hashing algorithms (default: `22`) -* *logstash_password_hash_salt_seed*: A seed to generate random but idempotent salt on the elasticstack ca host. The salt will be used to create idempotent logstash hashed user password (default: `SeedChangeMe`) -* *logstash_password*: Password of Elasticsearch user. It must be at least 6 characters long (default: `password`) -* *logstash_user_indices*: Indices the user has access to (default: `'"ecs-logstash*", "logstash*", "logs*"'`) +* *logstash_user_email*: email-address that is linked with the logstash_user (Default: `""`) +* *logstash_user_fullname*: fullname that is linked with the logstash_user (Default: `Internal Logstash User`) +* *logstash_user_password*: Password of `logstash_user` in Elasticsearch. It must be at least 6 characters long (default: `password`) +* *logstash_role_name*: Name of the logstash role that is getting created (Default: `logstash_writer`) +* *logstash_role_cluster_privileges*: Cluster privileges the role has access to (default: `"manage_index_templates", "monitor", "manage_ilm"`) +* *logstash_role_indicies_names*: Indices the role has access to (default: `"ecs-logstash*", "logstash*", "logs*"`) +* *logstash_role_indicies_privileges*: Index permissions the role has on `logstash_role_indicies_names` (default: `"write", "create", "delete", "create_index", "manage", "manage_ilm"`) * *logstash_reset_writer_role*: Reset user and role with every run: (default: `true`) * *logstash_validate_after_inactivity*: How long should logstash wait, before starting a new connection and leave the old one with elasticsearch, when the connection with elasticsearch get lost: (Default: `300`). * *logstash_queue_type*: What kind of queue should Logstash use per default: (Default: `persisted`, alternative: `memory`) diff --git a/molecule/elasticstack_default/molecule.yml b/molecule/elasticstack_default/molecule.yml index 122ee248..51536d2b 100644 --- a/molecule/elasticstack_default/molecule.yml +++ b/molecule/elasticstack_default/molecule.yml @@ -32,5 +32,7 @@ platforms: pre_build_image: true provisioner: name: ansible + env: + ANSIBLE_VERBOSITY: 3 verifier: name: ansible diff --git a/roles/elasticstack/tasks/packages.yml b/roles/elasticstack/tasks/packages.yml index 36a2f3f7..0b5b59c1 100644 --- a/roles/elasticstack/tasks/packages.yml +++ b/roles/elasticstack/tasks/packages.yml @@ -1,5 +1,4 @@ --- - - name: Update apt cache. ansible.builtin.apt: update_cache: yes @@ -20,3 +19,14 @@ - renew_beats_cert - renew_es_cert - renew_logstash_cert + +- name: Install packages for module dependencies + ansible.builtin.package: + name: + - python3 + - python3-pip + +- name: Install Elasticsearch Python Module + ansible.builtin.pip: + name: + - elasticsearch diff --git a/roles/logstash/defaults/main.yml b/roles/logstash/defaults/main.yml index 2a2a7690..ecab500e 100644 --- a/roles/logstash/defaults/main.yml +++ b/roles/logstash/defaults/main.yml @@ -45,14 +45,26 @@ logstash_forwarder_queue_max_bytes: 1gb logstash_sniffing: false # logstash security -logstash_password_hash: true -logstash_password_hash_algorithm: bcrypt -logstash_password_salt_length: 22 +logstash_role_cluster_privileges: + - manage_index_templates + - monitor + - manage_ilm +logstash_role_indicies_names: + - "ecs-logstash*" + - "logstash*" + - "logs*" +logstash_role_indicies_privileges: + - write + - create + - delete + - create_index + - manage + - manage_ilm +logstash_role_name: logstash_writer logstash_user: logstash_writer -logstash_password: password -logstash_password_hash_salt_length: 22 -logstash_password_hash_salt_seed: SeedChangeMe -logstash_user_indices: '"ecs-logstash*", "logstash*", "logs*"' +logstash_user_password: password +logstash_user_email: "" +logstash_user_fullname: "Internal Logstash User" logstash_reset_writer_role: true logstash_tls_key_passphrase: LogstashChangeMe diff --git a/roles/logstash/tasks/logstash-security.yml b/roles/logstash/tasks/logstash-security.yml index e83b6c07..605c50aa 100644 --- a/roles/logstash/tasks/logstash-security.yml +++ b/roles/logstash/tasks/logstash-security.yml @@ -348,12 +348,7 @@ - name: Check the length of logstash user password ansible.builtin.fail: msg: logstash user password must be at least 6 characters long. - when: logstash_password | length < 6 - -- name: Set password hash salt as a fact - ansible.builtin.set_fact: - logstash_password_hash_salt: "{{ lookup('password', '/dev/null', chars=['ascii_lowercase', 'digits'], length=logstash_password_hash_salt_length, seed=logstash_password_hash_salt_seed) }}" - when: logstash_password_hash | bool and inventory_hostname == elasticstack_ca + when: logstash_user_password | length < 6 - name: Fetch Elastic password # noqa: risky-shell-pipe ansible.builtin.shell: > @@ -368,81 +363,32 @@ - configuration - logstash_configuration -- name: Set elasticsearch security-api base url for elasticsearch > 7 - ansible.builtin.set_fact: - security_api_base_url: "https://{{ hostvars[elasticstack_ca].ansible_default_ipv4.address }}:{{ elasticstack_elasticsearch_http_port }}/_security/" - when: elasticstack_release | int > 7 - -- name: Set elasticsearch security-api base url for elasticsearch < 8 - ansible.builtin.set_fact: - security_api_base_url: "https://{{ hostvars[elasticstack_ca].ansible_default_ipv4.address }}:{{ elasticstack_elasticsearch_http_port }}/_xpack/security/" - when: elasticstack_release | int < 8 - -- name: Check for logstash_writer role - ansible.builtin.uri: - url: "{{ security_api_base_url }}role/logstash_writer" - ca_path: "{{ elasticstack_ca_dir }}/ca.crt" - user: elastic - password: "{{ logstash_elasticstack_password.stdout }}" - register: check_logstash_writer_role_response - delegate_to: "{{ elasticstack_ca }}" - failed_when: false - changed_when: false - no_log: "{{ elasticstack_no_log }}" - run_once: true - -- name: Set logstash_writer_role_present - ansible.builtin.set_fact: - logstash_writer_role_present: true - when: check_logstash_writer_role_response.json.logstash_writer is defined - -- name: Put logstash_writer role into Elasticsearch if not present - ansible.builtin.uri: - url: "{{ security_api_base_url }}role/logstash_writer" - ca_path: "{{ elasticstack_ca_dir }}/ca.crt" - user: elastic - password: "{{ logstash_elasticstack_password.stdout }}" - method: PUT - headers: - Content-Type: application/json - body: "{{ lookup('template', 'logstash_writer_role.j2') }}" - body_format: json - register: put_logstash_writer_role_response - when: logstash_writer_role_present is not defined - delegate_to: "{{ elasticstack_ca }}" - failed_when: not put_logstash_writer_role_response.json.role.created | bool - run_once: true - -- name: Check for logstash_writer user - ansible.builtin.uri: - url: "{{ security_api_base_url }}user/{{ logstash_user }}" - ca_path: "{{ elasticstack_ca_dir }}/ca.crt" - user: elastic - password: "{{ logstash_elasticstack_password.stdout }}" - register: check_logstash_writer_user_response - delegate_to: "{{ elasticstack_ca }}" - failed_when: false - changed_when: false - run_once: true - -- name: Set logstash_writer_user_present - ansible.builtin.set_fact: - logstash_writer_user_present: true - when: check_logstash_writer_user_response.json.logstash_writer.username is defined and check_logstash_writer_user_response.json.logstash_writer.username == "logstash_writer" - -- name: Put logstash_writer user into Elasticsearch if not present - ansible.builtin.uri: - url: "{{ security_api_base_url }}user/{{ logstash_user }}" - ca_path: "{{ elasticstack_ca_dir }}/ca.crt" - user: elastic - password: "{{ logstash_elasticstack_password.stdout }}" - method: PUT - headers: - Content-Type: application/json - body: "{{ lookup('template', 'logstash_writer_user.j2') }}" - body_format: json - register: put_logstash_writer_user_response - when: logstash_writer_user_present is not defined - delegate_to: "{{ elasticstack_ca }}" - run_once: true - failed_when: not put_logstash_writer_user_response.json.created +- name: Create logstash role {{ logstash_role_name }} + netways.elasticstack.elasticsearch_role: + name: "{{ logstash_role_name }}" + cluster: "{{ logstash_role_cluster_privileges }}" + indicies: + - names: "{{ logstash_role_indicies_names }}" + privileges: "{{ logstash_role_indicies_privileges }}" + state: present + host: "https://{{ hostvars[elasticstack_ca].ansible_default_ipv4.address }}:{{ elasticstack_elasticsearch_http_port }}" + auth_user: elastic + auth_pass: "{{ logstash_elasticstack_password.stdout }}" + verify_certs: true + ca_certs: "{{ logstash_certs_dir }}/ca.crt" + +- name: Create logstash user {{ logstash_user }} + netways.elasticstack.elasticsearch_user: + name: "{{ logstash_user }}" + fullname: "{{ logstash_user_fullname }}" + password: "{{ logstash_user_password }}" + email: "{{ logstash_user_email }}" + roles: + - "{{ logstash_role_name }}" + enabled: true + state: present + host: "https://{{ hostvars[elasticstack_ca].ansible_default_ipv4.address }}:{{ elasticstack_elasticsearch_http_port }}" + auth_user: elastic + auth_pass: "{{ logstash_elasticstack_password.stdout }}" + verify_certs: false + ca_certs: "{{ logstash_certs_dir }}/ca.crt" diff --git a/roles/logstash/templates/elasticsearch-output.conf.j2 b/roles/logstash/templates/elasticsearch-output.conf.j2 index 1f93292c..7c9884e6 100644 --- a/roles/logstash/templates/elasticsearch-output.conf.j2 +++ b/roles/logstash/templates/elasticsearch-output.conf.j2 @@ -37,7 +37,7 @@ output { cacert => "{{ logstash_certs_dir }}/ca.crt" ssl => true user => "{{ logstash_user }}" - password => "{{ logstash_password }}" + password => "{{ logstash_user_password }}" {% endif %} } } diff --git a/roles/logstash/templates/logstash_writer_role.j2 b/roles/logstash/templates/logstash_writer_role.j2 deleted file mode 100644 index fc990cbe..00000000 --- a/roles/logstash/templates/logstash_writer_role.j2 +++ /dev/null @@ -1,9 +0,0 @@ -{ - "cluster": ["manage_index_templates", "monitor", "manage_ilm"], - "indices": [ - { - "names": [ {{ logstash_user_indices }} ], - "privileges": ["write","create","delete","create_index","manage","manage_ilm"] - } - ] -} diff --git a/roles/logstash/templates/logstash_writer_user.j2 b/roles/logstash/templates/logstash_writer_user.j2 deleted file mode 100644 index 5c21a745..00000000 --- a/roles/logstash/templates/logstash_writer_user.j2 +++ /dev/null @@ -1,12 +0,0 @@ -{ -{% if logstash_password_hash | bool %} -{# using a fixed salt is neccessary for idempotency, will be generated as a set fact. -rounds specifies the bcrypt version. The default version in Ansible module is 12. The acceptable one is 10 on elasticsearch 7. -On elasticsearch 8, the 12 and 10 versions will work, so we should use 10 until the support of 7 stops #} - "password_hash" : "{{ logstash_password | password_hash( hashtype=logstash_password_hash_algorithm, salt=logstash_password_hash_salt, ident='2a', rounds=10 ) }}", -{% else %} - "password" : "{{ logstash_password }}", -{% endif %} - "roles" : [ "logstash_writer"], - "full_name" : "Internal Logstash User" -}