From 0bc3cb0bbc8fd46f6ee7e4440ba339ffa2276c4a Mon Sep 17 00:00:00 2001 From: Amunagala-itential Date: Thu, 25 Jun 2026 09:41:39 -0400 Subject: [PATCH 1/3] Align ELK role TLS paths and variables to deployer standard - Rename _tls_dest_dir/_tls_src_dir to _pki_base_dir/_pki_src_dir across all four ELK roles (filebeat, logstash, kibana, elasticsearch) - Add _pki_private_subdir and _pki_private_dir; move private key dest to private/ subdirectory to match deployer layout - Fix elasticsearch_pki_base_dir from /etc/elasticsearch/certs to /etc/pki/elasticsearch - Rename _tls_*_filename to _tls_*_file and _tls_ca_cert_* to _tls_ca_* - Update copy-certs tasks to create both base and private directories - Update all templates and docs to use the new variable names and paths --- docs/itential_elk_guide.md | 21 ++++++++-------- docs/itential_elk_lld.md | 8 +++---- roles/elasticsearch/README.md | 10 ++++---- roles/elasticsearch/defaults/main.yml | 24 ++++++++++--------- roles/elasticsearch/tasks/copy-certs.yml | 20 +++++++++++----- .../templates/elasticsearch.yml.j2 | 4 ++-- roles/filebeat/README.md | 9 ++++--- roles/filebeat/defaults/main.yml | 24 ++++++++++--------- roles/filebeat/tasks/copy-certs.yml | 16 +++++++++---- roles/filebeat/templates/filebeat.yml.j2 | 4 ++-- roles/kibana/README.md | 9 ++++--- roles/kibana/defaults/main.yml | 24 ++++++++++--------- roles/kibana/tasks/copy-certs.yml | 16 +++++++++---- roles/kibana/templates/kibana.yml.j2 | 2 +- roles/logstash/README.md | 9 ++++--- roles/logstash/defaults/main.yml | 24 ++++++++++--------- roles/logstash/tasks/copy-certs.yml | 16 +++++++++---- roles/logstash/templates/pipeline.conf.j2 | 6 ++--- 18 files changed, 148 insertions(+), 98 deletions(-) diff --git a/docs/itential_elk_guide.md b/docs/itential_elk_guide.md index 7ffcf2f..8a74aba 100644 --- a/docs/itential_elk_guide.md +++ b/docs/itential_elk_guide.md @@ -334,13 +334,14 @@ Before running the ELK playbooks: | Host Group | Cert path variables | |---|---| -| `elasticsearch` | `elasticsearch_tls_cert`, `elasticsearch_tls_key`, `elasticsearch_tls_ca_cert` | -| `logstash` | `logstash_tls_cert`, `logstash_tls_key`, `logstash_tls_ca_cert` | -| `kibana` | `kibana_tls_cert`, `kibana_tls_key`, `kibana_tls_ca_cert` | -| All Filebeat hosts | `filebeat_tls_cert`, `filebeat_tls_key`, `filebeat_tls_ca_cert` | +| `elasticsearch` | `elasticsearch_pki_base_dir`, `elasticsearch_tls_cert_file`, `elasticsearch_tls_key_file`, `elasticsearch_tls_ca_file` | +| `logstash` | `logstash_pki_base_dir`, `logstash_tls_cert_file`, `logstash_tls_key_file`, `logstash_tls_ca_file` | +| `kibana` | `kibana_pki_base_dir`, `kibana_tls_cert_file`, `kibana_tls_key_file`, `kibana_tls_ca_file` | +| All Filebeat hosts | `filebeat_pki_base_dir`, `filebeat_tls_cert_file`, `filebeat_tls_key_file`, `filebeat_tls_ca_file` | -All certificate path variables have defaults under `/etc//certs/`. Certificates -should be issued from the same internal CA used for the rest of the Itential deployment. +Certificate directories follow the `/etc/pki/` standard. Private keys are placed +in a `private/` subdirectory under the base PKI directory. Certificates should be issued +from the same internal CA used for the rest of the Itential deployment. --- @@ -411,10 +412,10 @@ same internal CA. For a minimal deployment, you need: -- **Elasticsearch host:** `ca.crt`, `elasticsearch.crt`, `elasticsearch.key` under `/etc/elasticsearch/certs/` -- **Logstash host:** `ca.crt`, `logstash.crt`, `logstash.key` under `/etc/logstash/certs/` -- **Kibana host:** `ca.crt`, `kibana.crt`, `kibana.key` under `/etc/kibana/certs/` -- **All Filebeat hosts:** `ca.crt`, `filebeat.crt`, `filebeat.key` under `/etc/filebeat/certs/` +- **Elasticsearch host:** `ca.crt` and `.crt` under `/etc/pki/elasticsearch/`; `.key` under `/etc/pki/elasticsearch/private/` +- **Logstash host:** `ca.crt` and `.crt` under `/etc/pki/logstash/`; `.key` under `/etc/pki/logstash/private/` +- **Kibana host:** `ca.crt` and `.crt` under `/etc/pki/kibana/`; `.key` under `/etc/pki/kibana/private/` +- **All Filebeat hosts:** `ca.crt` and `.crt` under `/etc/pki/filebeat/`; `.key` under `/etc/pki/filebeat/private/` ### Step 3 — Deploy the Full ELK Stack diff --git a/docs/itential_elk_lld.md b/docs/itential_elk_lld.md index fb6b0f3..390ed0c 100644 --- a/docs/itential_elk_lld.md +++ b/docs/itential_elk_lld.md @@ -351,8 +351,8 @@ input { beats { port => 5044 ssl => true - ssl_certificate => "/etc/logstash/certs/logstash.crt" - ssl_key => "/etc/logstash/certs/logstash.key" + ssl_certificate => "/etc/pki/logstash/.crt" + ssl_key => "/etc/pki/logstash/private/.key" } } @@ -451,7 +451,7 @@ output { index => "itential-failures-%{+yyyy.MM.dd}" user => "logstash_writer" password => "${ELASTIC_PASSWORD}" - ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"] + ssl_certificate_authorities => ["/etc/pki/logstash/ca.crt"] } } @@ -461,7 +461,7 @@ output { index => "itential-logs-%{app}-%{+yyyy.MM.dd}" user => "logstash_writer" password => "${ELASTIC_PASSWORD}" - ssl_certificate_authorities => ["/etc/logstash/certs/ca.crt"] + ssl_certificate_authorities => ["/etc/pki/logstash/ca.crt"] } } ``` diff --git a/roles/elasticsearch/README.md b/roles/elasticsearch/README.md index 1507800..748a1fc 100644 --- a/roles/elasticsearch/README.md +++ b/roles/elasticsearch/README.md @@ -42,11 +42,11 @@ These have empty string defaults and must be set in inventory using Ansible Vaul |---|---|---| | `elasticsearch_tls_enabled` | `true` | Enable xpack security and TLS for HTTP and transport | | `elasticsearch_tls_copy_certs` | `true` | Copy certificates from the control node to the target host | -| `elasticsearch_tls_src_dir` | `""` | Directory on the control node containing the certificate files | -| `elasticsearch_tls_dest_dir` | `/etc/elasticsearch/certs` | Directory on the target host where certificates are placed | -| `elasticsearch_tls_cert_filename` | `{{ inventory_hostname }}.crt` | Certificate filename | -| `elasticsearch_tls_key_filename` | `{{ inventory_hostname }}.key` | Private key filename | -| `elasticsearch_tls_ca_cert_filename` | `ca.crt` | CA certificate filename | +| `elasticsearch_pki_src_dir` | `""` | Directory on the control node containing the certificate files | +| `elasticsearch_pki_base_dir` | `/etc/pki/elasticsearch` | Base PKI directory on the target host | +| `elasticsearch_tls_cert_file` | `{{ inventory_hostname }}.crt` | Certificate filename | +| `elasticsearch_tls_key_file` | `{{ inventory_hostname }}.key` | Private key filename | +| `elasticsearch_tls_ca_file` | `ca.crt` | CA certificate filename | ### ILM diff --git a/roles/elasticsearch/defaults/main.yml b/roles/elasticsearch/defaults/main.yml index c8c9f89..f33ec8c 100644 --- a/roles/elasticsearch/defaults/main.yml +++ b/roles/elasticsearch/defaults/main.yml @@ -31,17 +31,19 @@ elasticsearch_heap_size: "1g" # all three flags must be set consistently to avoid a startup conflict. elasticsearch_tls_enabled: true elasticsearch_tls_copy_certs: true -elasticsearch_tls_src_dir: "" -elasticsearch_tls_dest_dir: /etc/elasticsearch/certs -elasticsearch_tls_cert_filename: "{{ inventory_hostname }}.crt" -elasticsearch_tls_cert_src: "{{ elasticsearch_tls_src_dir }}/{{ elasticsearch_tls_cert_filename }}" -elasticsearch_tls_cert_dest: "{{ elasticsearch_tls_dest_dir }}/{{ elasticsearch_tls_cert_filename }}" -elasticsearch_tls_key_filename: "{{ inventory_hostname }}.key" -elasticsearch_tls_key_src: "{{ elasticsearch_tls_src_dir }}/{{ elasticsearch_tls_key_filename }}" -elasticsearch_tls_key_dest: "{{ elasticsearch_tls_dest_dir }}/{{ elasticsearch_tls_key_filename }}" -elasticsearch_tls_ca_cert_filename: ca.crt -elasticsearch_tls_ca_cert_src: "{{ elasticsearch_tls_src_dir }}/{{ elasticsearch_tls_ca_cert_filename }}" -elasticsearch_tls_ca_cert_dest: "{{ elasticsearch_tls_dest_dir }}/{{ elasticsearch_tls_ca_cert_filename }}" +elasticsearch_pki_src_dir: "" +elasticsearch_pki_base_dir: /etc/pki/elasticsearch +elasticsearch_pki_private_subdir: private +elasticsearch_pki_private_dir: "{{ elasticsearch_pki_base_dir }}/{{ elasticsearch_pki_private_subdir }}" +elasticsearch_tls_cert_file: "{{ inventory_hostname }}.crt" +elasticsearch_tls_cert_src: "{{ elasticsearch_pki_src_dir }}/{{ elasticsearch_tls_cert_file }}" +elasticsearch_tls_cert_dest: "{{ elasticsearch_pki_base_dir }}/{{ elasticsearch_tls_cert_file }}" +elasticsearch_tls_key_file: "{{ inventory_hostname }}.key" +elasticsearch_tls_key_src: "{{ elasticsearch_pki_src_dir }}/{{ elasticsearch_tls_key_file }}" +elasticsearch_tls_key_dest: "{{ elasticsearch_pki_private_dir }}/{{ elasticsearch_tls_key_file }}" +elasticsearch_tls_ca_file: ca.crt +elasticsearch_tls_ca_src: "{{ elasticsearch_pki_src_dir }}/{{ elasticsearch_tls_ca_file }}" +elasticsearch_tls_ca_dest: "{{ elasticsearch_pki_base_dir }}/{{ elasticsearch_tls_ca_file }}" # System user/group created by the package elasticsearch_user: elasticsearch diff --git a/roles/elasticsearch/tasks/copy-certs.yml b/roles/elasticsearch/tasks/copy-certs.yml index 0d79a8c..7cf3fbc 100644 --- a/roles/elasticsearch/tasks/copy-certs.yml +++ b/roles/elasticsearch/tasks/copy-certs.yml @@ -7,9 +7,17 @@ - elasticsearch_certificates - elasticsearch_certs block: - - name: Create Elasticsearch TLS directory + - name: Create Elasticsearch PKI base directory ansible.builtin.file: - path: "{{ elasticsearch_tls_dest_dir }}" + path: "{{ elasticsearch_pki_base_dir }}" + state: directory + owner: "{{ elasticsearch_user }}" + group: "{{ elasticsearch_group }}" + mode: "0750" + + - name: Create Elasticsearch PKI private directory + ansible.builtin.file: + path: "{{ elasticsearch_pki_private_dir }}" state: directory owner: "{{ elasticsearch_user }}" group: "{{ elasticsearch_group }}" @@ -20,8 +28,8 @@ block: - name: Copy the CA certificate file ansible.builtin.copy: - src: "{{ elasticsearch_tls_ca_cert_src }}" - dest: "{{ elasticsearch_tls_ca_cert_dest }}" + src: "{{ elasticsearch_tls_ca_src }}" + dest: "{{ elasticsearch_tls_ca_dest }}" mode: "0644" owner: "{{ elasticsearch_user }}" group: "{{ elasticsearch_group }}" @@ -44,7 +52,7 @@ - name: Add Itential CA to system trust store (RedHat) ansible.builtin.copy: - src: "{{ elasticsearch_tls_ca_cert_dest }}" + src: "{{ elasticsearch_tls_ca_dest }}" dest: /etc/pki/ca-trust/source/anchors/itential-ca.crt remote_src: true owner: root @@ -54,7 +62,7 @@ - name: Add Itential CA to system trust store (Debian) ansible.builtin.copy: - src: "{{ elasticsearch_tls_ca_cert_dest }}" + src: "{{ elasticsearch_tls_ca_dest }}" dest: /usr/local/share/ca-certificates/itential-ca.crt remote_src: true owner: root diff --git a/roles/elasticsearch/templates/elasticsearch.yml.j2 b/roles/elasticsearch/templates/elasticsearch.yml.j2 index c19e1f1..65fbb67 100644 --- a/roles/elasticsearch/templates/elasticsearch.yml.j2 +++ b/roles/elasticsearch/templates/elasticsearch.yml.j2 @@ -36,10 +36,10 @@ xpack.security.enabled: {{ elasticsearch_tls_enabled | lower }} xpack.security.transport.ssl.enabled: {{ elasticsearch_tls_enabled | lower }} xpack.security.http.ssl.enabled: {{ elasticsearch_tls_enabled | lower }} {% if elasticsearch_tls_enabled | bool %} -xpack.security.transport.ssl.certificate_authorities: ["{{ elasticsearch_tls_ca_cert_dest }}"] +xpack.security.transport.ssl.certificate_authorities: ["{{ elasticsearch_tls_ca_dest }}"] xpack.security.transport.ssl.certificate: {{ elasticsearch_tls_cert_dest }} xpack.security.transport.ssl.key: {{ elasticsearch_tls_key_dest }} -xpack.security.http.ssl.certificate_authorities: ["{{ elasticsearch_tls_ca_cert_dest }}"] +xpack.security.http.ssl.certificate_authorities: ["{{ elasticsearch_tls_ca_dest }}"] xpack.security.http.ssl.certificate: {{ elasticsearch_tls_cert_dest }} xpack.security.http.ssl.key: {{ elasticsearch_tls_key_dest }} {% endif %} diff --git a/roles/filebeat/README.md b/roles/filebeat/README.md index 2a87fb7..86c10ec 100644 --- a/roles/filebeat/README.md +++ b/roles/filebeat/README.md @@ -19,9 +19,12 @@ Installs and configures Filebeat for the Itential monitoring stack. | `filebeat_log_path` | `/var/log/filebeat` | Log directory | | `filebeat_environment` | `unset` | Environment label attached to every event (e.g. `production`, `staging`) | | `filebeat_tls_enabled` | `true` | Enable TLS for the Logstash or Elasticsearch output | -| `filebeat_tls_ca_cert` | `/etc/filebeat/certs/ca.crt` | Path to the CA certificate used to verify the output host | -| `filebeat_tls_cert` | `/etc/filebeat/certs/filebeat.crt` | Path to the client certificate (PEM) | -| `filebeat_tls_key` | `/etc/filebeat/certs/filebeat.key` | Path to the client private key (PEM) | +| `filebeat_tls_copy_certs` | `true` | Copy certificates from the control node to the target host | +| `filebeat_pki_src_dir` | `""` | Directory on the control node containing the certificate files | +| `filebeat_pki_base_dir` | `/etc/pki/filebeat` | Base PKI directory on the target host | +| `filebeat_tls_cert_file` | `{{ inventory_hostname }}.crt` | Certificate filename | +| `filebeat_tls_key_file` | `{{ inventory_hostname }}.key` | Private key filename | +| `filebeat_tls_ca_file` | `ca.crt` | CA certificate filename | | `filebeat_output_logstash_enabled` | `true` | Send output to Logstash | | `filebeat_output_logstash_hosts` | `["localhost:5044"]` | Logstash hosts | | `filebeat_output_elasticsearch_enabled` | `false` | Send output directly to Elasticsearch | diff --git a/roles/filebeat/defaults/main.yml b/roles/filebeat/defaults/main.yml index 137df54..1672b45 100644 --- a/roles/filebeat/defaults/main.yml +++ b/roles/filebeat/defaults/main.yml @@ -21,17 +21,19 @@ filebeat_service_name: filebeat # TLS — secures the connection to Logstash or Elasticsearch filebeat_tls_enabled: true filebeat_tls_copy_certs: true -filebeat_tls_src_dir: "" -filebeat_tls_dest_dir: /etc/pki/filebeat -filebeat_tls_cert_filename: "{{ inventory_hostname }}.crt" -filebeat_tls_cert_src: "{{ filebeat_tls_src_dir }}/{{ filebeat_tls_cert_filename }}" -filebeat_tls_cert_dest: "{{ filebeat_tls_dest_dir }}/{{ filebeat_tls_cert_filename }}" -filebeat_tls_key_filename: "{{ inventory_hostname }}.key" -filebeat_tls_key_src: "{{ filebeat_tls_src_dir }}/{{ filebeat_tls_key_filename }}" -filebeat_tls_key_dest: "{{ filebeat_tls_dest_dir }}/{{ filebeat_tls_key_filename }}" -filebeat_tls_ca_cert_filename: ca.crt -filebeat_tls_ca_cert_src: "{{ filebeat_tls_src_dir }}/{{ filebeat_tls_ca_cert_filename }}" -filebeat_tls_ca_cert_dest: "{{ filebeat_tls_dest_dir }}/{{ filebeat_tls_ca_cert_filename }}" +filebeat_pki_src_dir: "" +filebeat_pki_base_dir: /etc/pki/filebeat +filebeat_pki_private_subdir: private +filebeat_pki_private_dir: "{{ filebeat_pki_base_dir }}/{{ filebeat_pki_private_subdir }}" +filebeat_tls_cert_file: "{{ inventory_hostname }}.crt" +filebeat_tls_cert_src: "{{ filebeat_pki_src_dir }}/{{ filebeat_tls_cert_file }}" +filebeat_tls_cert_dest: "{{ filebeat_pki_base_dir }}/{{ filebeat_tls_cert_file }}" +filebeat_tls_key_file: "{{ inventory_hostname }}.key" +filebeat_tls_key_src: "{{ filebeat_pki_src_dir }}/{{ filebeat_tls_key_file }}" +filebeat_tls_key_dest: "{{ filebeat_pki_private_dir }}/{{ filebeat_tls_key_file }}" +filebeat_tls_ca_file: ca.crt +filebeat_tls_ca_src: "{{ filebeat_pki_src_dir }}/{{ filebeat_tls_ca_file }}" +filebeat_tls_ca_dest: "{{ filebeat_pki_base_dir }}/{{ filebeat_tls_ca_file }}" # Output — mutually exclusive; set only one # Send to Logstash (default) diff --git a/roles/filebeat/tasks/copy-certs.yml b/roles/filebeat/tasks/copy-certs.yml index f0c8488..782c2c9 100644 --- a/roles/filebeat/tasks/copy-certs.yml +++ b/roles/filebeat/tasks/copy-certs.yml @@ -7,9 +7,17 @@ - filebeat_certificates - filebeat_certs block: - - name: Create Filebeat TLS directory + - name: Create Filebeat PKI base directory ansible.builtin.file: - path: "{{ filebeat_tls_dest_dir }}" + path: "{{ filebeat_pki_base_dir }}" + state: directory + owner: "{{ filebeat_user }}" + group: "{{ filebeat_group }}" + mode: "0750" + + - name: Create Filebeat PKI private directory + ansible.builtin.file: + path: "{{ filebeat_pki_private_dir }}" state: directory owner: "{{ filebeat_user }}" group: "{{ filebeat_group }}" @@ -20,8 +28,8 @@ block: - name: Copy the CA certificate file ansible.builtin.copy: - src: "{{ filebeat_tls_ca_cert_src }}" - dest: "{{ filebeat_tls_ca_cert_dest }}" + src: "{{ filebeat_tls_ca_src }}" + dest: "{{ filebeat_tls_ca_dest }}" mode: "0644" owner: "{{ filebeat_user }}" group: "{{ filebeat_group }}" diff --git a/roles/filebeat/templates/filebeat.yml.j2 b/roles/filebeat/templates/filebeat.yml.j2 index b5f988a..150d51b 100644 --- a/roles/filebeat/templates/filebeat.yml.j2 +++ b/roles/filebeat/templates/filebeat.yml.j2 @@ -23,7 +23,7 @@ output.logstash: hosts: {{ filebeat_output_logstash_hosts | to_json }} {% if filebeat_tls_enabled | bool %} ssl: - certificate_authorities: ["{{ filebeat_tls_ca_cert_dest }}"] + certificate_authorities: ["{{ filebeat_tls_ca_dest }}"] certificate: "{{ filebeat_tls_cert_dest }}" key: "{{ filebeat_tls_key_dest }}" {% endif %} @@ -33,7 +33,7 @@ output.elasticsearch: index: "{{ filebeat_output_elasticsearch_index }}" {% if filebeat_tls_enabled | bool %} ssl: - certificate_authorities: ["{{ filebeat_tls_ca_cert_dest }}"] + certificate_authorities: ["{{ filebeat_tls_ca_dest }}"] certificate: "{{ filebeat_tls_cert_dest }}" key: "{{ filebeat_tls_key_dest }}" {% endif %} diff --git a/roles/kibana/README.md b/roles/kibana/README.md index b0472d1..3198a85 100644 --- a/roles/kibana/README.md +++ b/roles/kibana/README.md @@ -23,9 +23,12 @@ Installs and configures Kibana for the Itential monitoring stack. | `kibana_server_base_path` | `""` | Base path when behind a reverse proxy (e.g. `/kibana`) | | `kibana_server_rewrite_base_path` | `false` | Whether Kibana should rewrite requests using the base path | | `kibana_tls_enabled` | `true` | Enable TLS for the Kibana server and the Elasticsearch connection | -| `kibana_tls_cert` | `/etc/kibana/certs/kibana.crt` | Path to the server certificate (PEM) | -| `kibana_tls_key` | `/etc/kibana/certs/kibana.key` | Path to the server private key (PEM) | -| `kibana_tls_ca_cert` | `/etc/kibana/certs/ca.crt` | Path to the CA certificate used to verify Elasticsearch | +| `kibana_tls_copy_certs` | `true` | Copy certificates from the control node to the target host | +| `kibana_pki_src_dir` | `""` | Directory on the control node containing the certificate files | +| `kibana_pki_base_dir` | `/etc/pki/kibana` | Base PKI directory on the target host | +| `kibana_tls_cert_file` | `{{ inventory_hostname }}.crt` | Certificate filename | +| `kibana_tls_key_file` | `{{ inventory_hostname }}.key` | Private key filename | +| `kibana_tls_ca_file` | `ca.crt` | CA certificate filename | ## TLS diff --git a/roles/kibana/defaults/main.yml b/roles/kibana/defaults/main.yml index 348997e..ee013c6 100644 --- a/roles/kibana/defaults/main.yml +++ b/roles/kibana/defaults/main.yml @@ -33,14 +33,16 @@ kibana_server_rewrite_base_path: false # TLS — secures both the Kibana server (HTTPS) and the Elasticsearch connection kibana_tls_enabled: true kibana_tls_copy_certs: true -kibana_tls_src_dir: "" -kibana_tls_dest_dir: /etc/pki/kibana -kibana_tls_cert_filename: "{{ inventory_hostname }}.crt" -kibana_tls_cert_src: "{{ kibana_tls_src_dir }}/{{ kibana_tls_cert_filename }}" -kibana_tls_cert_dest: "{{ kibana_tls_dest_dir }}/{{ kibana_tls_cert_filename }}" -kibana_tls_key_filename: "{{ inventory_hostname }}.key" -kibana_tls_key_src: "{{ kibana_tls_src_dir }}/{{ kibana_tls_key_filename }}" -kibana_tls_key_dest: "{{ kibana_tls_dest_dir }}/{{ kibana_tls_key_filename }}" -kibana_tls_ca_cert_filename: ca.crt -kibana_tls_ca_cert_src: "{{ kibana_tls_src_dir }}/{{ kibana_tls_ca_cert_filename }}" -kibana_tls_ca_cert_dest: "{{ kibana_tls_dest_dir }}/{{ kibana_tls_ca_cert_filename }}" +kibana_pki_src_dir: "" +kibana_pki_base_dir: /etc/pki/kibana +kibana_pki_private_subdir: private +kibana_pki_private_dir: "{{ kibana_pki_base_dir }}/{{ kibana_pki_private_subdir }}" +kibana_tls_cert_file: "{{ inventory_hostname }}.crt" +kibana_tls_cert_src: "{{ kibana_pki_src_dir }}/{{ kibana_tls_cert_file }}" +kibana_tls_cert_dest: "{{ kibana_pki_base_dir }}/{{ kibana_tls_cert_file }}" +kibana_tls_key_file: "{{ inventory_hostname }}.key" +kibana_tls_key_src: "{{ kibana_pki_src_dir }}/{{ kibana_tls_key_file }}" +kibana_tls_key_dest: "{{ kibana_pki_private_dir }}/{{ kibana_tls_key_file }}" +kibana_tls_ca_file: ca.crt +kibana_tls_ca_src: "{{ kibana_pki_src_dir }}/{{ kibana_tls_ca_file }}" +kibana_tls_ca_dest: "{{ kibana_pki_base_dir }}/{{ kibana_tls_ca_file }}" diff --git a/roles/kibana/tasks/copy-certs.yml b/roles/kibana/tasks/copy-certs.yml index bc6a2fb..49f9100 100644 --- a/roles/kibana/tasks/copy-certs.yml +++ b/roles/kibana/tasks/copy-certs.yml @@ -7,9 +7,17 @@ - kibana_certificates - kibana_certs block: - - name: Create Kibana TLS directory + - name: Create Kibana PKI base directory ansible.builtin.file: - path: "{{ kibana_tls_dest_dir }}" + path: "{{ kibana_pki_base_dir }}" + state: directory + owner: "{{ kibana_user }}" + group: "{{ kibana_group }}" + mode: "0750" + + - name: Create Kibana PKI private directory + ansible.builtin.file: + path: "{{ kibana_pki_private_dir }}" state: directory owner: "{{ kibana_user }}" group: "{{ kibana_group }}" @@ -20,8 +28,8 @@ block: - name: Copy the CA certificate file ansible.builtin.copy: - src: "{{ kibana_tls_ca_cert_src }}" - dest: "{{ kibana_tls_ca_cert_dest }}" + src: "{{ kibana_tls_ca_src }}" + dest: "{{ kibana_tls_ca_dest }}" mode: "0644" owner: "{{ kibana_user }}" group: "{{ kibana_group }}" diff --git a/roles/kibana/templates/kibana.yml.j2 b/roles/kibana/templates/kibana.yml.j2 index ad11faf..cfad79a 100644 --- a/roles/kibana/templates/kibana.yml.j2 +++ b/roles/kibana/templates/kibana.yml.j2 @@ -24,7 +24,7 @@ server.ssl.enabled: false # Elasticsearch elasticsearch.hosts: {{ kibana_elasticsearch_hosts | to_json }} {% if kibana_tls_enabled | bool %} -elasticsearch.ssl.certificateAuthorities: ["{{ kibana_tls_ca_cert_dest }}"] +elasticsearch.ssl.certificateAuthorities: ["{{ kibana_tls_ca_dest }}"] elasticsearch.ssl.verificationMode: certificate {% endif %} diff --git a/roles/logstash/README.md b/roles/logstash/README.md index 7a8f89b..b61d61f 100644 --- a/roles/logstash/README.md +++ b/roles/logstash/README.md @@ -20,9 +20,12 @@ Installs and configures Logstash for the Itential monitoring stack. | `logstash_log_path` | `/var/log/logstash` | Log directory | | `logstash_beats_port` | `5044` | Port for the Beats input | | `logstash_tls_enabled` | `true` | Enable TLS for the Beats input and the Elasticsearch output | -| `logstash_tls_cert` | `/etc/logstash/certs/logstash.crt` | Path to the server certificate presented to Beats (PEM) | -| `logstash_tls_key` | `/etc/logstash/certs/logstash.key` | Path to the server private key (PEM) | -| `logstash_tls_ca_cert` | `/etc/logstash/certs/ca.crt` | Path to the CA certificate used to verify Elasticsearch | +| `logstash_tls_copy_certs` | `true` | Copy certificates from the control node to the target host | +| `logstash_pki_src_dir` | `""` | Directory on the control node containing the certificate files | +| `logstash_pki_base_dir` | `/etc/pki/logstash` | Base PKI directory on the target host | +| `logstash_tls_cert_file` | `{{ inventory_hostname }}.crt` | Certificate filename | +| `logstash_tls_key_file` | `{{ inventory_hostname }}.key` | Private key filename | +| `logstash_tls_ca_file` | `ca.crt` | CA certificate filename | | `logstash_elasticsearch_hosts` | `["https://localhost:9200"]` | Elasticsearch output hosts | | `logstash_keystore_password` | `""` | Password for the Logstash keystore — **must be overridden** (use Ansible Vault) | | `logstash_elastic_password` | `""` | Password for the `logstash_writer` Elasticsearch user — **must be overridden** (use Ansible Vault) | diff --git a/roles/logstash/defaults/main.yml b/roles/logstash/defaults/main.yml index b6e01eb..3dc7d62 100644 --- a/roles/logstash/defaults/main.yml +++ b/roles/logstash/defaults/main.yml @@ -23,17 +23,19 @@ logstash_beats_port: 5044 # TLS — secures both the Beats input and the Elasticsearch output logstash_tls_enabled: true logstash_tls_copy_certs: true -logstash_tls_src_dir: "" -logstash_tls_dest_dir: /etc/pki/logstash -logstash_tls_cert_filename: "{{ inventory_hostname }}.crt" -logstash_tls_cert_src: "{{ logstash_tls_src_dir }}/{{ logstash_tls_cert_filename }}" -logstash_tls_cert_dest: "{{ logstash_tls_dest_dir }}/{{ logstash_tls_cert_filename }}" -logstash_tls_key_filename: "{{ inventory_hostname }}.key" -logstash_tls_key_src: "{{ logstash_tls_src_dir }}/{{ logstash_tls_key_filename }}" -logstash_tls_key_dest: "{{ logstash_tls_dest_dir }}/{{ logstash_tls_key_filename }}" -logstash_tls_ca_cert_filename: ca.crt -logstash_tls_ca_cert_src: "{{ logstash_tls_src_dir }}/{{ logstash_tls_ca_cert_filename }}" -logstash_tls_ca_cert_dest: "{{ logstash_tls_dest_dir }}/{{ logstash_tls_ca_cert_filename }}" +logstash_pki_src_dir: "" +logstash_pki_base_dir: /etc/pki/logstash +logstash_pki_private_subdir: private +logstash_pki_private_dir: "{{ logstash_pki_base_dir }}/{{ logstash_pki_private_subdir }}" +logstash_tls_cert_file: "{{ inventory_hostname }}.crt" +logstash_tls_cert_src: "{{ logstash_pki_src_dir }}/{{ logstash_tls_cert_file }}" +logstash_tls_cert_dest: "{{ logstash_pki_base_dir }}/{{ logstash_tls_cert_file }}" +logstash_tls_key_file: "{{ inventory_hostname }}.key" +logstash_tls_key_src: "{{ logstash_pki_src_dir }}/{{ logstash_tls_key_file }}" +logstash_tls_key_dest: "{{ logstash_pki_private_dir }}/{{ logstash_tls_key_file }}" +logstash_tls_ca_file: ca.crt +logstash_tls_ca_src: "{{ logstash_pki_src_dir }}/{{ logstash_tls_ca_file }}" +logstash_tls_ca_dest: "{{ logstash_pki_base_dir }}/{{ logstash_tls_ca_file }}" # Elasticsearch output logstash_elasticsearch_hosts: diff --git a/roles/logstash/tasks/copy-certs.yml b/roles/logstash/tasks/copy-certs.yml index b5dda22..f8e73e3 100644 --- a/roles/logstash/tasks/copy-certs.yml +++ b/roles/logstash/tasks/copy-certs.yml @@ -7,9 +7,17 @@ - logstash_certificates - logstash_certs block: - - name: Create Logstash TLS directory + - name: Create Logstash PKI base directory ansible.builtin.file: - path: "{{ logstash_tls_dest_dir }}" + path: "{{ logstash_pki_base_dir }}" + state: directory + owner: "{{ logstash_user }}" + group: "{{ logstash_group }}" + mode: "0750" + + - name: Create Logstash PKI private directory + ansible.builtin.file: + path: "{{ logstash_pki_private_dir }}" state: directory owner: "{{ logstash_user }}" group: "{{ logstash_group }}" @@ -20,8 +28,8 @@ block: - name: Copy the CA certificate file ansible.builtin.copy: - src: "{{ logstash_tls_ca_cert_src }}" - dest: "{{ logstash_tls_ca_cert_dest }}" + src: "{{ logstash_tls_ca_src }}" + dest: "{{ logstash_tls_ca_dest }}" mode: "0644" owner: "{{ logstash_user }}" group: "{{ logstash_group }}" diff --git a/roles/logstash/templates/pipeline.conf.j2 b/roles/logstash/templates/pipeline.conf.j2 index 3ce5810..cc58ffe 100644 --- a/roles/logstash/templates/pipeline.conf.j2 +++ b/roles/logstash/templates/pipeline.conf.j2 @@ -11,7 +11,7 @@ input { ssl_enabled => true ssl_certificate => "{{ logstash_tls_cert_dest }}" ssl_key => "{{ logstash_tls_key_dest }}" - ssl_certificate_authorities => ["{{ logstash_tls_ca_cert_dest }}"] + ssl_certificate_authorities => ["{{ logstash_tls_ca_dest }}"] {% else %} ssl_enabled => false {% endif %} @@ -411,7 +411,7 @@ output { user => "logstash_writer" password => "${ELASTIC_PASSWORD}" {% if logstash_tls_enabled | bool %} - ssl_certificate_authorities => ["{{ logstash_tls_ca_cert_dest }}"] + ssl_certificate_authorities => ["{{ logstash_tls_ca_dest }}"] {% endif %} } } @@ -423,7 +423,7 @@ output { user => "logstash_writer" password => "${ELASTIC_PASSWORD}" {% if logstash_tls_enabled | bool %} - ssl_certificate_authorities => ["{{ logstash_tls_ca_cert_dest }}"] + ssl_certificate_authorities => ["{{ logstash_tls_ca_dest }}"] {% endif %} } } From 4774786d943b3c7377c0c972241454982d0c3957 Mon Sep 17 00:00:00 2001 From: Amunagala-itential Date: Wed, 1 Jul 2026 12:15:52 -0400 Subject: [PATCH 2/3] Add CLAUDE.md with codebase guidance for Claude Code Documents playbook commands, tag-based selective execution, macOS prerequisites, and architecture for all three stacks (Prometheus/Grafana, ELK, Loki/Alloy). --- CLAUDE.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..11b3875 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,132 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Collection Identity + +Ansible collection `itential.monitoring` (v1.1.0, namespace: `itential`). Requires Ansible >= 2.9.10. + +## Commands + +### Run playbooks + +```bash +# Full stacks +ansible-playbook itential.monitoring.elk -i +ansible-playbook itential.monitoring.site -i # prometheus + grafana + exporters + +# Individual components +ansible-playbook itential.monitoring.loki -i +ansible-playbook itential.monitoring.alloy -i +ansible-playbook itential.monitoring.prometheus -i +ansible-playbook itential.monitoring.grafana -i +ansible-playbook itential.monitoring.prometheus_exporters -i +ansible-playbook itential.monitoring.elasticsearch -i +ansible-playbook itential.monitoring.logstash -i +ansible-playbook itential.monitoring.kibana -i +ansible-playbook itential.monitoring.filebeat -i +``` + +### Selective execution (tags) + +Each role tags its blocks as `_install` and `_configure`. The `always` block (service start + health assert) always runs. + +```bash +ansible-playbook itential.monitoring.loki -i --tags loki_configure +ansible-playbook itential.monitoring.alloy -i --tags alloy_configure +ansible-playbook itential.monitoring.elasticsearch -i --tags elasticsearch_configure +``` + +### Lint + +```bash +ansible-lint +``` + +Config in `.ansible-lint`. The following are warnings (not errors): `yaml[line-length]`, `var-naming[no-role-prefix]`, `meta-runtime[unsupported-version]`, `run-once[task]`. + +## Prerequisites + +**Prometheus stack** — `prometheus.prometheus` is not installed as a collection dependency and must be added manually: + +```bash +ansible-galaxy collection install prometheus.prometheus # requires >= 0.22.0 +``` + +**macOS** — GNU tar is required for Prometheus-related playbooks: + +```bash +brew install gnu-tar +export PATH="/opt/homebrew/opt/gnu-tar/libexec/gnubin:$PATH" +export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES # prevents fork() crash +``` + +## Architecture + +Three independent monitoring stacks — all optional, deployable together or separately. + +### Role structure pattern + +Every role uses the same three-block pattern in `tasks/main.yml`: + +1. **`_install`** — package/binary install, user/group creation, firewalld rules +2. **`_configure`** — template deployment; notifies the restart handler +3. **`always`** — flush handlers → start/enable service → assert `ActiveState == active` + +Each role has a single restart handler. Variable defaults live in `defaults/main.yml`; the only exception is `roles/grafana/vars/main.yml`, which holds the internal Prometheus port constant. + +--- + +### Prometheus / Grafana (metrics) + +`itential.monitoring.prometheus` is a thin wrapper around the community `prometheus.prometheus.prometheus` role. Its only additions: +- Generates `{{ prometheus_config_dir }}/scrape_configs/itential.yml` from inventory using `templates/scrape_configs.j2` +- The scrape template iterates `platform`, `gateway`, `mongodb`, `vault`, and all groups matching `^redis_.*` +- Per-host port overrides use `_web_listen_address` hostvars; the default port from `defaults/main.yml` is used if the hostvar is absent + +`itential.monitoring.grafana` is **RHEL/CentOS only** — tasks use `yum_repository`/`dnf` with no `ansible_os_family` guards. Bundled dashboard JSON files live in `roles/grafana/files/definitions/`; adding a file there is enough to include a new dashboard (no task change needed). The Prometheus datasource is provisioned from `groups.prometheus | first`. + +`prometheus_exporters.yml` wraps four community exporter roles and adds firewalld rules and — for MongoDB — injects `MONGODB_USER`/`MONGODB_PASSWORD` env vars into the systemd service unit. Redis exporter requires `redis_prometheus_user_enabled: true` on redis hosts. MongoDB exporter requires `mongodb_exporter_global_conn_pool: true` when replication is enabled (prevents file descriptor exhaustion). + +`site.yml` chains: exporters → prometheus → grafana. + +--- + +### ELK Stack (centralized logs) + +`elk.yml` chains: elasticsearch → logstash → kibana → filebeat. + +**TLS is enabled by default** across all four ELK components (`*_tls_enabled: true`). Certificates must be pre-placed on hosts at `/etc//certs/` before any play runs — no role distributes certs. + +**Elasticsearch** defaults to `discovery.type: single-node`. Setting both `elasticsearch_discovery_seed_hosts` and `elasticsearch_cluster_initial_master_nodes` switches to multi-node. JVM heap is deployed as a drop-in to `/etc/elasticsearch/jvm.options.d/itential-heap.options` (not overwriting the main `jvm.options`). + +**Logstash** uses a keystore to store `ELASTIC_PASSWORD` at rest. Both `logstash_keystore_password` and `logstash_elastic_password` must be set via Ansible Vault — empty defaults will cause keystore creation to fail. The pipeline (`templates/pipeline.conf.j2`) routes events by the `app` field set in Filebeat input templates; automation failures are dual-written to `itential-failures-`. The keystore task detects Logstash's lowercase key-name output in stdout for idempotency (`changed_when`). + +**Filebeat** deploys a drop-in `inputs.d/` directory (`reload.enabled: true`). `main.yml` only deploys `input_default.yml` (syslog). All app-specific inputs are deployed via separate plays in `playbooks/filebeat.yml` using `import_role: tasks_from: filebeat_` — this means app inputs are not deployed when running the role directly; use the `filebeat.yml` playbook. + +--- + +### Loki / Alloy (lightweight log aggregation) + +`loki.yml` installs Loki on the `loki` group and — in the same run — calls `tasks_from: loki_datasource` on the `grafana` group to wire the datasource. No separate Grafana playbook step is needed. + +**Loki** is installed as a binary from GitHub releases (not via package manager). The binary is idempotent: if `loki --version` already matches `loki_version`, the download is skipped. Single-node filesystem storage only — `auth_enabled: false`. Changing `loki_data_dir` post-deployment requires manually migrating existing data. + +**Alloy** replaces Promtail — the install block stops and disables `promtail` (`failed_when: false` so it does not fail if Promtail is absent). Installed via the Grafana RPM repository. The config template (`config.alloy.j2`) is validated with `alloy fmt %s` on every deploy; syntactically invalid HCL will fail the task before writing. + +`playbooks/group_vars/` pre-configures `alloy_log_paths` and `alloy_extra_groups` for all standard Itential host groups. The only variable required in inventory is `alloy_loki_url` under `all.vars`. User-defined inventory group_vars take precedence over the collection's playbook group_vars. + +`alloy_loki_url` must use the private/VPC-internal IP of the Loki host — EC2 instances cannot route to their own public IP. If this is left as the default empty string, the role deploys a broken config without failing. + +`alloy_extra_groups` adds the `alloy` user to OS groups so it can read files not world-readable (e.g. `mongod.log` is `0640 mongod:mongod`). If omitted for a host group that requires it, Alloy silently fails to tail those files. + +--- + +## Secrets (Ansible Vault required in production) + +| Variable | Role | +|---|---| +| `logstash_keystore_password` | logstash | +| `logstash_elastic_password` | logstash | +| `mongodb_exporter_admin_password` | prometheus_exporters | +| `redis_exporter_password` | prometheus_exporters | From 826fd50cf55862bc926a8c3fe0f90305659374eb Mon Sep 17 00:00:00 2001 From: Amunagala-itential Date: Wed, 1 Jul 2026 15:02:38 -0400 Subject: [PATCH 3/3] Remove Debian/Ubuntu support from ELK roles All four ELK roles (elasticsearch, filebeat, logstash, kibana) are RHEL/CentOS only, matching the rest of the itential.monitoring collection and itential.deployer. Remove apt_key/apt_repository tasks, the Debian trust store tasks in elasticsearch copy-certs, and the Debian OS family conditionals throughout. Update READMEs, docs, and role CLAUDE.md files accordingly. --- docs/itential_elk_guide.md | 3 +-- roles/elasticsearch/CLAUDE.md | 2 +- roles/elasticsearch/README.md | 2 +- roles/elasticsearch/tasks/copy-certs.yml | 22 ++-------------------- roles/elasticsearch/tasks/main.yml | 15 +-------------- roles/filebeat/CLAUDE.md | 2 +- roles/filebeat/README.md | 2 +- roles/filebeat/tasks/main.yml | 15 +-------------- roles/kibana/CLAUDE.md | 2 +- roles/kibana/README.md | 2 +- roles/kibana/tasks/main.yml | 15 +-------------- roles/logstash/CLAUDE.md | 4 ++-- roles/logstash/README.md | 4 ++-- roles/logstash/tasks/main.yml | 17 ++--------------- 14 files changed, 18 insertions(+), 89 deletions(-) diff --git a/docs/itential_elk_guide.md b/docs/itential_elk_guide.md index 8a74aba..b6b0ae1 100644 --- a/docs/itential_elk_guide.md +++ b/docs/itential_elk_guide.md @@ -321,8 +321,7 @@ Before running the ELK playbooks: - Ansible 2.12 or later installed on the control node - The `itential.monitoring` collection installed -- Target hosts running RHEL/CentOS 8+ or Debian/Ubuntu (Filebeat, Logstash, - Elasticsearch, Kibana all support both OS families) +- Target hosts running RHEL/CentOS 8+ - Internet access to `artifacts.elastic.co` on target hosts, or Elastic packages mirrored in a local Nexus repository for air-gapped environments - TLS certificates for each component generated and distributed to the target hosts diff --git a/roles/elasticsearch/CLAUDE.md b/roles/elasticsearch/CLAUDE.md index 1edc656..23fbdf5 100644 --- a/roles/elasticsearch/CLAUDE.md +++ b/roles/elasticsearch/CLAUDE.md @@ -22,7 +22,7 @@ Installs Elasticsearch from the official Elastic package repository and deploys Certificates must be pre-placed on the host before the play runs. The role does not distribute certs. ## OS support -Supports both RHEL/CentOS (`rpm_key` + `yum_repository`) and Debian/Ubuntu (`apt_key` + `apt_repository`). All package tasks are guarded with `when: ansible_os_family == "RedHat/Debian"`. +RHEL/CentOS only. Package install uses `rpm_key` + `yum_repository`. ## User and role provisioning `tasks/create-users.yml` creates the `logstash_writer` role and user, the `grafana_reader` role, and applies the `itential-logs-policy` ILM policy via the Elasticsearch REST API. It runs after the service is confirmed active and is tagged `elasticsearch_users`. Both `elasticsearch_elastic_password` and `elasticsearch_logstash_writer_password` must be set (use Ansible Vault). diff --git a/roles/elasticsearch/README.md b/roles/elasticsearch/README.md index 748a1fc..73d454b 100644 --- a/roles/elasticsearch/README.md +++ b/roles/elasticsearch/README.md @@ -4,7 +4,7 @@ Installs and configures Elasticsearch for the Itential monitoring stack. ## Requirements -- RHEL/CentOS 8+ or Debian/Ubuntu +- RHEL/CentOS 8+ - Systemd - Internet access to the Elastic package repository (or a local mirror) diff --git a/roles/elasticsearch/tasks/copy-certs.yml b/roles/elasticsearch/tasks/copy-certs.yml index 7cf3fbc..3720cfd 100644 --- a/roles/elasticsearch/tasks/copy-certs.yml +++ b/roles/elasticsearch/tasks/copy-certs.yml @@ -50,7 +50,7 @@ owner: "{{ elasticsearch_user }}" group: "{{ elasticsearch_group }}" - - name: Add Itential CA to system trust store (RedHat) + - name: Add Itential CA to system trust store ansible.builtin.copy: src: "{{ elasticsearch_tls_ca_dest }}" dest: /etc/pki/ca-trust/source/anchors/itential-ca.crt @@ -58,26 +58,8 @@ owner: root group: root mode: '0644' - when: ansible_os_family == "RedHat" - - name: Add Itential CA to system trust store (Debian) - ansible.builtin.copy: - src: "{{ elasticsearch_tls_ca_dest }}" - dest: /usr/local/share/ca-certificates/itential-ca.crt - remote_src: true - owner: root - group: root - mode: '0644' - when: ansible_os_family == "Debian" - - - name: Update system CA trust store (RedHat) + - name: Update system CA trust store ansible.builtin.command: cmd: update-ca-trust extract changed_when: false - when: ansible_os_family == "RedHat" - - - name: Update system CA trust store (Debian) - ansible.builtin.command: - cmd: update-ca-certificates - changed_when: false - when: ansible_os_family == "Debian" diff --git a/roles/elasticsearch/tasks/main.yml b/roles/elasticsearch/tasks/main.yml index 7bf815b..2d02360 100644 --- a/roles/elasticsearch/tasks/main.yml +++ b/roles/elasticsearch/tasks/main.yml @@ -20,22 +20,9 @@ enabled: true when: ansible_os_family == "RedHat" - - name: Add Elasticsearch APT GPG key - ansible.builtin.apt_key: - url: https://artifacts.elastic.co/GPG-KEY-elasticsearch - state: present - when: ansible_os_family == "Debian" - - - name: Add Elasticsearch APT repository - ansible.builtin.apt_repository: - repo: "deb https://artifacts.elastic.co/packages/{{ elasticsearch_version.split('.')[0] }}.x/apt stable main" - state: present - filename: elasticsearch - when: ansible_os_family == "Debian" - - name: Install Elasticsearch package ansible.builtin.package: - name: "elasticsearch{% if ansible_os_family == 'RedHat' %}-{{ elasticsearch_version }}{% endif %}" + name: "elasticsearch-{{ elasticsearch_version }}" state: present - name: Configure Elasticsearch diff --git a/roles/filebeat/CLAUDE.md b/roles/filebeat/CLAUDE.md index b0e6013..d4b6618 100644 --- a/roles/filebeat/CLAUDE.md +++ b/roles/filebeat/CLAUDE.md @@ -37,7 +37,7 @@ Only one output should be enabled at a time. `filebeat_output_logstash_enabled: `filebeat_tls_enabled` adds an `ssl` block to whichever output is active, providing the CA cert, client cert, and client key for mutual TLS. Certs must be pre-placed before the play runs. ## OS support -Supports both RHEL/CentOS (`rpm_key` + `yum_repository`) and Debian/Ubuntu (`apt_key` + `apt_repository`). All package tasks are guarded with `when: ansible_os_family == "RedHat/Debian"`. +RHEL/CentOS only. Package install uses `rpm_key` + `yum_repository`. ## Variables that should be overridden - `filebeat_environment` — defaults to `"unset"`; should be set to `production`, `staging`, etc. diff --git a/roles/filebeat/README.md b/roles/filebeat/README.md index 86c10ec..9c1cf4f 100644 --- a/roles/filebeat/README.md +++ b/roles/filebeat/README.md @@ -4,7 +4,7 @@ Installs and configures Filebeat for the Itential monitoring stack. ## Requirements -- RHEL/CentOS 8+ or Debian/Ubuntu +- RHEL/CentOS 8+ - Systemd - Internet access to the Elastic package repository (or a local mirror) - A running Logstash or Elasticsearch instance to receive events diff --git a/roles/filebeat/tasks/main.yml b/roles/filebeat/tasks/main.yml index e152a5d..4bf9393 100644 --- a/roles/filebeat/tasks/main.yml +++ b/roles/filebeat/tasks/main.yml @@ -20,22 +20,9 @@ enabled: true when: ansible_os_family == "RedHat" - - name: Add Elastic APT GPG key - ansible.builtin.apt_key: - url: https://artifacts.elastic.co/GPG-KEY-elasticsearch - state: present - when: ansible_os_family == "Debian" - - - name: Add Filebeat APT repository - ansible.builtin.apt_repository: - repo: "deb https://artifacts.elastic.co/packages/{{ filebeat_version.split('.')[0] }}.x/apt stable main" - state: present - filename: filebeat - when: ansible_os_family == "Debian" - - name: Install Filebeat package ansible.builtin.package: - name: "filebeat{% if ansible_os_family == 'RedHat' %}-{{ filebeat_version }}{% endif %}" + name: "filebeat-{{ filebeat_version }}" state: present - name: Configure Filebeat diff --git a/roles/kibana/CLAUDE.md b/roles/kibana/CLAUDE.md index a37ec64..2620fd4 100644 --- a/roles/kibana/CLAUDE.md +++ b/roles/kibana/CLAUDE.md @@ -27,7 +27,7 @@ Certificates must be pre-placed on the host before the play runs. The role does Set `kibana_server_base_path` to a non-empty string to enable the `server.basePath` / `server.rewriteBasePath` block. When empty (default), neither setting is written to `kibana.yml`. ## OS support -Supports both RHEL/CentOS (`rpm_key` + `yum_repository`) and Debian/Ubuntu (`apt_key` + `apt_repository`). All package tasks are guarded with `when: ansible_os_family == "RedHat/Debian"`. +RHEL/CentOS only. Package install uses `rpm_key` + `yum_repository`. ## Variables that must be overridden - `kibana_elasticsearch_hosts` — should point to the actual Elasticsearch host(s), not localhost diff --git a/roles/kibana/README.md b/roles/kibana/README.md index 3198a85..eb52865 100644 --- a/roles/kibana/README.md +++ b/roles/kibana/README.md @@ -4,7 +4,7 @@ Installs and configures Kibana for the Itential monitoring stack. ## Requirements -- RHEL/CentOS 8+ or Debian/Ubuntu +- RHEL/CentOS 8+ - Systemd - Internet access to the Elastic package repository (or a local mirror) - A running Elasticsearch instance diff --git a/roles/kibana/tasks/main.yml b/roles/kibana/tasks/main.yml index 2f77990..a6d01b4 100644 --- a/roles/kibana/tasks/main.yml +++ b/roles/kibana/tasks/main.yml @@ -20,22 +20,9 @@ enabled: true when: ansible_os_family == "RedHat" - - name: Add Elastic APT GPG key - ansible.builtin.apt_key: - url: https://artifacts.elastic.co/GPG-KEY-elasticsearch - state: present - when: ansible_os_family == "Debian" - - - name: Add Kibana APT repository - ansible.builtin.apt_repository: - repo: "deb https://artifacts.elastic.co/packages/{{ kibana_version.split('.')[0] }}.x/apt stable main" - state: present - filename: kibana - when: ansible_os_family == "Debian" - - name: Install Kibana package ansible.builtin.package: - name: "kibana{% if ansible_os_family == 'RedHat' %}-{{ kibana_version }}{% endif %}" + name: "kibana-{{ kibana_version }}" state: present - name: Configure Kibana diff --git a/roles/logstash/CLAUDE.md b/roles/logstash/CLAUDE.md index f8557ee..2c0e065 100644 --- a/roles/logstash/CLAUDE.md +++ b/roles/logstash/CLAUDE.md @@ -19,7 +19,7 @@ The configure block manages the keystore in this order: 2. Create keystore (skipped if file exists) — protected by `logstash_keystore_password` 3. Remove existing `ELASTIC_PASSWORD` key (always runs, `failed_when: false`) 4. Add `ELASTIC_PASSWORD` with the value of `logstash_elastic_password` -5. Write `LOGSTASH_KEYSTORE_PASS` to the service environment file (`/etc/sysconfig/logstash` on RHEL, `/etc/default/logstash` on Debian) so the service can decrypt the keystore at startup +5. Write `LOGSTASH_KEYSTORE_PASS` to `/etc/sysconfig/logstash` so the service can decrypt the keystore at startup The pipeline template references `${ELASTIC_PASSWORD}`, which Logstash resolves from the keystore at runtime. @@ -36,7 +36,7 @@ Both of these have empty string defaults and will cause the keystore create to f The pipeline at `/etc/logstash/conf.d/itential.conf` routes by the `app` field set by Filebeat inputs. Automation failures are dual-written to `itential-failures-`. All events go to `itential-logs--`. ## OS support -Supports both RHEL/CentOS (`rpm_key` + `yum_repository`) and Debian/Ubuntu (`apt_key` + `apt_repository`). All package tasks are guarded with `when: ansible_os_family == "RedHat/Debian"`. The service environment file path is selected per OS family in the keystore task. +RHEL/CentOS only. Package install uses `rpm_key` + `yum_repository`. Keystore password is written to `/etc/sysconfig/logstash`. ## Known gaps - The `logstash_writer` Elasticsearch user is not created by any role or playbook. Authentication worked in testing without it, which suggests the Elasticsearch security configuration may allow it — this should be investigated. diff --git a/roles/logstash/README.md b/roles/logstash/README.md index b61d61f..2d564e8 100644 --- a/roles/logstash/README.md +++ b/roles/logstash/README.md @@ -4,7 +4,7 @@ Installs and configures Logstash for the Itential monitoring stack. ## Requirements -- RHEL/CentOS 8+ or Debian/Ubuntu +- RHEL/CentOS 8+ - Systemd - Internet access to the Elastic package repository (or a local mirror) - Java is bundled with the Logstash package — no separate JDK required @@ -48,7 +48,7 @@ The role manages the Logstash keystore automatically: 1. Creates `/etc/logstash/logstash.keystore` if it does not exist, protected by `logstash_keystore_password`. 2. Stores `logstash_elastic_password` in the keystore under the key `ELASTIC_PASSWORD`. -3. Writes `LOGSTASH_KEYSTORE_PASS` to the service environment file (`/etc/sysconfig/logstash` on RHEL, `/etc/default/logstash` on Debian) so Logstash can decrypt the keystore at startup. +3. Writes `LOGSTASH_KEYSTORE_PASS` to `/etc/sysconfig/logstash` so Logstash can decrypt the keystore at startup. The pipeline template references `${ELASTIC_PASSWORD}` which Logstash resolves from the keystore at runtime. diff --git a/roles/logstash/tasks/main.yml b/roles/logstash/tasks/main.yml index 14ffc2d..56f2e6a 100644 --- a/roles/logstash/tasks/main.yml +++ b/roles/logstash/tasks/main.yml @@ -20,22 +20,9 @@ enabled: true when: ansible_os_family == "RedHat" - - name: Add Elastic APT GPG key - ansible.builtin.apt_key: - url: https://artifacts.elastic.co/GPG-KEY-elasticsearch - state: present - when: ansible_os_family == "Debian" - - - name: Add Logstash APT repository - ansible.builtin.apt_repository: - repo: "deb https://artifacts.elastic.co/packages/{{ logstash_version.split('.')[0] }}.x/apt stable main" - state: present - filename: logstash - when: ansible_os_family == "Debian" - - name: Install Logstash package ansible.builtin.package: - name: "logstash{% if ansible_os_family == 'RedHat' %}-{{ logstash_version }}{% endif %}" + name: "logstash-{{ logstash_version }}" state: present - name: Configure Logstash @@ -123,7 +110,7 @@ - name: Set LOGSTASH_KEYSTORE_PASS in service environment ansible.builtin.lineinfile: - path: "{{ '/etc/sysconfig/logstash' if ansible_os_family == 'RedHat' else '/etc/default/logstash' }}" + path: /etc/sysconfig/logstash regexp: "^LOGSTASH_KEYSTORE_PASS=" line: "LOGSTASH_KEYSTORE_PASS={{ logstash_keystore_password }}" create: true