Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion molecule/mtls-debian12/molecule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,11 @@ provisioner:
jolokia_user: user1
jolokia_password: pass

# Enable Jolokia access control for security
jolokia_access_control_enabled: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and above test both are validating same case but we dont have any case for access control enabled false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's by default false only, so we need to check for jolokia_access_control_custom_file_enabled true and false, which i have added tc in pr - https://github.com/confluentinc/cp-ansible/pull/2197/files

kafka_controller_jolokia_access_control_enabled: true
kafka_controller_jolokia_access_control_custom_file_enabled: true
kafka_controller_jolokia_access_control_file_src_path: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/roles/kafka_controller/templates/jolokia_migration_temp.xml.j2"
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path references 'jolokia_migration_temp.xml.j2' which is not included in the PR changes and likely doesn't exist, causing test failures.

Suggested change
kafka_controller_jolokia_access_control_file_src_path: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/roles/kafka_controller/templates/jolokia_migration_temp.xml.j2"
kafka_controller_jolokia_access_control_file_src_path: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/roles/kafka_controller/templates/jolokia_access_control.xml.j2"

Copilot uses AI. Check for mistakes.

confluent_cli_download_enabled: true
custom_java_path: /opt/jdk17 # Use custom java 17
custom_java_path: /opt/jdk17 # Use custom java 17
11 changes: 11 additions & 0 deletions molecule/zookeeper-digest-rhel/molecule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,14 @@ provisioner:
sasl_protocol: plain

zookeeper_chroot: "/kafka"
# Add these to the group_vars/all section:
jolokia_enabled: true
jolokia_auth_mode: basic
jolokia_user: user1
jolokia_password: pass

# Configure Jolokia access control
kafka_controller_jolokia_enabled: true
kafka_controller_jolokia_access_control_enabled: true
kafka_controller_jolokia_access_control_custom_file_enabled: true
kafka_controller_jolokia_access_control_file_src_path: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/roles/kafka_controller/templates/jolokia_migration_temp.xml.j2"
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path references 'jolokia_migration_temp.xml.j2' which is not included in the PR changes and likely doesn't exist, causing test failures.

Suggested change
kafka_controller_jolokia_access_control_file_src_path: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/roles/kafka_controller/templates/jolokia_migration_temp.xml.j2"
# kafka_controller_jolokia_access_control_file_src_path: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/roles/kafka_controller/templates/jolokia_migration_temp.xml.j2"

Copilot uses AI. Check for mistakes.
139 changes: 139 additions & 0 deletions playbooks/ZKtoKraftMigration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,114 @@
- import_playbook: kafka_controller.yml
tags: migrate_to_dual_write

#todo:should check if we can hit the jolokia endppint or not, if no the fail and print the error message, continue on yes
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo in 'endppint' which should be 'endpoint'.

Suggested change
#todo:should check if we can hit the jolokia endppint or not, if no the fail and print the error message, continue on yes
#todo:should check if we can hit the jolokia endpoint or not, if no then fail and print the error message, continue on yes

Copilot uses AI. Check for mistakes.

- name: Validate Jolokia Endpoint Access
hosts: kafka_controller
gather_facts: false
tasks:
- import_role:
name: variables

- name: Test Jolokia Endpoint Access (No Auth)
uri:
url: "{{ 'https' if kafka_controller_jolokia_ssl_enabled|bool else 'http' }}://localhost:{{kafka_controller_jolokia_port}}/jolokia/list"
validate_certs: false
return_content: true
status_code: 200
register: jolokia_no_auth_result
failed_when: false

- name: Test Jolokia Endpoint Access (Basic Auth)
uri:
url: "{{ 'https' if kafka_controller_jolokia_ssl_enabled|bool else 'http' }}://localhost:{{kafka_controller_jolokia_port}}/jolokia/list"
validate_certs: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should combine this and no auth into a single task

return_content: true
force_basic_auth: true
url_username: "{{ jolokia_user }}"
url_password: "{{ jolokia_password }}"
status_code: 200
register: jolokia_basic_auth_result
failed_when: false

