From e136cdf26a07c613d65475606fdd5cc4f5bcb915 Mon Sep 17 00:00:00 2001 From: Sam Crauwels Date: Sun, 28 Jun 2026 12:01:22 +0200 Subject: [PATCH] feat(kibana): allow supplying xpack encryption keys The role generates random xpack.security.encryptionKey and xpack.encryptedSavedObjects.encryptionKey on first run. When migrating Kibana to a new host or running multiple instances these must match the existing keys, otherwise encrypted saved objects (alerting rules, connectors, Fleet tokens) and session state cannot be decrypted by the new instance. Add kibana_security_encryptionkey and kibana_encryptedsavedobjects_encryptionkey to supply them; empty keeps the generate-random default. Covered by the kibana_custom molecule scenario. --- molecule/kibana_custom/converge.yml | 2 ++ molecule/kibana_custom/verify.yml | 18 ++++++++++++++++++ roles/kibana/defaults/main.yml | 16 ++++++++++++++++ roles/kibana/tasks/kibana-security.yml | 20 ++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/molecule/kibana_custom/converge.yml b/molecule/kibana_custom/converge.yml index 1f8e1152..76badd9a 100644 --- a/molecule/kibana_custom/converge.yml +++ b/molecule/kibana_custom/converge.yml @@ -12,6 +12,8 @@ # Kibana custom settings kibana_config_backup: true kibana_sniff_on_start: true + kibana_security_encryptionkey: "moleculeProvidedSecurityKey0123456789abcd" + kibana_encryptedsavedobjects_encryptionkey: "moleculeProvidedSavedObjectsKey0123456789" kibana_extra_config: |- ops.interval: 10000 logging.root.level: warn diff --git a/molecule/kibana_custom/verify.yml b/molecule/kibana_custom/verify.yml index de04ef16..cd5893d2 100644 --- a/molecule/kibana_custom/verify.yml +++ b/molecule/kibana_custom/verify.yml @@ -72,6 +72,24 @@ - "'logging.root.level: warn' in (kibana_yml.content | b64decode)" fail_msg: "Extra config not found in kibana.yml" + - name: Read provided encryption keys from the keystore + ansible.builtin.command: > + /usr/share/kibana/bin/kibana-keystore show {{ item }} + environment: + KBN_PATH_CONF: /etc/kibana + loop: + - xpack.security.encryptionKey + - xpack.encryptedSavedObjects.encryptionKey + register: provided_keys + changed_when: false + + - name: Verify provided encryption keys are honoured in the keystore + ansible.builtin.assert: + that: + - "provided_keys.results[0].stdout_lines[-1] == 'moleculeProvidedSecurityKey0123456789abcd'" + - "provided_keys.results[1].stdout_lines[-1] == 'moleculeProvidedSavedObjectsKey0123456789'" + fail_msg: "Provided encryption keys not stored in the Kibana keystore" + - name: Verify sniff on start is enabled (pre-9.x only) ansible.builtin.assert: that: diff --git a/roles/kibana/defaults/main.yml b/roles/kibana/defaults/main.yml index 12ad5d86..f8d6478d 100644 --- a/roles/kibana/defaults/main.yml +++ b/roles/kibana/defaults/main.yml @@ -37,6 +37,22 @@ kibana_tls: false kibana_tls_key_passphrase: PleaseChangeMe # @var kibana_keystore_password:description: Password to encrypt the Kibana keystore file. Empty means no encryption (same as ES default) kibana_keystore_password: "" + +# @var kibana_security_encryptionkey:description: > +# Value for xpack.security.encryptionKey (stored in the Kibana keystore). +# Set this to reuse an existing key when migrating Kibana to a new host or +# running multiple instances, so encrypted session state stays valid. Empty +# generates a random key on first run. +# @end +kibana_security_encryptionkey: "" + +# @var kibana_encryptedsavedobjects_encryptionkey:description: > +# Value for xpack.encryptedSavedObjects.encryptionKey (stored in the keystore). +# Set this to reuse an existing key when migrating or scaling out, otherwise +# encrypted saved objects (alerting rules, connectors, Fleet tokens) cannot be +# decrypted by the new instance. Empty generates a random key on first run. +# @end +kibana_encryptedsavedobjects_encryptionkey: "" # @var kibana_cert_expiration_buffer:description: Days before certificate expiry to trigger renewal kibana_cert_expiration_buffer: 30 # @var kibana_cert_validity_period:description: Validity period in days for generated TLS certificates diff --git a/roles/kibana/tasks/kibana-security.yml b/roles/kibana/tasks/kibana-security.yml index 1cb18f31..f2a327ee 100644 --- a/roles/kibana/tasks/kibana-security.yml +++ b/roles/kibana/tasks/kibana-security.yml @@ -292,6 +292,16 @@ loop_control: label: "{{ item.item }}" + - name: kibana-security | Use provided encryption key + ansible.builtin.copy: + dest: "{{ elasticstack_ca_dir }}/encryption_key" + content: "{{ kibana_security_encryptionkey }}" + owner: root + group: elasticsearch + mode: "0600" + when: kibana_security_encryptionkey | default('') | length > 0 + no_log: true + - name: kibana-security | Generate encryption key ansible.builtin.shell: > set -o pipefail; @@ -309,6 +319,16 @@ register: kibana_encryption_key check_mode: false + - name: kibana-security | Use provided saved objects encryption key + ansible.builtin.copy: + dest: "{{ elasticstack_ca_dir }}/savedobjects_encryption_key" + content: "{{ kibana_encryptedsavedobjects_encryptionkey }}" + owner: root + group: elasticsearch + mode: "0600" + when: kibana_encryptedsavedobjects_encryptionkey | default('') | length > 0 + no_log: true + - name: kibana-security | Generate saved objects encryption key ansible.builtin.shell: > set -o pipefail;