- name: Check if Either Auth Method Succeeded
set_fact:
jolokia_accessible: >-
{{
(jolokia_no_auth_result.status is defined and jolokia_no_auth_result.status == 200) or
(jolokia_basic_auth_result.status is defined and jolokia_basic_auth_result.status == 200)
}}
- name: Fail if Both Auth Methods Failed
fail:
msg: |
ERROR: Unable to access Jolokia endpoint with either authentication method!
Endpoint: {{ 'https' if kafka_controller_jolokia_ssl_enabled|bool else 'http' }}://localhost:{{kafka_controller_jolokia_port}}/jolokia/list
No Auth Result: {{ jolokia_no_auth_result.status | default('Connection Failed') }}
Basic Auth Result: {{ jolokia_basic_auth_result.status | default('Connection Failed') }}
Possible causes:
1. Jolokia access control XML file is blocking access
2. Jolokia service is not running properly
3. Both authentication methods are misconfigured
4. SSL/TLS configuration issues
Please check your jolokia_access_control_custom_file_enabled and
jolokia_access_control_file_src_path configuration.
when: not jolokia_accessible|bool

- name: Success - Jolokia Endpoint is Accessible
debug:
msg: |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please attach the result of curl request to jolokia endpoint when the access control.xml file doesnt have the mbean for allowing this request

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"json": {"error": "java.lang.Exception : Reading attribute Verbose is forbidden for MBean kafka.controller:name=ZkMigrationState,type=KafkaController", "redirected": false, "status": 403, "transfer_encoding": "chunked", "url": "http://127.0.0.1:7777/jolokia/read/kafka.controller:name=ZkMigrationState,type=KafkaController"}

✅ SUCCESS: Jolokia endpoint is accessible and ready for ZK to KRaft migration
Endpoint: {{ 'https' if kafka_controller_jolokia_ssl_enabled|bool else 'http' }}://localhost:{{kafka_controller_jolokia_port}}/jolokia/list
No Auth: {{ jolokia_no_auth_result.status | default('Failed') }}
Basic Auth: {{ jolokia_basic_auth_result.status | default('Failed') }}
At least one method succeeded!
when: jolokia_accessible|bool

- import_playbook: kafka_broker.yml
vars:
deployment_strategy: 'serial'
tags: migrate_to_dual_write

- name: Deploy Temporary Migration Jolokia Policy
hosts: kafka_controller
gather_facts: false
tasks:
- import_role:
name: variables

- name: Backup Current Jolokia Access Control XML (if exists)
copy:
src: "{{ kafka_controller_jolokia_access_control_dest_path }}"
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable 'kafka_controller_jolokia_access_control_dest_path' is used but not defined in the visible code. This will cause the task to fail.

Copilot uses AI. Check for mistakes.
dest: "{{ kafka_controller_jolokia_access_control_dest_path }}.backup"
remote_src: true
failed_when: false
when: kafka_controller_jolokia_access_control_enabled|bool

- name: Deploy Temporary Migration-Specific Jolokia Access Control XML
template:
src: jolokia_migration_temp.xml.j2
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template 'jolokia_migration_temp.xml.j2' is referenced but this file is not included in the PR changes, which will cause the template task to fail.

Copilot uses AI. Check for mistakes.
dest: "{{ kafka_controller_jolokia_access_control_dest_path }}"
mode: '640'
owner: "{{ kafka_controller_user }}"
group: "{{ kafka_controller_group }}"
when: kafka_controller_jolokia_access_control_enabled|bool
notify: restart Kafka Controller

- meta: flush_handlers

- name: Wait for Kafka Controller to restart with new policy
wait_for:
port: "{{ kafka_controller_jolokia_port }}"
host: "{{ inventory_hostname }}"
delay: 10
timeout: 300
when: kafka_controller_jolokia_access_control_enabled|bool

- name: Wait for migration to complete
hosts: kafka_controller
tags: migrate_to_dual_write
Expand Down Expand Up @@ -162,6 +265,42 @@
register: jolokia_output
when: jolokia_auth_mode == "basic"

- name: Restore Original Jolokia Policy
hosts: kafka_controller
gather_facts: false
tasks:
- import_role:
name: variables

- name: Restore Original Jolokia Access Control XML
copy:
src: "{{ kafka_controller_jolokia_access_control_dest_path }}.backup"
dest: "{{ kafka_controller_jolokia_access_control_dest_path }}"
remote_src: true
mode: '640'
owner: "{{ kafka_controller_user }}"
group: "{{ kafka_controller_group }}"
when:
- kafka_controller_jolokia_access_control_enabled|bool
failed_when: false
notify: restart Kafka Controller

- name: Remove Backup File
file:
path: "{{ kafka_controller_jolokia_access_control_dest_path }}.backup"
state: absent
when: kafka_controller_jolokia_access_control_enabled|bool

- meta: flush_handlers

- name: Wait for Kafka Controller to restart with restored policy
wait_for:
port: "{{ kafka_controller_jolokia_port }}"
host: "{{ inventory_hostname }}"
delay: 10
timeout: 300
when: kafka_controller_jolokia_access_control_enabled|bool

- name: Finish Migration
hosts: kafka_controller
tags: migrate_to_kraft
Expand Down
77 changes: 77 additions & 0 deletions roles/common/tasks/config_validations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,80 @@
- not (control_center_next_gen_dependency_alertmanager_ssl_enabled | bool and control_center_next_gen_dependency_alertmanager_mtls_enabled | bool and control_center_next_gen_dependency_alertmanager_basic_auth_enabled | bool)
fail_msg: "Alertmanager SSL, mTLS, and Basic Auth cannot all be enabled simultaneously."
tags: validate

# Jolokia Access Control Validations
- name: Validate Jolokia Access Control Configuration - Kafka Controller
fail:
msg: |
Security requirement: When Jolokia is enabled and access control is enabled,
you must set kafka_controller_jolokia_access_control_custom_file_enabled to either:
- true (provide your own secure XML file via kafka_controller_jolokia_access_control_file_src_path)
- false (use our secure default XML)
This prevents lateral movement attacks via exposed Jolokia endpoints.
when:
- "'kafka_controller' in group_names"
- kafka_controller_jolokia_enabled|bool
- kafka_controller_jolokia_access_control_enabled|bool
- kafka_controller_jolokia_access_control_custom_file_enabled is not defined or kafka_controller_jolokia_access_control_custom_file_enabled is none
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have this var defined in defaults so it should never be not defined. So this can be removed.
Also we are we setting default null as default so is this kafka_controller_jolokia_access_control_custom_file_enabled is none true for kafka_controller_jolokia_access_control_custom_file_enabled: null ?

tags:
- validate
- validate_jolokia

- name: Validate Custom Jolokia Access Control File Path - Kafka Controller
fail:
msg: |
When kafka_controller_jolokia_access_control_custom_file_enabled is true,
you must provide kafka_controller_jolokia_access_control_file_src_path with a valid file path.
when:
- "'kafka_controller' in group_names"
- kafka_controller_jolokia_enabled|bool
- kafka_controller_jolokia_access_control_enabled|bool
- kafka_controller_jolokia_access_control_custom_file_enabled|bool
- kafka_controller_jolokia_access_control_file_src_path == ""
tags:
- validate
- validate_jolokia

- name: Validate Custom Jolokia Access Control File Exists - Kafka Controller
stat:
path: "{{ kafka_controller_jolokia_access_control_file_src_path }}"
delegate_to: localhost
register: kafka_controller_custom_jolokia_file_check
when:
- "'kafka_controller' in group_names"
- kafka_controller_jolokia_enabled|bool
- kafka_controller_jolokia_access_control_enabled|bool
- kafka_controller_jolokia_access_control_custom_file_enabled|bool
- kafka_controller_jolokia_access_control_file_src_path != ""
tags:
- validate
- validate_jolokia

- name: Fail if Custom Jolokia Access Control File Does Not Exist - Kafka Controller
fail:
msg: |
Custom Jolokia access control file not found at: {{ kafka_controller_jolokia_access_control_file_src_path }}
Please ensure the file exists on the Ansible controller.
when:
- "'kafka_controller' in group_names"
- kafka_controller_jolokia_enabled|bool
- kafka_controller_jolokia_access_control_enabled|bool
- kafka_controller_jolokia_access_control_custom_file_enabled|bool
- kafka_controller_jolokia_access_control_file_src_path != ""
- not kafka_controller_custom_jolokia_file_check.stat.exists
tags:
- validate
- validate_jolokia

- name: Set Default Jolokia Access Control File Path - Kafka Controller
set_fact:
kafka_controller_jolokia_access_control_file_src_path: "roles/kafka_controller/templates/jolokia_access_control_default.xml"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a validation so we can move it to roles/variables/tasks

when:
- "'kafka_controller' in group_names"
- kafka_controller_jolokia_enabled|bool
- kafka_controller_jolokia_access_control_enabled|bool
- not kafka_controller_jolokia_access_control_custom_file_enabled|bool
tags:
- validate
- validate_jolokia
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Default Jolokia Access Control Policy
This policy allows access only to the endpoint required for "Wait for Metadata Migration" task
and should be replaced with a more restrictive policy after migration completes
-->
<restrict>
<!-- Allow access to migration state endpoint - Required for "Wait for Metadata Migration" task -->
<mbean>
<n>kafka.controller:type=KafkaController,name=ZkMigrationState</n>
<operation>read</operation>
</mbean>

<!-- Allow jolokia read operations -->
<command>read</command>

<!-- Default deny for everything else -->
<default>deny</default>
</restrict>
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ authMode=basic
user={{kafka_controller_jolokia_user}}
password={{kafka_controller_jolokia_password}}
{% endif %}
{% if kafka_controller_jolokia_access_control_enabled|bool %}
policy={{ lookup('file', kafka_controller_jolokia_access_control_file_src_path) | replace('\n', '') | replace(' ', '') }}
Copy link

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The policy configuration line removes all newlines and spaces from the XML file, which will likely break XML structure and make it invalid. XML requires proper formatting with spaces and newlines for valid parsing.

Suggested change
policy={{ lookup('file', kafka_controller_jolokia_access_control_file_src_path) | replace('\n', '') | replace(' ', '') }}
policy={{ lookup('file', kafka_controller_jolokia_access_control_file_src_path) }}

Copilot uses AI. Check for mistakes.
{% endif %}
20 changes: 20 additions & 0 deletions roles/variables/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ jolokia_user: admin
### Password for Jolokia Agent when using Basic Auth
jolokia_password: password

### Boolean to enable Jolokia Access Control for security. Defaults to same as jolokia_enabled
jolokia_access_control_enabled: "{{ jolokia_enabled }}"

### Boolean to use custom Jolokia access control file. Must be set to true or false when Jolokia access control is enabled. Set to null to enforce explicit choice.
jolokia_access_control_custom_file_enabled: null

### Full path on Ansible Controller to custom Jolokia access control XML file. Required when jolokia_access_control_custom_file_enabled is true
jolokia_access_control_file_src_path: ""

### To copy from Ansible control host or download
jmxexporter_url_remote: true

Expand Down Expand Up @@ -549,6 +558,17 @@ kafka_controller_jolokia_user: "{{jolokia_user}}"
### Password for Kafka's Jolokia Agent when using Basic Auth
kafka_controller_jolokia_password: "{{jolokia_password}}"

### Boolean to enable Jolokia Access Control for Kafka Controller. Inherits from global setting
kafka_controller_jolokia_access_control_enabled: "{{ jolokia_access_control_enabled }}"

### Boolean to use custom Jolokia access control file for Kafka Controller. Inherits from global setting
kafka_controller_jolokia_access_control_custom_file_enabled: "{{ jolokia_access_control_custom_file_enabled }}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have this variable for kraft specifically and not other components ?
we can take a call but I think it should either be in all components or not for any component specific.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having only one var and not for each component will make it easier to handle. So I think we can remove this kafka controller prefix and use it in all components


### Full path on Ansible Controller to custom Jolokia access control XML file for Kafka Controller. Inherits from global setting
kafka_controller_jolokia_access_control_file_src_path: "{{ jolokia_access_control_file_src_path }}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the task to move this file to cp node from ansible controller node ?




# TODO move these to vars, should not be customizable, do they even belong w the shared vars
kafka_controller_jolokia_java_arg_ssl_addon: ",keystore={{kafka_controller_keystore_path}},keystorePassword={{kafka_controller_keystore_storepass}},protocol=https"
kafka_controller_jolokia_urp_url: "{{ 'https' if kafka_controller_jolokia_ssl_enabled|bool else 'http' }}://{{ hostvars[inventory_hostname]|confluent.platform.resolve_and_format_hostname}}:{{kafka_controller_jolokia_port}}/jolokia/read/kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions"
Expand Down