From 01d60afe7f0514bb89db31ff1b2d932b9371eb43 Mon Sep 17 00:00:00 2001 From: w4hf Date: Thu, 17 Oct 2024 23:45:16 +0200 Subject: [PATCH 01/14] add default_environment to org Default for controller_rules testing --- tests/configs/organizations.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/configs/organizations.yml b/tests/configs/organizations.yml index c0380a93..75f8c088 100644 --- a/tests/configs/organizations.yml +++ b/tests/configs/organizations.yml @@ -2,6 +2,7 @@ controller_organizations: - name: Satellite - name: Default + default_environment: Automation Hub Default Execution Environment - name: Test-dispatch-dependencies galaxy_credentials: - galaxy-server From 39b71c1aa7d568e8896e4c7b05518026a0e7e69a Mon Sep 17 00:00:00 2001 From: w4hf Date: Thu, 17 Oct 2024 23:45:52 +0200 Subject: [PATCH 02/14] add users of different types --- tests/configs/user_accounts.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/configs/user_accounts.yml b/tests/configs/user_accounts.yml index f73dad90..7ee16217 100644 --- a/tests/configs/user_accounts.yml +++ b/tests/configs/user_accounts.yml @@ -3,4 +3,17 @@ controller_user_accounts: - user: controller_user is_superuser: false password: controller_password + - user: second_controller_user + is_superuser: false + organization: Default + password: controller_password + - user: controller_auditor + is_system_auditor: true + password: controller_password + - user: controller_admin + is_superuser: true + password: controller_password + - user: second_controller_admin + is_superuser: true + password: controller_password ... From b45b8a70d90fc84632de890baebe308956bd619e Mon Sep 17 00:00:00 2001 From: w4hf Date: Thu, 17 Oct 2024 23:50:34 +0200 Subject: [PATCH 03/14] create first version of controller_rules_validation role --- .../defaults/main.yml | 153 ++++++++++++++++++ .../tasks/check_credentials_encryption.yml | 54 +++++++ .../tasks/check_fields_regex.yml | 35 ++++ .../tasks/check_hosts.yml | 36 +++++ .../tasks/check_inventories.yml | 27 ++++ .../tasks/check_mandatory_fields.yml | 74 +++++++++ .../tasks/check_objects_count.yml | 89 ++++++++++ .../tasks/check_organizations.yml | 88 ++++++++++ .../tasks/check_roles.yml | 130 +++++++++++++++ .../tasks/check_users.yml | 77 +++++++++ .../tasks/main.yml | 55 +++++++ .../tasks/rule_check.yml | 103 ++++++++++++ 12 files changed, 921 insertions(+) create mode 100644 roles/controller_rules_validation/defaults/main.yml create mode 100644 roles/controller_rules_validation/tasks/check_credentials_encryption.yml create mode 100644 roles/controller_rules_validation/tasks/check_fields_regex.yml create mode 100644 roles/controller_rules_validation/tasks/check_hosts.yml create mode 100644 roles/controller_rules_validation/tasks/check_inventories.yml create mode 100644 roles/controller_rules_validation/tasks/check_mandatory_fields.yml create mode 100644 roles/controller_rules_validation/tasks/check_objects_count.yml create mode 100644 roles/controller_rules_validation/tasks/check_organizations.yml create mode 100644 roles/controller_rules_validation/tasks/check_roles.yml create mode 100644 roles/controller_rules_validation/tasks/check_users.yml create mode 100644 roles/controller_rules_validation/tasks/main.yml create mode 100644 roles/controller_rules_validation/tasks/rule_check.yml diff --git a/roles/controller_rules_validation/defaults/main.yml b/roles/controller_rules_validation/defaults/main.yml new file mode 100644 index 00000000..4750ca59 --- /dev/null +++ b/roles/controller_rules_validation/defaults/main.yml @@ -0,0 +1,153 @@ +fail_if_violations_found: true + +print_rules_violations_data: true + +warn_about_audited_types_not_in_rules_objects: false +warn_about_rules_objects_not_in_audited_types: false + +audited_objects: + - applications + - credential_input_sources + - credentials + - credential_types + - execution_environments + - groups + - hosts + - instance_groups + - instances + - inventories + - inventory_sources + - job_templates + - labels + - notification_templates + - organizations + - projects + - roles + - settings + - teams + - users + - workflow_job_templates + +__object_var_names: + projects: controller_projects + job_templates: controller_templates + inventories: controller_inventories + applications: controller_applications + hosts: controller_hosts + credential_input_sources: controller_credential_input_sources + credentials: controller_credentials + credential_types: controller_credential_types + execution_environments: controller_execution_environments + groups: controller_groups + instance_groups: controller_instance_groups + instances: controller_instances + inventory_sources: controller_inventory_sources + labels: controller_labels + notification_templates: controller_notifications + organizations: controller_organizations + roles: controller_roles + settings: controller_settings + teams: controller_teams + users: controller_user_accounts + workflow_job_templates: controller_workflows + schedules: controller_schedules + +__singular: + users: user + teams: team + target_teams: target_team + inventories: inventory + job_templates: job_template + workflows: workflow + credentials: credential + organizations: organization + lookup_organization: lookup_organization + projects: project + instance_groups: instance_group + +__plural: + user: users + team: teams + target_team: target_teams + inventory: inventories + job_template: job_templates + workflow: workflows + credential: credentials + organization: organizations + lookup_organizations: lookup_organizations + project: projects + instance_group: instance_groups + +__org_scoped_objects: + - applications + - credentials + - inventories + - inventory_sources + - job_templates + - notification_templates + - projects + - teams + - users + - workflow_job_templates + +__global_scoped_objects: + - credential_input_sources + - credential_types + - execution_environments + - hosts + - groups + - instance_groups + - instances + - labels + - organizations + - roles + - schedules + - settings + +__scope: + applications: organization + credentials: organization + groups: global + inventories: organization + inventory_sources: organization + job_templates: organization + notification_templates: organization + projects: organization + teams: organization + users: global + workflow_job_templates: organization + credential_input_sources: global + credential_types: global + execution_environments: global + hosts: global + instance_groups: global + instances: global + labels: global + organizations: global + roles: global + schedules: global + settings: global + +__name_field: + applications: name + credentials: name + groups: name + inventories: name + inventory_sources: name + job_templates: name + notification_templates: name + projects: name + teams: name + users: username + workflow_job_templates: name + credential_input_sources: target_credential # optimally it should be "target_credential + '_' + input_field_name" + credential_types: name + execution_environments: name + hosts: name + instance_groups: name + instances: hostname + labels: name + organizations: name + roles: __undefined__ + schedules: name + settings: __undefined__ diff --git a/roles/controller_rules_validation/tasks/check_credentials_encryption.yml b/roles/controller_rules_validation/tasks/check_credentials_encryption.yml new file mode 100644 index 00000000..8f47c80b --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_credentials_encryption.yml @@ -0,0 +1,54 @@ +- name: Init sensitive data list + ansible.builtin.set_fact: + __sensitive_data: [] + __unencrypted: [] + +- name: Set sensitive list + ansible.builtin.set_fact: + __sensitive_data: "{{ __sensitive_data + ([item.key] | product(item.value)) }}" + with_dict: "{{ rule['credential_sensitive_data'] }}" + +- name: Extract unencrypted credentials of all orgs + ansible.builtin.set_fact: + __unencrypted: "{{ __unencrypted + controller_credentials | selectattr('credential_type', 'defined') | selectattr('credential_type', 'equalto', sensitive_cred_type) | selectattr(sensitive_path, 'defined') | rejectattr(sensitive_path, 'vault_encrypted') | product([sensitive_path]) }}" + vars: + sensitive_cred_type: "{{ item[0] }}" + sensitive_path: "inputs.{{ item[1] }}" + loop: "{{ __sensitive_data }}" + when: rule['organizations'] is not defined + or rule['organizations'] == None + or rule['organizations'] | length == 0 + +- name: Extract unencrypted credentials of rule's orgs + ansible.builtin.set_fact: + __unencrypted: "{{ __unencrypted + controller_credentials | selectattr('organization', 'defined') | selectattr('organization', 'in', rule['organizations']) | selectattr('credential_type', 'defined') | selectattr('credential_type', 'equalto', sensitive_cred_type) | selectattr(sensitive_path, 'defined') | rejectattr(sensitive_path, 'vault_encrypted') | product([sensitive_path]) }}" + vars: + sensitive_cred_type: "{{ item[0] }}" + sensitive_path: "inputs.{{ item[1] }}" + loop: "{{ __sensitive_data }}" + when: rule['organizations'] is defined + and rule['organizations'] != None + and rule['organizations'] | length > 0 + +- name: Update rules violation regarding credentials encryption + ansible.builtin.set_fact: + rules_violations_msgs: "{{ (rules_violations_msgs + [msg]) }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'encrypt_credentials_sensitive_data', + 'object_type': 'credentials', + 'object_scope': 'organizations', + 'object_organization': __cred_org, + 'object_name': __cred_name, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | credentials | {{ __cred_org }} | {{ __cred_name }} | The credential sensitive field '{{ unencrypted_cred[1] }}' is not encrypted" + __cred_name: "{{ unencrypted_cred[0]['name'] }}" + __cred_org: "{{ unencrypted_cred[0]['organization'] | default('__undefined_org__') }}" + loop: "{{ __unencrypted }}" + loop_control: + loop_var: unencrypted_cred diff --git a/roles/controller_rules_validation/tasks/check_fields_regex.yml b/roles/controller_rules_validation/tasks/check_fields_regex.yml new file mode 100644 index 00000000..98b2f97c --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_fields_regex.yml @@ -0,0 +1,35 @@ +- name: Init regex issues list + ansible.builtin.set_fact: + __regex_issue: [] + +- name: Extract objects with regex issue - {{ object_type }} + ansible.builtin.set_fact: + __regex_issue: "{{ __regex_issue + ([{'field_regex':field_regex.key, 'regex_value':field_regex.value, 'object_type':object_type}] | product(lookup('vars', __object_var_names[object_type]) | selectattr(field_regex.key, 'defined') | rejectattr(field_regex.key, 'regex', field_regex.value))) }}" + loop: "{{ rule['fields_regex'] | dict2items }}" + loop_control: + loop_var: field_regex + +- name: Update violation msgs with regex issue - {{ object_type }} + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'fields_regex', + 'object_type': object_type, + 'object_scope': __scope[object_type], + 'object_organization': item[1]['organization'] | default('__undefined_org__'), + 'object_name': __object_name, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ __object_type }} | {{ __object_scope }} | {{ __object_name }} | The value of the field {{ __field }} ({{ __value }}) do not respect the regex ({{ __regex }})" + __object_type: "{{ item[0]['object_type'] }}" + __object_scope: "{{ item[1]['organization'] | default(__scope[object_type]) | default('__undefined_org__') }}" + __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fileds + __field: "{{ item[0]['field_regex'] }}" + __value: "{{ item[1][item[0]['field_regex']] }}" + __regex: "{{ item[0]['regex_value'] }}" + loop: "{{ __regex_issue }}" diff --git a/roles/controller_rules_validation/tasks/check_hosts.yml b/roles/controller_rules_validation/tasks/check_hosts.yml new file mode 100644 index 00000000..c344d764 --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_hosts.yml @@ -0,0 +1,36 @@ +- name: Check for ungrouped hosts + when: rule['allow_ungrouped_hosts'] is defined + and not rule['allow_ungrouped_hosts'] + and controller_groups is defined + and controller_hosts is defined + block: + - name: Extract all and grouped hosts + ansible.builtin.set_fact: + __grouped_hosts: "{{ controller_groups | selectattr('hosts', 'defined') | map(attribute='hosts') | flatten | unique }}" + __all_hosts: "{{ controller_hosts | map(attribute='name') | flatten | unique }}" + + - name: Extract ungrouped hosts + ansible.builtin.set_fact: + __ungrouped_hosts: "{{ __all_hosts | difference(__grouped_hosts) }}" + + - name: Update violations if ungrouped hosts found + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'allow_ungrouped_hosts', + 'object_type': 'hosts', + 'object_scope': 'global', + 'object_organization': '__organizationless__', + 'object_name': '__multiple_objects__', + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | hosts | global | {{ __ungrouped_hosts_names }} | Found {{ __ungrouped_hosts | length }} ungrouped hosts" + __ungrouped_hosts_names: "{{ (__ungrouped_hosts[:3] | join(',') + '...') if (__ungrouped_hosts | length) > 3 else (__ungrouped_hosts | join(',')) }}" + when: __ungrouped_hosts is defined + and __ungrouped_hosts != None + and __ungrouped_hosts | length > 0 diff --git a/roles/controller_rules_validation/tasks/check_inventories.yml b/roles/controller_rules_validation/tasks/check_inventories.yml new file mode 100644 index 00000000..9cfaf272 --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_inventories.yml @@ -0,0 +1,27 @@ +- name: Check if inventory hosts count is inferior to maximum allowed + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'max_hosts_per_inventory', + 'object_type': 'inventories', + 'object_scope': 'organization', + 'object_organization': __inventory_org, + 'object_name': __inventory_name, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | inventories | {{ __inventory_org }} | {{ __inventory_name }} | Inventory has more hosts ({{ __hosts_in_inventory }}) than allowed ({{ rule['max_hosts_per_inventory'] }})" + __hosts_in_inventory: "{{ controller_hosts | selectattr('inventory', 'equalto', inventory['name']) | length }}" + __inventory_org: "{{ inventory['organization'] | default('__undefined_org__') }}" + __inventory_name: "{{ inventory['name'] | default('__undefined_name__') }}" + when: controller_hosts is defined + and controller_hosts != None + and controller_hosts | length > 0 + and rule['max_hosts_per_inventory'] is defined + and rule['max_hosts_per_inventory'] > 0 + and inventory['name'] not in (rule['exceptions']['inventories'] | default([])) + and controller_hosts | selectattr('inventory', 'equalto', inventory['name']) | length > rule['max_hosts_per_inventory'] diff --git a/roles/controller_rules_validation/tasks/check_mandatory_fields.yml b/roles/controller_rules_validation/tasks/check_mandatory_fields.yml new file mode 100644 index 00000000..0e04b8cc --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_mandatory_fields.yml @@ -0,0 +1,74 @@ +- name: Init - {{ object_type }} + ansible.builtin.set_fact: + __defined_none: [] + __defined_empty: [] + __undefined: [] + +- name: Extract objects with mandatory defined but empty (None) fields - {{ object_type }} + ansible.builtin.set_fact: + __defined_none: "{{ __defined_none + [{'mandatory_field':mandatory_field, 'object_type': object_type}] | product(lookup('vars', __object_var_names[object_type]) | selectattr(mandatory_field, 'defined') | selectattr(mandatory_field, 'equalto', None)) }}" + loop: "{{ rule['mandatory_fields'] }}" + loop_control: + loop_var: mandatory_field + +- name: Extract objects with mandatory defined but contains empty string - {{ object_type }} + ansible.builtin.set_fact: + __defined_empty: "{{ __defined_empty + [{'mandatory_field':mandatory_field, 'object_type': object_type}] | product(lookup('vars', __object_var_names[object_type]) | selectattr(mandatory_field, 'defined') | selectattr(mandatory_field, 'equalto', '')) }}" + loop: "{{ rule['mandatory_fields'] }}" + loop_control: + loop_var: mandatory_field + +- name: Update violation msgs with mandatory defined but empty fields - {{ object_type }} + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'mandatory_fields', + 'object_type': object_type, + 'object_scope': __scope[object_type], + 'object_organization': __object_org, + 'object_name': __object_name, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ __object_type }} | {{ __object_scope }} | {{ __object_name }} | The mandatory field '{{ __field }}' is empty" + __field: "{{ item[0]['mandatory_field'] }}" + __object_type: "{{ item[0]['object_type'] }}" + __object_scope: "{{ item[1]['organization'] | default(__scope[object_type]) | default('__undefined_org__') }}" + __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fileds + __object_org: "{{ item[1]['organization'] | default('__undefined_org__') }}" + loop: "{{ __defined_empty + __defined_none }}" + +- name: Extract objects with undefined mandatory fields - {{ object_type }} + ansible.builtin.set_fact: + __undefined: "{{ __undefined + [{'mandatory_field':mandatory_field, 'object_type': object_type}] | product(lookup('vars', __object_var_names[object_type]) | selectattr(mandatory_field, 'undefined')) }}" + loop: "{{ rule['mandatory_fields'] }}" + loop_control: + loop_var: mandatory_field + +- name: Update violation msgs with mandatory undefined fields - {{ object_type }} + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'mandatory_fields', + 'object_type': __object_type, + 'object_scope': __scope[object_type], + 'object_organization': __object_org, + 'object_name': __object_name, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ __object_type }} | {{ __object_scope }} | {{ __object_name }} | The mandatory field '{{ __field }}' is not defined" + __field: "{{ item[0]['mandatory_field'] }}" + __object_type: "{{ item[0]['object_type'] }}" + __object_scope: "{{ item[1]['organization'] | default(__scope[object_type]) | default('__undefined_org__') }}" + __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fields + __object_org: "{{ item[1]['organization'] | default('__undefined_org__') }}" + loop: "{{ __undefined }}" diff --git a/roles/controller_rules_validation/tasks/check_objects_count.yml b/roles/controller_rules_validation/tasks/check_objects_count.yml new file mode 100644 index 00000000..45dcb379 --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_objects_count.yml @@ -0,0 +1,89 @@ +- name: Global objects minimum count check + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'minimum_defined_globally', + 'object_type': object_type, + 'object_scope': 'global', + 'object_organization': '__organizationless__', + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ object_type }} | global | Global {{ object_type }} count ({{ lookup('vars', __object_var_names[object_type]) | length }}) is inferior to the minimum allowed ({{ rule['minimum_defined_globally'] }})" + when: rule['minimum_defined_globally'] is defined + and (lookup('vars', __object_var_names[object_type]) | unique | length < rule['minimum_defined_globally']) + +- name: Per organization objects minimum count check + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'minimum_defined_per_org', + 'object_type': object_type, + 'object_scope': 'organization', + 'object_organization': org, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ object_type }} | {{ org }} | Organization {{ org }} {{ object_type }} count ({{ __object_count }}) is inferior to the minimum allowed ({{ __org_minimum }})" + __object_count: "{{ lookup('vars', __object_var_names[object_type]) | selectattr('organization', 'defined') | selectattr('organization', 'equalto', org) | unique | length }}" + __org_minimum: "{{ rule['minimum_defined_per_org'] }}" + when: rule['organizations'] is defined + and object_type in __org_scoped_objects + and rule['minimum_defined_per_org'] is defined + and ((lookup('vars', __object_var_names[object_type]) | selectattr('organization', 'defined') | selectattr('organization', 'equalto', org) | unique | length) < rule['minimum_defined_per_org']) + loop: "{{ rule['organizations'] }}" + loop_control: + loop_var: org + +- name: Objects count maximum check + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'maximum_defined_globally', + 'object_type': object_type, + 'object_scope': 'global', + 'object_organization': '__organizationless__', + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ object_type }} | global | Global {{ object_type }} count ({{ lookup('vars', __object_var_names[object_type]) | length }}) is superior to the maximum allowed ({{ rule['maximum_defined_globally'] }})" + when: rule['maximum_defined_globally'] is defined + and lookup('vars', __object_var_names[object_type]) | length > rule['maximum_defined_globally'] + +- name: Per organization objects maximum count check + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'maximum_defined_per_org', + 'object_type': object_type, + 'object_scope': 'organization', + 'object_organization': org, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | {{ object_type }} | {{ org }} | Organization {{ org }} {{ object_type }} count ({{ __object_count }}) is superior to the maximum allowed ({{ __org_maximum }})" + __object_count: "{{ lookup('vars', __object_var_names[object_type]) | selectattr('organization', 'defined') | selectattr('organization', 'equalto', org) | unique | length }}" + __org_maximum: "{{ rule['maximum_defined_per_org'] }}" + when: rule['organizations'] is defined + and object_type in __org_scoped_objects + and rule['maximum_defined_per_org'] is defined + and ((lookup('vars', __object_var_names[object_type]) | selectattr('organization', 'defined') | selectattr('organization', 'equalto', org) | unique | length) > rule['maximum_defined_per_org']) + loop: "{{ rule['organizations'] }}" + loop_control: + loop_var: org diff --git a/roles/controller_rules_validation/tasks/check_organizations.yml b/roles/controller_rules_validation/tasks/check_organizations.yml new file mode 100644 index 00000000..1112a931 --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_organizations.yml @@ -0,0 +1,88 @@ +- name: Check organizations max_hosts when defined + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'max_hosts_per_organization', + 'object_type': 'organizations', + 'object_scope': 'global', + 'object_organization': '__undefined_org__', + 'object_name': org['name'], + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | organizations | global | {{ org['name'] }} | max_hosts ({{ org['max_hosts'] }}) is superior to {{ rule['max_hosts_per_organization'] }}" + when: org['name'] not in (rule['exceptions']['organizations'] | default([])) + and org['max_hosts'] is defined + and rule['max_hosts_per_organization'] is defined + and org['max_hosts'] | int > rule['max_hosts_per_organization'] | int + +- name: Check organizations undefined or unset max_hosts + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'max_hosts_per_organization', + 'object_type': 'organizations', + 'object_scope': 'global', + 'object_organization': '__undefined_org__', + 'object_name': org['name'], + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | organizations | global | {{ org['name'] }} | max_hosts is not set" + when: org['name'] not in (rule['exceptions']['organizations'] | default([])) + and rule['max_hosts_per_organization'] is defined + and (org['max_hosts'] is not defined + or org['max_hosts'] == "" + or org['max_hosts'] == None) + +- name: Check if organizations default EE is in allowed list + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'max_hosts_per_organization', + 'object_type': 'organizations', + 'object_scope': 'global', + 'object_organization': '__undefined_org__', + 'object_name': org['name'], + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | organizations | global | {{ org['name'] }} | The EE ({{ org['default_environment'] }}) is not allowed." + when: org['name'] not in (rule['exceptions']['organizations'] | default([])) + and org['default_environment'] is defined + and rule['allowed_organization_default_environments'] is defined + and org['default_environment'] not in rule['allowed_organization_default_environments'] + +- name: Check if organizations default EE is in forbidden list + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'max_hosts_per_organization', + 'object_type': 'organizations', + 'object_scope': 'global', + 'object_organization': '__undefined_org__', + 'object_name': org['name'], + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | organizations | global | {{ org['name'] }} | The EE ({{ org['default_environment'] }}) is forbidden." + when: org['name'] not in (rule['exceptions']['organizations'] | default([])) + and org['default_environment'] is defined + and rule['forbidden_organization_default_environments'] is defined + and org['default_environment'] in rule['forbidden_organization_default_environments'] diff --git a/roles/controller_rules_validation/tasks/check_roles.yml b/roles/controller_rules_validation/tasks/check_roles.yml new file mode 100644 index 00000000..8a8ba075 --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_roles.yml @@ -0,0 +1,130 @@ +- name: Init variables + ansible.builtin.set_fact: + __forbidden_roles: [] + __forbidden: [] + __objects_and_roles: [] + __objects_and_roles_product: [] + unallowed_roles: [] + +- name: Verify forbidden roles presence + when: rule['forbidden_roles'] is defined + and rule['forbidden_roles'] != None + and rule['forbidden_roles'] | length > 0 + block: + + - name: Set forbidden roles list + ansible.builtin.set_fact: + __forbidden_roles: "{{ __forbidden_roles + ([item.key] | product(item.value)) }}" + with_dict: "{{ rule['forbidden_roles'] }}" + + - name: Extract declared forbidden roles + ansible.builtin.set_fact: + __forbidden: "{{ __forbidden + + ( + ( + ( + (controller_roles | selectattr(__role_target, 'defined')) + + (controller_roles | selectattr(__singular[__role_target], 'defined')) + ) | selectattr('role', 'defined') | selectattr('role', 'equalto', __role_verb)) + + ( + (controller_roles | selectattr(__role_target,'defined')) + + (controller_roles | selectattr(__singular[__role_target], 'defined')) + ) | selectattr('roles', 'defined') | selectattr('roles', 'contains', __role_verb) + ) | product([{'role_target_object': __role_target, 'role_verb': __role_verb}]) + }}" + vars: + __role_target: "{{ item[0] }}" + __role_verb: "{{ item[1] }}" + loop: "{{ __forbidden_roles }}" + + - name: Update violations related to forbidden roles + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'forbidden_roles', + 'object_type': 'roles', + 'object_scope': 'global', + 'object_organization': '__undefined_org__', + 'object_name': '__undefined_name__', + 'role_details': + { + 'users': __role_users, + 'teams': __role_teams, + 'targets': __role_target, + 'role': __role_verb + }, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | roles | global | __undefined_org__ | The role '{{ __role_verb }}' on '{{ __role_target }}' requested for '{{ __role_benefactor }}' is forbidden" + __role_users: "{{ item[0]['user'] | default('') + item[0]['users'] | default([]) | join(',') }}" + __role_teams: "{{ item[0]['team'] | default('') + item[0]['teams'] | default([]) | join(',') }}" + __role_target: "{{ item[1]['role_target_object'] }}" + __role_verb: "{{ item[1]['role_verb'] }}" + __role_benefactor: "{{ (('team(s) ' + __role_teams) if __role_teams != '' else '') + ((' and ') if __role_teams != '' and __role_users != '' else '' ) + (('user(s) ' + __role_users) if __role_users != '' else '' ) }}" + loop: "{{ __forbidden }}" + +- name: Verify non-allowed roles presence + when: rule['allowed_roles'] is defined + and rule['allowed_roles'] != None + and rule['allowed_roles'] | length > 0 + block: + + - name: Extract and format declared roles + ansible.builtin.set_fact: + __objects_and_roles: "{{ __objects_and_roles + + [{ + 'objects': item | dict2items | rejectattr('key', 'in' ,['role', 'roles', 'teams', 'team', 'teams', 'user', 'users']) | map(attribute='key'), + 'roles': [ item['role'] | default('') ] | select() + item['roles'] | default([]) | select() + }] }}" + loop: "{{ controller_roles }}" + + - name: Expand declared roles for easier comparaison + ansible.builtin.set_fact: + __objects_and_roles_product: "{{ __objects_and_roles_product + item['objects'] | product(item['roles']) }}" + loop: "{{ __objects_and_roles }}" + + - name: Compute unallowed_roles + ansible.builtin.set_fact: + unallowed_roles: "{{ unallowed_roles + [ {'object': __object, 'role': __role } ] }}" + when: ( + (__plural[__object] is defined and rule['allowed_roles'][__plural[__object]] is not defined) + or (__singular[__object] is defined and rule['allowed_roles'][__object] is not defined) + ) + or + ( + (__plural[__object] is defined and rule['allowed_roles'][__plural[__object]] is defined and __role not in rule['allowed_roles'][__plural[__object]]) + or (__singular[__object] is defined and rule['allowed_roles'][__object] is defined and __role not in rule['allowed_roles'][__object]) + ) + vars: + __object: "{{ item[0] }}" + __role: "{{ item[1] }}" + loop: "{{ __objects_and_roles_product }}" + + - name: Update violations related to non-allowed roles + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'allowed_roles', + 'object_type': 'roles', + 'object_scope': 'global', + 'object_organization': '__undefined_org__', + 'object_name': '__undefined_name__', + 'role_details': + { + 'targets': item['object'], + 'role': item['role'] + }, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | roles | global | __undefined_org__ | The role '{{ item['role'] }}' on '{{ item['object'] }}' is not allowed." + loop: "{{ unallowed_roles }}" diff --git a/roles/controller_rules_validation/tasks/check_users.yml b/roles/controller_rules_validation/tasks/check_users.yml new file mode 100644 index 00000000..cc3f7e9b --- /dev/null +++ b/roles/controller_rules_validation/tasks/check_users.yml @@ -0,0 +1,77 @@ +- name: Update rules violation regarding users passwords encryption + ansible.builtin.set_fact: + rules_violations_msgs: "{{ (rules_violations_msgs + [msg]) }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'encrypt_user_passwords', + 'object_type': 'users', + 'object_scope': 'organizations', + 'object_organization': __user_org, + 'object_name': __username, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | users | {{ __user_org }} | {{ __username }} | The user's password is not encrypted" + __username: "{{ __unencrypted_user['username'] | default(__unencrypted_user['user']) }}" + __user_org: "{{ __unencrypted_user['organization'] | default('__undefined_org__') }}" + loop: "{{ controller_user_accounts | selectattr('password', 'defined') | rejectattr('password', 'vault_encrypted') }}" + when: rule['encrypt_user_passwords'] is defined + and rule['encrypt_user_passwords'] | bool + and __unencrypted_user['username'] | default(__unencrypted_user['user']) not in (rule['exceptions']['users'] | default([])) + loop_control: + loop_var: __unencrypted_user + +- name: Update rules violation regarding superusers + ansible.builtin.set_fact: + rules_violations_msgs: "{{ (rules_violations_msgs + [msg]) }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'allow_superusers', + 'object_type': 'users', + 'object_scope': 'organizations', + 'object_organization': __user_org, + 'object_name': __username, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | users | {{ __user_org }} | {{ __username }} | Superusers are not allowed" + __username: "{{ __superusers['username'] | default(__superusers['user']) }}" + __user_org: "{{ __superusers['organization'] | default('__undefined_org__') }}" + loop: "{{ controller_user_accounts | selectattr('is_superuser', 'defined') | rejectattr('is_superuser', 'false') }}" + when: rule['allow_superusers'] is defined + and not rule['allow_superusers'] | bool + and __superusers['username'] | default(__superusers['user']) not in (rule['exceptions']['users'] | default([])) + loop_control: + loop_var: __superusers + +- name: Update rules violation regarding systm auditors + ansible.builtin.set_fact: + rules_violations_msgs: "{{ (rules_violations_msgs + [msg]) }}" + rules_violations_data: "{{ rules_violations_data + + [{ + 'rule_id': rule_id, + 'rule_index': rule_index, + 'rule_broken': 'allow_system_auditors', + 'object_type': 'users', + 'object_scope': 'organizations', + 'object_organization': __user_org, + 'object_name': __username, + 'msg': msg + }] + }}" + vars: + msg: "Rule {{ rule_id }} | users | {{ __user_org }} | {{ __username }} | System Auditors are not allowed" + __username: "{{ __system_auditors['username'] | default(__system_auditors['user']) }}" + __user_org: "{{ __system_auditors['organization'] | default('__undefined_org__') }}" + when: rule['allow_system_auditors'] is defined + and not rule['allow_system_auditors'] | bool + and __system_auditors['username'] | default(__system_auditors['user']) not in (rule['exceptions']['users'] | default([])) + loop: "{{ controller_user_accounts | selectattr('is_system_auditor', 'defined') | rejectattr('is_system_auditor', 'false') }}" + loop_control: + loop_var: __system_auditors diff --git a/roles/controller_rules_validation/tasks/main.yml b/roles/controller_rules_validation/tasks/main.yml new file mode 100644 index 00000000..95ad7b88 --- /dev/null +++ b/roles/controller_rules_validation/tasks/main.yml @@ -0,0 +1,55 @@ +- name: Look for rules violations + when: controller_rules is defined and controller_rules != None and controller_rules | length > 0 + block: + - name: Init rules violations + ansible.builtin.set_fact: + rules_violations_msgs: [] + rules_violations_data: [] + __objects_in_rules: [] + + - name: Rules check loop + ansible.builtin.include_tasks: + file: tasks/rule_check.yml + loop: "{{ controller_rules }}" + when: rule['objects'] is defined and rule['objects'] != None and rule['objects'] | length > 0 + loop_control: + loop_var: rule + index_var: rule_index + + - name: Flag discrepancy between objects in rules and in audited types as violations + when: warn_about_audited_types_not_in_rules_objects or warn_about_rules_objects_not_in_audited_types + block: + - name: Get object types declared rules + ansible.builtin.set_fact: + __objects_in_rules: "{{ controller_rules | map(attribute='objects') | flatten | unique }}" + + - name: Object types in rules that are not part of audited object types + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + vars: + diff: "{{ __objects_in_rules | difference(audited_objects) }}" + msg: "The object type(s) '{{ diff | join(',') }}' are mentioned in rules but are not audited." + when: warn_about_audited_types_not_in_rules_objects and diff is defined and diff | length > 0 + + - name: Audited object types that are not mentioned in any rule + ansible.builtin.set_fact: + rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" + vars: + diff: "{{ audited_objects | difference(__objects_in_rules) }}" + msg: "The object types(s) '{{ diff | join(',') }}' are audited but are not mentioned in any rule." + when: warn_about_rules_objects_not_in_audited_types and diff is defined and diff | length > 0 + + - name: Print rules violations data + ansible.builtin.debug: + var: rules_violations_data | unique + when: print_rules_violations_data is defined and print_rules_violations_data | bool + + - name: Print rules violations messages + ansible.builtin.debug: + var: rules_violations_msgs | unique + failed_when: fail_if_violations_found is defined and fail_if_violations_found | bool and rules_violations_msgs | length > 0 + + - name: Print success message + ansible.builtin.debug: + msg: "Success. No rules violations detected." + when: rules_violations_msgs | length == 0 diff --git a/roles/controller_rules_validation/tasks/rule_check.yml b/roles/controller_rules_validation/tasks/rule_check.yml new file mode 100644 index 00000000..7892c479 --- /dev/null +++ b/roles/controller_rules_validation/tasks/rule_check.yml @@ -0,0 +1,103 @@ +- name: Set rule ID + ansible.builtin.set_fact: + rule_id: "{{ rule['rule_name'] | default('n°' + (rule_index + 1) | string) }}" + +- name: Objects count check + ansible.builtin.include_tasks: + file: tasks/check_objects_count.yml + when: lookup('vars', __object_var_names[object_type], default='__undefined__') != '__undefined__' and + ((rule['minimum_defined_globally'] is defined and rule['minimum_defined_globally'] > 0) + or (rule['maximum_defined_globally'] is defined and rule['maximum_defined_globally'] > 0) + or (rule['minimum_defined_per_org'] is defined and rule['minimum_defined_per_org'] > 0) + or (rule['maximum_defined_per_org'] is defined and rule['maximum_defined_per_org'] > 0)) + loop: "{{ rule['objects'] }}" + loop_control: + loop_var: object_type + +- name: Mandatory field check + ansible.builtin.include_tasks: + file: tasks/check_mandatory_fields.yml + when: rule['mandatory_fields'] is defined + and rule['mandatory_fields'] != None + and rule['mandatory_fields'] | length > 0 + and lookup('vars', __object_var_names[object_type], default='__undefined__') != '__undefined__' + loop: "{{ rule['objects'] }}" + loop_control: + loop_var: object_type + +- name: Regex check loop + ansible.builtin.include_tasks: + file: tasks/check_fields_regex.yml + when: rule['fields_regex'] is defined + and lookup('vars', __object_var_names[object_type], default='__undefined__') != '__undefined__' + loop: "{{ rule['objects'] }}" + loop_control: + loop_var: object_type + +- name: Organizations specific checks - {{ object_type }} + ansible.builtin.include_tasks: + file: tasks/check_organizations.yml + when: controller_organizations is defined + and controller_organizations | length > 0 + and 'organizations' in rule['objects'] + and (org['name'] in rule['organizations'] if rule['organizations'] is defined else true) + loop: "{{ controller_organizations }}" + loop_control: + loop_var: org + +- name: Inventory specific checks + ansible.builtin.include_tasks: + file: tasks/check_inventories.yml + when: controller_inventories is defined + and controller_inventories | length > 0 + and 'inventories' in rule['objects'] + loop: "{{ controller_inventories }}" + loop_control: + loop_var: inventory + +- name: Hosts specific checks + ansible.builtin.include_tasks: + file: tasks/check_hosts.yml + when: controller_hosts is defined + and 'hosts' in rule['objects'] + and rule['allow_ungrouped_hosts'] is defined + and not rule['allow_ungrouped_hosts'] + +- name: Credential encryption check + ansible.builtin.include_tasks: + file: tasks/check_credentials_encryption.yml + when: controller_credentials is defined + and controller_credentials | length > 0 + and 'credentials' in rule['objects'] + and rule['encrypt_credentials_sensitive_data'] is defined + and rule['encrypt_credentials_sensitive_data'] | bool + and rule['credential_sensitive_data'] is defined + and rule['credential_sensitive_data'] | length > 0 + +- name: User specific checks + ansible.builtin.include_tasks: + file: tasks/check_users.yml + when: controller_user_accounts is defined + and controller_user_accounts | length > 0 + and 'users' in rule['objects'] + and + ( + (rule['allow_superusers'] is defined and not rule['allow_superusers'] | bool ) + or + (rule['allow_system_auditors'] is defined and not rule['allow_system_auditors'] | bool ) + or + (rule['encrypt_user_passwords'] is defined and rule['encrypt_user_passwords'] | bool ) + ) + +- name: Roles specific checks + ansible.builtin.include_tasks: + file: tasks/check_roles.yml + when: controller_roles is defined + and controller_roles | length > 0 + and 'roles' in rule['objects'] + and + ( + (rule['allowed_roles'] is defined and rule['allowed_roles'] | length > 0 ) + or + (rule['forbidden_roles'] is defined and rule['forbidden_roles'] | length > 0 ) + ) From 3162045941e4c950f798b031afa6b41fd521cc8a Mon Sep 17 00:00:00 2001 From: w4hf Date: Thu, 17 Oct 2024 23:52:58 +0200 Subject: [PATCH 04/14] sample of controller_rules used by controller_rules_validation role --- tests/configs/controller_rules.yml | 224 +++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 tests/configs/controller_rules.yml diff --git a/tests/configs/controller_rules.yml b/tests/configs/controller_rules.yml new file mode 100644 index 00000000..d028f760 --- /dev/null +++ b/tests/configs/controller_rules.yml @@ -0,0 +1,224 @@ +--- +controller_rules: + + # ------- Rule n°1 # Generic - Minimum and Maximum + - rule_name: Generic Stuff + organizations: + - Satellite + - Default + objects: + - credentials + - groups + - inventories + - inventory_sources + - job_templates + - projects + - teams + - users + - credential_types + - instances + minimum_defined_globally: 3 + maximum_defined_globally: 5 + minimum_defined_per_org: 2 + maximum_defined_per_org: 4 + + # ------- Rule n°2 # Generic - Regex + - organizations: + - Default + objects: + - projects + - credentials + fields_regex: + name: '^[Default].*' # name must start with '[Default]' + description: "^DESC - .*" # description must start with 'DESC - ' + scm_type: ^git$ # accept only git scm + scm_branch: ^main$ # branch naming rule : accept only main branch + + # ------- Rule n°3 # Generic - Mandatory fields + - objects: + - credentials + - groups + - inventories + - inventory_sources + - job_templates + - projects + - teams + - users + - credential_types + - instances + mandatory_fields: + - description + + # ------- Rule n°4 # Organizations + - organizations: + - Satellite + - Default + - objects: + - organizations + max_hosts_per_organization: 100 + allowed_organization_default_environments: + - Automation Hub Minimal Execution Environment + # forbidden_organization_default_environments: + # - Automation Hub Default Execution Environment + # - Custom EE + exceptions: + organizations: + - Satellite + + # ------- Rule n°5 # inventories + - organizations: + - Satellite + - Default + objects: + - inventories + max_hosts_per_inventory: 1 # needs 'controller_hosts' and 'controller_inventories' to be defined + exceptions: + inventories: + - localhost + + # ------- Rule n°6 # hosts + - objects: + - hosts + allow_ungrouped_hosts: false # needs 'controller_groups' and 'controller_hosts' to be defined + + # ------- Rule n°7 # Credentials + - rule_name: Credentials Check # Optional arbitrary string + encrypt_credentials_sensitive_data: true # DO NOT WORK with intermediary variables (filetree_read) + organizations: + - Default + - Satellite + objects: + - credentials + credential_sensitive_data: + Source Control: + - password + Red Hat Virtualization: + - password + Vault: + - vault_password + CyberArk Central Credential Provider Lookup: + - app_id + + # ------- Rule n°8 # Users + - objects: + - users + allow_superusers: false + allow_system_auditors: false + encrypt_user_passwords: true + exceptions: + users: + - controller_admin + - admin + + # ------- Rule n°9 # Roles + - objects: + - roles + allowed_roles: + projects: + - read + - update + organizations: + - member + target_teams: + - admin + - member + # forbidden_roles: + # projects: + # - update + # - admin + # organizations: + # - admin + # target_teams: + # - admin + + +# # -------------------------------------------- All Possible Options : +# - rule_name: Custom rule name # Optional arbitrary string +# organizations: +# - Satellite # Audited organization +# - Default +# objects: +# # org scope: +# - applications +# - credentials +# - inventories +# - inventory_sources +# - job_templates +# - notification_templates +# - projects +# - teams +# - users +# - workflow_job_templates +# # global scope: +# - credential_input_sources +# - credential_types +# - execution_environments +# - groups +# - hosts +# - instance_groups +# - instances +# - labels +# - organizations +# - roles +# - schedules +# - settings + +# # ------ generic rules +# minimum_defined: 1 # at least 1 organization is defined +# maximum_defined: 2 # no more than 2 organization +# mandatory_fields: +# - description # force use of description & max_hosts field +# - max_hosts +# fields_regex: +# name: '\[Default\].*' # names should start with '[Default]' +# description: "^DESC - .*" # description should start with 'DESC - ' +# scm_type: ^git$ # accept only git scm +# scm_branch: ^main$ # accept only main branch + +# # ------ organization specific rules +# max_hosts_per_organization: 100 +# allowed_organization_default_environments: +# - Automation Hub Minimal Execution Environment +# forbidden_organization_default_environments: +# - Automation Hub Default Execution Environment +# - Custom EE + +# # ------ inventory specific rules +# max_hosts_per_inventory: 1 # needs both 'controller_inventories' and 'controller_hosts' to be defined + +# # ------ hosts specific rules +# allow_ungrouped_hosts: false # needs both 'controller_groups' and 'controller_hosts' to be defined + +# # ------ Credentials specific rules +# encrypt_credentials_sensitive_data: true # DO NOT WORK with intermediary variables (filetree_read) +# credential_sensitive_data: +# Source Control: +# - password + +# # ------- Users specific rules +# allow_superusers: false +# allow_system_auditors: false +# encrypt_user_passwords: true # DO NOT WORK with intermediary variables (filetree_read) +# exceptions: +# users: +# - admin + +# # ------- Roles specific rules +# allowed_roles: +# projects: +# - read +# - update +# organizations: +# - member +# target_teams: +# - admin +# - member +# +# forbidden_roles: +# projects: +# - update +# - admin +# organizations: +# - admin +# target_teams: +# - admin From cf3e3659c9b987b7e1dad71167fca7a255cea4fb Mon Sep 17 00:00:00 2001 From: w4hf Date: Thu, 17 Oct 2024 23:53:30 +0200 Subject: [PATCH 05/14] playbooks used to test controller_rules_validation role --- .../controller_rules_validation_filetree_read.yml | 7 +++++++ .../playbooks/controller_rules_validation_include.yml | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/playbooks/controller_rules_validation_filetree_read.yml create mode 100644 tests/playbooks/controller_rules_validation_include.yml diff --git a/tests/playbooks/controller_rules_validation_filetree_read.yml b/tests/playbooks/controller_rules_validation_filetree_read.yml new file mode 100644 index 00000000..ce0bf176 --- /dev/null +++ b/tests/playbooks/controller_rules_validation_filetree_read.yml @@ -0,0 +1,7 @@ +--- +- name: Check for controller rules violations with configuration read using filetree_read + hosts: localhost + gather_facts: false + roles: + - role: infra.aap_controller_configuration_extended.filetree_read + - role: infra.aap_controller_configuration_extended.controller_rules_validation diff --git a/tests/playbooks/controller_rules_validation_include.yml b/tests/playbooks/controller_rules_validation_include.yml new file mode 100644 index 00000000..d0246a1e --- /dev/null +++ b/tests/playbooks/controller_rules_validation_include.yml @@ -0,0 +1,11 @@ +--- +- name: Check for controller rules violations with configuration read using include_vars + hosts: localhost + gather_facts: false + pre_tasks: + - name: Read configuration + ansible.builtin.include_vars: + dir: ../configs/ + roles: + - role: infra.aap_controller_configuration_extended.controller_rules_validation +... From 06d8a060d73edc955dfd22ce3ba7e1a5e5359f94 Mon Sep 17 00:00:00 2001 From: w4hf Date: Fri, 18 Oct 2024 18:51:00 +0200 Subject: [PATCH 06/14] add default empty rules to avoid errors --- roles/controller_rules_validation/defaults/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/roles/controller_rules_validation/defaults/main.yml b/roles/controller_rules_validation/defaults/main.yml index 4750ca59..fa02ad86 100644 --- a/roles/controller_rules_validation/defaults/main.yml +++ b/roles/controller_rules_validation/defaults/main.yml @@ -1,3 +1,5 @@ +controller_rules: [] + fail_if_violations_found: true print_rules_violations_data: true From 6bd1c3c325e357ce675474223a77481aa5ae9821 Mon Sep 17 00:00:00 2001 From: w4hf Date: Fri, 18 Oct 2024 18:51:28 +0200 Subject: [PATCH 07/14] improve and clarify example rules --- tests/configs/controller_rules.yml | 60 ++++++++++++++++-------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/tests/configs/controller_rules.yml b/tests/configs/controller_rules.yml index d028f760..4fdad9a0 100644 --- a/tests/configs/controller_rules.yml +++ b/tests/configs/controller_rules.yml @@ -29,7 +29,7 @@ controller_rules: - projects - credentials fields_regex: - name: '^[Default].*' # name must start with '[Default]' + name: '^\[Default\].*' # name must start with '[Default]' description: "^DESC - .*" # description must start with 'DESC - ' scm_type: ^git$ # accept only git scm scm_branch: ^main$ # branch naming rule : accept only main branch @@ -49,23 +49,28 @@ controller_rules: mandatory_fields: - description - # ------- Rule n°4 # Organizations - - organizations: - - Satellite - - Default + # ------- Rule n°4 # Organizations - Allow only the following EEs - objects: - organizations + organizations: + - Satellite + - Default max_hosts_per_organization: 100 allowed_organization_default_environments: - - Automation Hub Minimal Execution Environment - # forbidden_organization_default_environments: - # - Automation Hub Default Execution Environment - # - Custom EE + - Automation Hub Default Execution Environment + - Custom EE + + # ------- Rule n°5 # Organizations - Allow all EEs except the listed forbidden EEs for all organizations except Satellite + - objects: + - organizations + max_hosts_per_organization: 10 + forbidden_organization_default_environments: + - Automation Hub Default Execution Environment exceptions: organizations: - Satellite - # ------- Rule n°5 # inventories + # ------- Rule n°6 # inventories - organizations: - Satellite - Default @@ -76,12 +81,12 @@ controller_rules: inventories: - localhost - # ------- Rule n°6 # hosts + # ------- Rule n°7 # hosts - objects: - hosts - allow_ungrouped_hosts: false # needs 'controller_groups' and 'controller_hosts' to be defined + allow_ungrouped_hosts: false # needs 'controller_groups' and 'controller_hosts' to be defined - # ------- Rule n°7 # Credentials + # ------- Rule n°8 # Credentials - rule_name: Credentials Check # Optional arbitrary string encrypt_credentials_sensitive_data: true # DO NOT WORK with intermediary variables (filetree_read) organizations: @@ -96,10 +101,8 @@ controller_rules: - password Vault: - vault_password - CyberArk Central Credential Provider Lookup: - - app_id - # ------- Rule n°8 # Users + # ------- Rule n°9 # Users - objects: - users allow_superusers: false @@ -110,27 +113,28 @@ controller_rules: - controller_admin - admin - # ------- Rule n°9 # Roles + # ------- Rule n°10 # Roles - Allow ONLY 'read' on projects, 'member' on organizations and 'admin' on teams. Any other role is forbidden. - objects: - roles allowed_roles: projects: - - read - update + - read organizations: - member target_teams: - - admin - member - # forbidden_roles: - # projects: - # - update - # - admin - # organizations: - # - admin - # target_teams: - # - admin + # ------- Rule n°11 # Roles - Do not allow 'admin' on projects, 'admin' on organizations and 'admin' on teams, allow everything else + - objects: + - roles + forbidden_roles: + projects: + - admin + organizations: + - admin + target_teams: + - admin # # -------------------------------------------- All Possible Options : # - rule_name: Custom rule name # Optional arbitrary string @@ -222,3 +226,5 @@ controller_rules: # - admin # target_teams: # - admin + +... From c58cc271d3a01ab41e65fc4ca8313051f4068db2 Mon Sep 17 00:00:00 2001 From: w4hf Date: Fri, 18 Oct 2024 18:52:43 +0200 Subject: [PATCH 08/14] create roles README --- roles/controller_rules_validation/README.md | 358 ++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 roles/controller_rules_validation/README.md diff --git a/roles/controller_rules_validation/README.md b/roles/controller_rules_validation/README.md new file mode 100644 index 00000000..b0fd11c7 --- /dev/null +++ b/roles/controller_rules_validation/README.md @@ -0,0 +1,358 @@ +# aap_configuration_extended.controller_rules_validation + +An ansible role which audit the declared controller configuration and validate it against a set of user-defined rules. + +At the end of the role's execution, a structured data list and human readable report are generated containing the detected violations. + +## Requirements + +n/a + +## Role Input Variables + +| Variable Name | Default Value | Required | Description | +| :------------ | :-----------: | :------: | :---------- | +| `controller_rules` | `[]` | yes | The list of rules to enforce on the declared configuration | +| `fail_if_violations_found` | `true` | no | Force the role to fails if if it finds violations | +| `print_rules_violations_data` | `true` | no | Print the detailed violation data before printing the violation messages | +| `audited_objects` | a list of all the controller objet types | no | The objects to be audited. See the roles defaults main.yml file for the complete list | +| `warn_about_audited_types_not_in_rules_objects` | `false` | no | Treat the objects to be audited but are not in any rule as a violation | +| `warn_about_rules_objects_not_in_audited_types` | `false` | no | Treat the objects that are defined in rules are not in the audited objects list as a violation | + +## Role Output Variables + +| Variable Name | Description | +| :------------ | :---------- | +| `rules_violations_data` | a list of dictionaries containing all the found rules violation details | +| `rules_violations_msgs` | a list of all the found rules violation messages | + + +## rules_violations_msgs format + +Each `rules_violations_msgs` list element has the following syntax : + +``` +Rule ID | Object Type | Object Scope | Object Name | Violation message related to this specific object" +``` + +Example of `rules_violations_msgs` : + +``` +fatal: [localhost]: FAILED! => { + "rules_violations_msgs | unique": [ + "Rule n°1 | organizations | global | Satellite | max_hosts is not set", + "Rule n°2 | organizations | global | Default | max_hosts is not set", + "Rule n°2 | organizations | global | Default | The EE (Automation Hub Default Execution Environment) is forbidden.", + ] +} +``` + +## rules_violations_data structure + +Each `rules_violations_data` list element contains the following elements : + +| Sub-element Name | Description | +| :------------ | :---------- | +| `msg` | The violation message relative to this object. Same format as in `rules_violations_msgs` | +| `object_name` | Name of the non-compliant object | +| `object_organization` | Name of the organization to which belong the affected object if available. | +| `object_scope` | Scope of the non-compliant object, one of two values : `global` or `organization` | +| `object_type` | Type of the non-compliant object | +| `rule_broken` | The rule not respected by the non-compliant object | +| `rule_id` | Rule name if available otherwise the rule order (index + 1) | +| `rule_index` | Index of rule (starting from 0) | + +Example of `rules_violations_data` : + +``` +"rules_violations_data | unique": [ + { + "msg": "Rule n°1 | organizations | global | Satellite | max_hosts is not set", + "object_name": "Satellite", + "object_organization": "__undefined_org__", + "object_scope": "global", + "object_type": "organizations", + "rule_broken": "max_hosts_per_organization", + "rule_id": "n°1", + "rule_index": 0 + }, + ... +``` + +## Role Output Examples + + +## Rules + +The rules should be defined as a list in the variable `controller_rules` + +Each element of the list is a rule that is audited seperately + +There is generic rules fields which are object-type-agnostic and other fields that are applicable to specific object type + +### Generic Rule fields + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `rule_name` | string | A descriptive string set by the user to better identify the rule | +| `organizations` | list | Limit the audit to the objects belonging to the organizations specified in this list. Not applicable to every rule. See the rules below for mor details. | +| `objects` | list | The object types to be audited | +| `exceptions` | dictionary | The specific objects to be discarded from the audit. Applicable only to specific rules. See specific rules details below. | +| `mandatory_fields` | list | The fields that are mandatory. It is a violation if they are empty or undefined. At the moment, `organizations` is ineffective with this rule. | +| `minimum_defined_globally` | integer | The minimum objects count allowed to be defined globally for the objects specified in `objects` | +| `maximum_defined_globally` | integer | The maximum objects count allowed to be defined globally for the objects specified in `objects` | +| `minimum_defined_per_org` | integer | The minimum objects count allowed to be defined in the organizations specified in `organizations` for the objects specified in `objects` | +| `maximum_defined_per_org` | integer | The maximum objects count allowed to be defined in the organizations specified in `organizations` for the objects specified in `objects` | +| `fields_regex` | dictionary | control if the fields of the objects defined in `objects` respect the declared regular expression. The dictionary keys are the fields to be monitored and the values are the corresponding regex. See examples below. | + +#### Generic Rule Examples + +Here's examples of generic rules + +```yaml +controller_rules: + + # ------- Rule n°1 # Generic - Make 'description' a mandatory fields for the listed objects + - objects: + - credentials + - inventories + - inventory_sources + - job_templates + - projects + - teams + - users + - instances + mandatory_fields: + - description # the field 'description' must be defined and non-empty for each of the object types listed in 'objects' + + # ------- Rule n°2 # Generic - Minimum and Maximum + - rule_name: Minimum and Maximum + organizations: + - Satellite + - Default + objects: + - credentials + - groups + - inventories + - inventory_sources + - job_templates + - projects + - teams + - users + - credential_types + - instances + minimum_defined_globally: 3 # each object type listed in 'objects' must be defined at least 3 times in all of AAP + maximum_defined_globally: 5 + minimum_defined_per_org: 2 # each object type listed in 'objects' must be defined at least 2 times in each organization + maximum_defined_per_org: 4 + + # ------- Rule n°3 # Generic - Check if 'projects' and 'credentials' of the 'Default' org respect the regex + - organizations: + - Default + objects: + - projects + - credentials + fields_regex: + name: '^\[Default\].*' # name must start with '[Default]' + description: "^DESC - .*" # description must start with 'DESC - ' + scm_type: ^git$ # accept only git scm + scm_branch: ^main$ # branch naming rule : accept only main branch +``` + +### Object specific Rules + +#### Organizations + +The following rules are specific to organizations and are ineffective for other type of objects. + +The organizations specific rules are compatible with the `exceptions` field + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `max_hosts_per_organization` | integer | The maximum hosts count allowed for the organization | +| `allowed_organization_default_environments` | list | The only possible EEs to be used in the organization | +| `forbidden_organization_default_environments` | integer | The maximum hosts count allowed for the organization | + +##### Organizations specific rules examples : + +```yaml +controller_rules: + + # # ------- Rule n°4 # Organizations - Allow only the following EEs for the organizations 'Satellite' and 'Default' + - objects: + - organizations + organizations: + - Satellite + - Default + max_hosts_per_organization: 100 + allowed_organization_default_environments: + - Automation Hub Default Execution Environment + - Custom EE + + # # ------- Rule n°5 # Organizations - Allow all EEs except the listed forbidden EEs for all organizations except the org 'Satellite' + - objects: + - organizations + max_hosts_per_organization: 10 + forbidden_organization_default_environments: + - Automation Hub Default Execution Environment + exceptions: + organizations: + - Satellite +``` + +#### Inventories + +The following rule is specific to the inventories. However it needs both `controller_hosts` and `controller_inventories` to be defined to work correctly. + +The inventories specific rules are compatible with the `exceptions` field + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `max_hosts_per_inventory` | integer | The maximum hosts count allowed for the organization | + +##### Inventories specific rules examples : + +```yaml +controller_rules: + + # ------- Rule n°6 # inventories - The static hosts maximum count of each inventories of the organization 'Satellite' and 'Default' should not exceed 10 except the 'localhost' inventory + + - organizations: + - Satellite + - Default + objects: + - inventories + max_hosts_per_inventory: 10 # needs 'controller_hosts' and 'controller_inventories' to be defined + exceptions: + inventories: + - localhost +``` + +#### Hosts + +The following rule is specific to hosts. However it needs both `controller_groups` and `controller_hosts` to be defined to work correctly. + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `allow_ungrouped_hosts`| boolean | Set to `true` to flag ungrouped hosts as violations | + +##### Hosts specific rules examples + +```yaml +controller_rules: + + # ------- Rule n°7 # Hosts + - objects: + - hosts + allow_ungrouped_hosts: false # needs 'controller_groups' and 'controller_hosts' to be defined +``` + +#### Credentials + +The following rules are specific to credentials. + +The sensitive data encryption check **will not work** if the credentials transit through intermediary variables, like when the `filetree_read` role is used. + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `encrypt_credentials_sensitive_data` | boolean | Set to `true` to activate sensitive data encryption check | +| `credential_sensitive_data` | dictionary | The credential types and the lists of the sensitive fields to check. The dictionary keys are the credential type to check and the values are the list of the input sub-fields. See the example below. | + +##### Credentials specific rules examples + + +```yaml +controller_rules: + + # ------- Rule n°8 # Credentials + - encrypt_credentials_sensitive_data: true # DO NOT WORK with intermediary variables (filetree_read) + organizations: + - Default + - Satellite + objects: + - credentials + credential_sensitive_data: + Source Control: + - password + Red Hat Virtualization: + - password + Vault: + - vault_password +``` + +#### Users + +The following rules are specific to users. + +The users rules are compatible with the `exceptions` option. + +The `encrypt_user_passwords` option **will not work** if the users transit through intermediary variables, like when the `filetree_read` role is used. + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `allow_superusers`| boolean | Set to `false` to flag superusers as a violation | +| `allow_system_auditors`| boolean | Set to `false` to flag system auditors as a violation | +| `encrypt_user_passwords`| boolean | Set to `true` to flag unvaulted users passwords as a violation | + +##### Users specific rules examples + +```yaml +controller_rules: + + # ------- Rule n°9 # Users - do not allow system auditors or super-admins except the 'controller-admin' and 'admin' users + - objects: + - users + allow_superusers: false + allow_system_auditors: false + encrypt_user_passwords: true + exceptions: + users: + - controller_admin + - admin +``` + +#### Roles + +The following rules are specific to roles. + +| Variable Name | Type | Description | +| :------------ | :------: | :---------- | +| `allowed_roles`| dictionary | The allowed objects roles. Any role not explicitly listed in this dictionary will be flagged as a violation. The dictionary keys are the object types and the values are the list of the allowed roles. See the example below. | +| `forbidden_roles`| dictionary | The forbidden objects roles. The roles listed in this dictionary will be considered as a violation. Any other role not specified in this dictionary is allowed. The dictionary keys are the object types and the values are the list of the forbidden roles. See the example below. | + +##### Roles specific rules examples + +```yaml +controller_rules: + + # ------- Rule n°10 # Roles - Allow ONLY 'read' on projects, 'member' on organizations and 'admin' on teams + - objects: + - roles + allowed_roles: + projects: + - update + - read + organizations: + - member + target_teams: + - member + + # ------- Rule n°11 # Roles - Do not allow 'admin' on projects, 'admin' on organizations and 'admin' on teams, allow everything else. + - objects: + - roles + forbidden_roles: + projects: + - admin + organizations: + - admin + target_teams: + - admin +``` + +## License + +[GPLv3+](https://github.com/ansible/galaxy_collection#licensing) + +## Author + +[Hamza Bouabdallah](https://github.com/w4hf) From 100d3acb791aef47c470c059555cec1dc040697d Mon Sep 17 00:00:00 2001 From: w4hf Date: Fri, 18 Oct 2024 18:58:02 +0200 Subject: [PATCH 09/14] improvements --- roles/controller_rules_validation/README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/roles/controller_rules_validation/README.md b/roles/controller_rules_validation/README.md index b0fd11c7..37d25541 100644 --- a/roles/controller_rules_validation/README.md +++ b/roles/controller_rules_validation/README.md @@ -1,4 +1,4 @@ -# aap_configuration_extended.controller_rules_validation +# controller_rules_validation An ansible role which audit the declared controller configuration and validate it against a set of user-defined rules. @@ -27,7 +27,7 @@ n/a | `rules_violations_msgs` | a list of all the found rules violation messages | -## rules_violations_msgs format +### rules_violations_msgs format Each `rules_violations_msgs` list element has the following syntax : @@ -37,7 +37,7 @@ Rule ID | Object Type | Object Scope | Object Name | Violation message related t Example of `rules_violations_msgs` : -``` +```json fatal: [localhost]: FAILED! => { "rules_violations_msgs | unique": [ "Rule n°1 | organizations | global | Satellite | max_hosts is not set", @@ -47,7 +47,7 @@ fatal: [localhost]: FAILED! => { } ``` -## rules_violations_data structure +### rules_violations_data structure Each `rules_violations_data` list element contains the following elements : @@ -64,7 +64,7 @@ Each `rules_violations_data` list element contains the following elements : Example of `rules_violations_data` : -``` +```json "rules_violations_data | unique": [ { "msg": "Rule n°1 | organizations | global | Satellite | max_hosts is not set", @@ -79,9 +79,6 @@ Example of `rules_violations_data` : ... ``` -## Role Output Examples - - ## Rules The rules should be defined as a list in the variable `controller_rules` @@ -90,7 +87,7 @@ Each element of the list is a rule that is audited seperately There is generic rules fields which are object-type-agnostic and other fields that are applicable to specific object type -### Generic Rule fields +### Generic Rules | Variable Name | Type | Description | | :------------ | :------: | :---------- | From 429af5c2598e4095ca0824e3369eca85615ea1b9 Mon Sep 17 00:00:00 2001 From: w4hf Date: Wed, 23 Oct 2024 17:29:30 +0200 Subject: [PATCH 10/14] align role name with collection scope --- .../README.md | 29 ++++++++++--------- .../defaults/main.yml | 7 +++-- .../tasks/check_credentials_encryption.yml | 2 ++ .../tasks/check_fields_regex.yml | 4 ++- .../tasks/check_hosts.yml | 2 ++ .../tasks/check_inventories.yml | 2 ++ .../tasks/check_mandatory_fields.yml | 6 ++-- .../tasks/check_objects_count.yml | 2 ++ .../tasks/check_organizations.yml | 2 ++ .../tasks/check_roles.yml | 2 ++ .../tasks/check_users.yml | 2 ++ .../tasks/main.yml | 20 +++++++------ .../tasks/rule_check.yml | 2 ++ .../{controller_rules.yml => aap_rules.yml} | 2 +- ...troller_rules_validation_filetree_read.yml | 2 +- 15 files changed, 57 insertions(+), 29 deletions(-) rename roles/{controller_rules_validation => aap_rules_validation}/README.md (93%) rename roles/{controller_rules_validation => aap_rules_validation}/defaults/main.yml (96%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_credentials_encryption.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_fields_regex.yml (93%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_hosts.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_inventories.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_mandatory_fields.yml (94%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_objects_count.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_organizations.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_roles.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/check_users.yml (99%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/main.yml (73%) rename roles/{controller_rules_validation => aap_rules_validation}/tasks/rule_check.yml (99%) rename tests/configs/{controller_rules.yml => aap_rules.yml} (99%) diff --git a/roles/controller_rules_validation/README.md b/roles/aap_rules_validation/README.md similarity index 93% rename from roles/controller_rules_validation/README.md rename to roles/aap_rules_validation/README.md index 37d25541..258528f9 100644 --- a/roles/controller_rules_validation/README.md +++ b/roles/aap_rules_validation/README.md @@ -1,9 +1,11 @@ -# controller_rules_validation +# aap_rules_validation -An ansible role which audit the declared controller configuration and validate it against a set of user-defined rules. +An ansible role which audit the declared AAP configuration and validate it against a set of user-defined rules. At the end of the role's execution, a structured data list and human readable report are generated containing the detected violations. +The actual version of the role supports only the controller component + ## Requirements n/a @@ -12,10 +14,10 @@ n/a | Variable Name | Default Value | Required | Description | | :------------ | :-----------: | :------: | :---------- | -| `controller_rules` | `[]` | yes | The list of rules to enforce on the declared configuration | +| `aap_rules` | `[]` | yes | The list of rules to enforce on the declared configuration | | `fail_if_violations_found` | `true` | no | Force the role to fails if if it finds violations | | `print_rules_violations_data` | `true` | no | Print the detailed violation data before printing the violation messages | -| `audited_objects` | a list of all the controller objet types | no | The objects to be audited. See the roles defaults main.yml file for the complete list | +| `audited_objects` | a list of all the objet types | no | The objects to be audited. See the roles defaults main.yml file for the complete list | | `warn_about_audited_types_not_in_rules_objects` | `false` | no | Treat the objects to be audited but are not in any rule as a violation | | `warn_about_rules_objects_not_in_audited_types` | `false` | no | Treat the objects that are defined in rules are not in the audited objects list as a violation | @@ -41,8 +43,9 @@ Example of `rules_violations_msgs` : fatal: [localhost]: FAILED! => { "rules_violations_msgs | unique": [ "Rule n°1 | organizations | global | Satellite | max_hosts is not set", - "Rule n°2 | organizations | global | Default | max_hosts is not set", "Rule n°2 | organizations | global | Default | The EE (Automation Hub Default Execution Environment) is forbidden.", + "Rule n°3 | projects | Default | Test Project 3 | The value of the field name (Test Project 3) do not respect the regex (^\\[Default\\].*)", + "Rule n°4 | groups | global | group3 | The mandatory field 'description' is not defined" ] } ``` @@ -81,7 +84,7 @@ Example of `rules_violations_data` : ## Rules -The rules should be defined as a list in the variable `controller_rules` +The rules should be defined as a list in the variable `aap_rules` Each element of the list is a rule that is audited seperately @@ -107,7 +110,7 @@ There is generic rules fields which are object-type-agnostic and other fields th Here's examples of generic rules ```yaml -controller_rules: +aap_rules: # ------- Rule n°1 # Generic - Make 'description' a mandatory fields for the listed objects - objects: @@ -173,7 +176,7 @@ The organizations specific rules are compatible with the `exceptions` field ##### Organizations specific rules examples : ```yaml -controller_rules: +aap_rules: # # ------- Rule n°4 # Organizations - Allow only the following EEs for the organizations 'Satellite' and 'Default' - objects: @@ -210,7 +213,7 @@ The inventories specific rules are compatible with the `exceptions` field ##### Inventories specific rules examples : ```yaml -controller_rules: +aap_rules: # ------- Rule n°6 # inventories - The static hosts maximum count of each inventories of the organization 'Satellite' and 'Default' should not exceed 10 except the 'localhost' inventory @@ -236,7 +239,7 @@ The following rule is specific to hosts. However it needs both `controller_group ##### Hosts specific rules examples ```yaml -controller_rules: +aap_rules: # ------- Rule n°7 # Hosts - objects: @@ -259,7 +262,7 @@ The sensitive data encryption check **will not work** if the credentials transit ```yaml -controller_rules: +aap_rules: # ------- Rule n°8 # Credentials - encrypt_credentials_sensitive_data: true # DO NOT WORK with intermediary variables (filetree_read) @@ -294,7 +297,7 @@ The `encrypt_user_passwords` option **will not work** if the users transit throu ##### Users specific rules examples ```yaml -controller_rules: +aap_rules: # ------- Rule n°9 # Users - do not allow system auditors or super-admins except the 'controller-admin' and 'admin' users - objects: @@ -320,7 +323,7 @@ The following rules are specific to roles. ##### Roles specific rules examples ```yaml -controller_rules: +aap_rules: # ------- Rule n°10 # Roles - Allow ONLY 'read' on projects, 'member' on organizations and 'admin' on teams - objects: diff --git a/roles/controller_rules_validation/defaults/main.yml b/roles/aap_rules_validation/defaults/main.yml similarity index 96% rename from roles/controller_rules_validation/defaults/main.yml rename to roles/aap_rules_validation/defaults/main.yml index fa02ad86..d97e6b62 100644 --- a/roles/controller_rules_validation/defaults/main.yml +++ b/roles/aap_rules_validation/defaults/main.yml @@ -1,4 +1,5 @@ -controller_rules: [] +--- +aap_rules: [] fail_if_violations_found: true @@ -142,7 +143,7 @@ __name_field: teams: name users: username workflow_job_templates: name - credential_input_sources: target_credential # optimally it should be "target_credential + '_' + input_field_name" + credential_input_sources: target_credential # optimally it should be "target_credential + '_' + input_field_name" credential_types: name execution_environments: name hosts: name @@ -153,3 +154,5 @@ __name_field: roles: __undefined__ schedules: name settings: __undefined__ + +... diff --git a/roles/controller_rules_validation/tasks/check_credentials_encryption.yml b/roles/aap_rules_validation/tasks/check_credentials_encryption.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_credentials_encryption.yml rename to roles/aap_rules_validation/tasks/check_credentials_encryption.yml index 8f47c80b..eb2071e5 100644 --- a/roles/controller_rules_validation/tasks/check_credentials_encryption.yml +++ b/roles/aap_rules_validation/tasks/check_credentials_encryption.yml @@ -1,3 +1,4 @@ +--- - name: Init sensitive data list ansible.builtin.set_fact: __sensitive_data: [] @@ -52,3 +53,4 @@ loop: "{{ __unencrypted }}" loop_control: loop_var: unencrypted_cred +... diff --git a/roles/controller_rules_validation/tasks/check_fields_regex.yml b/roles/aap_rules_validation/tasks/check_fields_regex.yml similarity index 93% rename from roles/controller_rules_validation/tasks/check_fields_regex.yml rename to roles/aap_rules_validation/tasks/check_fields_regex.yml index 98b2f97c..25ddb1ab 100644 --- a/roles/controller_rules_validation/tasks/check_fields_regex.yml +++ b/roles/aap_rules_validation/tasks/check_fields_regex.yml @@ -1,3 +1,4 @@ +--- - name: Init regex issues list ansible.builtin.set_fact: __regex_issue: [] @@ -28,8 +29,9 @@ msg: "Rule {{ rule_id }} | {{ __object_type }} | {{ __object_scope }} | {{ __object_name }} | The value of the field {{ __field }} ({{ __value }}) do not respect the regex ({{ __regex }})" __object_type: "{{ item[0]['object_type'] }}" __object_scope: "{{ item[1]['organization'] | default(__scope[object_type]) | default('__undefined_org__') }}" - __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fileds + __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fields __field: "{{ item[0]['field_regex'] }}" __value: "{{ item[1][item[0]['field_regex']] }}" __regex: "{{ item[0]['regex_value'] }}" loop: "{{ __regex_issue }}" +... diff --git a/roles/controller_rules_validation/tasks/check_hosts.yml b/roles/aap_rules_validation/tasks/check_hosts.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_hosts.yml rename to roles/aap_rules_validation/tasks/check_hosts.yml index c344d764..70d3c983 100644 --- a/roles/controller_rules_validation/tasks/check_hosts.yml +++ b/roles/aap_rules_validation/tasks/check_hosts.yml @@ -1,3 +1,4 @@ +--- - name: Check for ungrouped hosts when: rule['allow_ungrouped_hosts'] is defined and not rule['allow_ungrouped_hosts'] @@ -34,3 +35,4 @@ when: __ungrouped_hosts is defined and __ungrouped_hosts != None and __ungrouped_hosts | length > 0 +... diff --git a/roles/controller_rules_validation/tasks/check_inventories.yml b/roles/aap_rules_validation/tasks/check_inventories.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_inventories.yml rename to roles/aap_rules_validation/tasks/check_inventories.yml index 9cfaf272..33523546 100644 --- a/roles/controller_rules_validation/tasks/check_inventories.yml +++ b/roles/aap_rules_validation/tasks/check_inventories.yml @@ -1,3 +1,4 @@ +--- - name: Check if inventory hosts count is inferior to maximum allowed ansible.builtin.set_fact: rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" @@ -25,3 +26,4 @@ and rule['max_hosts_per_inventory'] > 0 and inventory['name'] not in (rule['exceptions']['inventories'] | default([])) and controller_hosts | selectattr('inventory', 'equalto', inventory['name']) | length > rule['max_hosts_per_inventory'] +... diff --git a/roles/controller_rules_validation/tasks/check_mandatory_fields.yml b/roles/aap_rules_validation/tasks/check_mandatory_fields.yml similarity index 94% rename from roles/controller_rules_validation/tasks/check_mandatory_fields.yml rename to roles/aap_rules_validation/tasks/check_mandatory_fields.yml index 0e04b8cc..b8b7d486 100644 --- a/roles/controller_rules_validation/tasks/check_mandatory_fields.yml +++ b/roles/aap_rules_validation/tasks/check_mandatory_fields.yml @@ -1,3 +1,4 @@ +--- - name: Init - {{ object_type }} ansible.builtin.set_fact: __defined_none: [] @@ -38,7 +39,7 @@ __field: "{{ item[0]['mandatory_field'] }}" __object_type: "{{ item[0]['object_type'] }}" __object_scope: "{{ item[1]['organization'] | default(__scope[object_type]) | default('__undefined_org__') }}" - __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fileds + __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fileds __object_org: "{{ item[1]['organization'] | default('__undefined_org__') }}" loop: "{{ __defined_empty + __defined_none }}" @@ -69,6 +70,7 @@ __field: "{{ item[0]['mandatory_field'] }}" __object_type: "{{ item[0]['object_type'] }}" __object_scope: "{{ item[1]['organization'] | default(__scope[object_type]) | default('__undefined_org__') }}" - __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fields + __object_name: "{{ item[1][__name_field[object_type]] | default(item[1]['user']) | default('__undefined_name__') }}" # TO BE CHECKED FOR ALTERNATIVE 'name' fields __object_org: "{{ item[1]['organization'] | default('__undefined_org__') }}" loop: "{{ __undefined }}" +... diff --git a/roles/controller_rules_validation/tasks/check_objects_count.yml b/roles/aap_rules_validation/tasks/check_objects_count.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_objects_count.yml rename to roles/aap_rules_validation/tasks/check_objects_count.yml index 45dcb379..1f27cfd7 100644 --- a/roles/controller_rules_validation/tasks/check_objects_count.yml +++ b/roles/aap_rules_validation/tasks/check_objects_count.yml @@ -1,3 +1,4 @@ +--- - name: Global objects minimum count check ansible.builtin.set_fact: rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" @@ -87,3 +88,4 @@ loop: "{{ rule['organizations'] }}" loop_control: loop_var: org +... diff --git a/roles/controller_rules_validation/tasks/check_organizations.yml b/roles/aap_rules_validation/tasks/check_organizations.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_organizations.yml rename to roles/aap_rules_validation/tasks/check_organizations.yml index 1112a931..69275e1f 100644 --- a/roles/controller_rules_validation/tasks/check_organizations.yml +++ b/roles/aap_rules_validation/tasks/check_organizations.yml @@ -1,3 +1,4 @@ +--- - name: Check organizations max_hosts when defined ansible.builtin.set_fact: rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" @@ -86,3 +87,4 @@ and org['default_environment'] is defined and rule['forbidden_organization_default_environments'] is defined and org['default_environment'] in rule['forbidden_organization_default_environments'] +... diff --git a/roles/controller_rules_validation/tasks/check_roles.yml b/roles/aap_rules_validation/tasks/check_roles.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_roles.yml rename to roles/aap_rules_validation/tasks/check_roles.yml index 8a8ba075..a3787990 100644 --- a/roles/controller_rules_validation/tasks/check_roles.yml +++ b/roles/aap_rules_validation/tasks/check_roles.yml @@ -1,3 +1,4 @@ +--- - name: Init variables ansible.builtin.set_fact: __forbidden_roles: [] @@ -128,3 +129,4 @@ vars: msg: "Rule {{ rule_id }} | roles | global | __undefined_org__ | The role '{{ item['role'] }}' on '{{ item['object'] }}' is not allowed." loop: "{{ unallowed_roles }}" +... diff --git a/roles/controller_rules_validation/tasks/check_users.yml b/roles/aap_rules_validation/tasks/check_users.yml similarity index 99% rename from roles/controller_rules_validation/tasks/check_users.yml rename to roles/aap_rules_validation/tasks/check_users.yml index cc3f7e9b..786e66e5 100644 --- a/roles/controller_rules_validation/tasks/check_users.yml +++ b/roles/aap_rules_validation/tasks/check_users.yml @@ -1,3 +1,4 @@ +--- - name: Update rules violation regarding users passwords encryption ansible.builtin.set_fact: rules_violations_msgs: "{{ (rules_violations_msgs + [msg]) }}" @@ -75,3 +76,4 @@ loop: "{{ controller_user_accounts | selectattr('is_system_auditor', 'defined') | rejectattr('is_system_auditor', 'false') }}" loop_control: loop_var: __system_auditors +... diff --git a/roles/controller_rules_validation/tasks/main.yml b/roles/aap_rules_validation/tasks/main.yml similarity index 73% rename from roles/controller_rules_validation/tasks/main.yml rename to roles/aap_rules_validation/tasks/main.yml index 95ad7b88..4f6ce677 100644 --- a/roles/controller_rules_validation/tasks/main.yml +++ b/roles/aap_rules_validation/tasks/main.yml @@ -1,5 +1,6 @@ +--- - name: Look for rules violations - when: controller_rules is defined and controller_rules != None and controller_rules | length > 0 + when: aap_rules is defined and aap_rules != None and aap_rules | length > 0 block: - name: Init rules violations ansible.builtin.set_fact: @@ -10,7 +11,7 @@ - name: Rules check loop ansible.builtin.include_tasks: file: tasks/rule_check.yml - loop: "{{ controller_rules }}" + loop: "{{ aap_rules }}" when: rule['objects'] is defined and rule['objects'] != None and rule['objects'] | length > 0 loop_control: loop_var: rule @@ -21,23 +22,23 @@ block: - name: Get object types declared rules ansible.builtin.set_fact: - __objects_in_rules: "{{ controller_rules | map(attribute='objects') | flatten | unique }}" + __objects_in_rules: "{{ aap_rules | map(attribute='objects') | flatten | unique }}" - name: Object types in rules that are not part of audited object types ansible.builtin.set_fact: rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" vars: - diff: "{{ __objects_in_rules | difference(audited_objects) }}" - msg: "The object type(s) '{{ diff | join(',') }}' are mentioned in rules but are not audited." - when: warn_about_audited_types_not_in_rules_objects and diff is defined and diff | length > 0 + __diff: "{{ __objects_in_rules | difference(audited_objects) }}" + msg: "The object type(s) '{{ __diff | join(',') }}' are mentioned in rules but are not audited." + when: warn_about_audited_types_not_in_rules_objects and __diff is defined and __diff | length > 0 - name: Audited object types that are not mentioned in any rule ansible.builtin.set_fact: rules_violations_msgs: "{{ rules_violations_msgs + [msg] }}" vars: - diff: "{{ audited_objects | difference(__objects_in_rules) }}" - msg: "The object types(s) '{{ diff | join(',') }}' are audited but are not mentioned in any rule." - when: warn_about_rules_objects_not_in_audited_types and diff is defined and diff | length > 0 + __diff: "{{ audited_objects | difference(__objects_in_rules) }}" + msg: "The object types(s) '{{ __diff | join(',') }}' are audited but are not mentioned in any rule." + when: warn_about_rules_objects_not_in_audited_types and __diff is defined and __diff | length > 0 - name: Print rules violations data ansible.builtin.debug: @@ -53,3 +54,4 @@ ansible.builtin.debug: msg: "Success. No rules violations detected." when: rules_violations_msgs | length == 0 +... diff --git a/roles/controller_rules_validation/tasks/rule_check.yml b/roles/aap_rules_validation/tasks/rule_check.yml similarity index 99% rename from roles/controller_rules_validation/tasks/rule_check.yml rename to roles/aap_rules_validation/tasks/rule_check.yml index 7892c479..a1902527 100644 --- a/roles/controller_rules_validation/tasks/rule_check.yml +++ b/roles/aap_rules_validation/tasks/rule_check.yml @@ -1,3 +1,4 @@ +--- - name: Set rule ID ansible.builtin.set_fact: rule_id: "{{ rule['rule_name'] | default('n°' + (rule_index + 1) | string) }}" @@ -101,3 +102,4 @@ or (rule['forbidden_roles'] is defined and rule['forbidden_roles'] | length > 0 ) ) +... diff --git a/tests/configs/controller_rules.yml b/tests/configs/aap_rules.yml similarity index 99% rename from tests/configs/controller_rules.yml rename to tests/configs/aap_rules.yml index 4fdad9a0..e08759b8 100644 --- a/tests/configs/controller_rules.yml +++ b/tests/configs/aap_rules.yml @@ -1,5 +1,5 @@ --- -controller_rules: +aap_rules: # ------- Rule n°1 # Generic - Minimum and Maximum - rule_name: Generic Stuff diff --git a/tests/playbooks/controller_rules_validation_filetree_read.yml b/tests/playbooks/controller_rules_validation_filetree_read.yml index ce0bf176..38cc67aa 100644 --- a/tests/playbooks/controller_rules_validation_filetree_read.yml +++ b/tests/playbooks/controller_rules_validation_filetree_read.yml @@ -4,4 +4,4 @@ gather_facts: false roles: - role: infra.aap_controller_configuration_extended.filetree_read - - role: infra.aap_controller_configuration_extended.controller_rules_validation + - role: infra.aap_controller_configuration_extended.aap_rules_validation From 980ea8a3d4b56e441ef635ab4d623bce0b59ad3d Mon Sep 17 00:00:00 2001 From: w4hf Date: Wed, 30 Oct 2024 17:13:43 +0100 Subject: [PATCH 11/14] replace deprecated with_dict --- .../aap_rules_validation/tasks/check_credentials_encryption.yml | 2 +- roles/aap_rules_validation/tasks/check_roles.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/aap_rules_validation/tasks/check_credentials_encryption.yml b/roles/aap_rules_validation/tasks/check_credentials_encryption.yml index eb2071e5..08e7a74c 100644 --- a/roles/aap_rules_validation/tasks/check_credentials_encryption.yml +++ b/roles/aap_rules_validation/tasks/check_credentials_encryption.yml @@ -7,7 +7,7 @@ - name: Set sensitive list ansible.builtin.set_fact: __sensitive_data: "{{ __sensitive_data + ([item.key] | product(item.value)) }}" - with_dict: "{{ rule['credential_sensitive_data'] }}" + loop: "{{ rule['credential_sensitive_data'] | dict2items }}" - name: Extract unencrypted credentials of all orgs ansible.builtin.set_fact: diff --git a/roles/aap_rules_validation/tasks/check_roles.yml b/roles/aap_rules_validation/tasks/check_roles.yml index a3787990..e09277ee 100644 --- a/roles/aap_rules_validation/tasks/check_roles.yml +++ b/roles/aap_rules_validation/tasks/check_roles.yml @@ -16,7 +16,7 @@ - name: Set forbidden roles list ansible.builtin.set_fact: __forbidden_roles: "{{ __forbidden_roles + ([item.key] | product(item.value)) }}" - with_dict: "{{ rule['forbidden_roles'] }}" + loop: "{{ rule['forbidden_roles'] | dict2items }}" - name: Extract declared forbidden roles ansible.builtin.set_fact: From 21c748b1768af1bfdd095768e674b79e413d8cbc Mon Sep 17 00:00:00 2001 From: w4hf Date: Mon, 4 Nov 2024 05:48:59 +0100 Subject: [PATCH 12/14] fix markdown linting issues --- roles/aap_rules_validation/README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/roles/aap_rules_validation/README.md b/roles/aap_rules_validation/README.md index 258528f9..246e6d7a 100644 --- a/roles/aap_rules_validation/README.md +++ b/roles/aap_rules_validation/README.md @@ -1,6 +1,6 @@ # aap_rules_validation -An ansible role which audit the declared AAP configuration and validate it against a set of user-defined rules. +An ansible role which audit the declared AAP configuration and validate it against a set of user-defined rules. At the end of the role's execution, a structured data list and human readable report are generated containing the detected violations. @@ -28,12 +28,11 @@ n/a | `rules_violations_data` | a list of dictionaries containing all the found rules violation details | | `rules_violations_msgs` | a list of all the found rules violation messages | - ### rules_violations_msgs format Each `rules_violations_msgs` list element has the following syntax : -``` +```markdown Rule ID | Object Type | Object Scope | Object Name | Violation message related to this specific object" ``` @@ -107,7 +106,7 @@ There is generic rules fields which are object-type-agnostic and other fields th #### Generic Rule Examples -Here's examples of generic rules +Here's examples of generic rules ```yaml aap_rules: @@ -173,7 +172,7 @@ The organizations specific rules are compatible with the `exceptions` field | `allowed_organization_default_environments` | list | The only possible EEs to be used in the organization | | `forbidden_organization_default_environments` | integer | The maximum hosts count allowed for the organization | -##### Organizations specific rules examples : +##### Organizations specific rules examples ```yaml aap_rules: @@ -210,7 +209,7 @@ The inventories specific rules are compatible with the `exceptions` field | :------------ | :------: | :---------- | | `max_hosts_per_inventory` | integer | The maximum hosts count allowed for the organization | -##### Inventories specific rules examples : +##### Inventories specific rules examples ```yaml aap_rules: @@ -260,7 +259,6 @@ The sensitive data encryption check **will not work** if the credentials transit ##### Credentials specific rules examples - ```yaml aap_rules: From 4ad09472d45b95e6cbe97e29abe3d5bdd87fb612 Mon Sep 17 00:00:00 2001 From: w4hf Date: Mon, 4 Nov 2024 05:49:24 +0100 Subject: [PATCH 13/14] fix new role name --- tests/playbooks/controller_rules_validation_include.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playbooks/controller_rules_validation_include.yml b/tests/playbooks/controller_rules_validation_include.yml index d0246a1e..cbc899c0 100644 --- a/tests/playbooks/controller_rules_validation_include.yml +++ b/tests/playbooks/controller_rules_validation_include.yml @@ -7,5 +7,5 @@ ansible.builtin.include_vars: dir: ../configs/ roles: - - role: infra.aap_controller_configuration_extended.controller_rules_validation + - role: infra.aap_controller_configuration_extended.aap_rules_validation ... From 677c8a497dc36fd41301cd0fd3a8d624ea1214e6 Mon Sep 17 00:00:00 2001 From: w4hf Date: Mon, 4 Nov 2024 06:00:51 +0100 Subject: [PATCH 14/14] improve examples --- roles/aap_rules_validation/README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/roles/aap_rules_validation/README.md b/roles/aap_rules_validation/README.md index 246e6d7a..c3f9f279 100644 --- a/roles/aap_rules_validation/README.md +++ b/roles/aap_rules_validation/README.md @@ -172,7 +172,7 @@ The organizations specific rules are compatible with the `exceptions` field | `allowed_organization_default_environments` | list | The only possible EEs to be used in the organization | | `forbidden_organization_default_environments` | integer | The maximum hosts count allowed for the organization | -##### Organizations specific rules examples +##### Organizations specific rules example 1 : Allow only the specified EEs and deny everything else ```yaml aap_rules: @@ -187,7 +187,11 @@ aap_rules: allowed_organization_default_environments: - Automation Hub Default Execution Environment - Custom EE +``` + +##### Organizations specific rules example 2 : Deny only the specified EEs and allow everything else +```yaml # # ------- Rule n°5 # Organizations - Allow all EEs except the listed forbidden EEs for all organizations except the org 'Satellite' - objects: - organizations @@ -250,13 +254,13 @@ aap_rules: The following rules are specific to credentials. -The sensitive data encryption check **will not work** if the credentials transit through intermediary variables, like when the `filetree_read` role is used. - | Variable Name | Type | Description | | :------------ | :------: | :---------- | | `encrypt_credentials_sensitive_data` | boolean | Set to `true` to activate sensitive data encryption check | | `credential_sensitive_data` | dictionary | The credential types and the lists of the sensitive fields to check. The dictionary keys are the credential type to check and the values are the list of the input sub-fields. See the example below. | +**Important Note**: The sensitive data encryption check **will not work** if the credentials transit through intermediary variables, like when the `filetree_read` role is used. + ##### Credentials specific rules examples ```yaml @@ -284,14 +288,14 @@ The following rules are specific to users. The users rules are compatible with the `exceptions` option. -The `encrypt_user_passwords` option **will not work** if the users transit through intermediary variables, like when the `filetree_read` role is used. - | Variable Name | Type | Description | | :------------ | :------: | :---------- | | `allow_superusers`| boolean | Set to `false` to flag superusers as a violation | | `allow_system_auditors`| boolean | Set to `false` to flag system auditors as a violation | | `encrypt_user_passwords`| boolean | Set to `true` to flag unvaulted users passwords as a violation | +**Important Note**: The `encrypt_user_passwords` option **will not work** if the users transit through intermediary variables, like when the `filetree_read` role is used. + ##### Users specific rules examples ```yaml @@ -318,7 +322,7 @@ The following rules are specific to roles. | `allowed_roles`| dictionary | The allowed objects roles. Any role not explicitly listed in this dictionary will be flagged as a violation. The dictionary keys are the object types and the values are the list of the allowed roles. See the example below. | | `forbidden_roles`| dictionary | The forbidden objects roles. The roles listed in this dictionary will be considered as a violation. Any other role not specified in this dictionary is allowed. The dictionary keys are the object types and the values are the list of the forbidden roles. See the example below. | -##### Roles specific rules examples +##### Roles specific rules example 1 : Allow only the specified roles. Deny anything else ```yaml aap_rules: @@ -334,7 +338,11 @@ aap_rules: - member target_teams: - member +``` +##### Roles specific rules example 2 : Deny only the specified roles. Allow everything else + +```yaml # ------- Rule n°11 # Roles - Do not allow 'admin' on projects, 'admin' on organizations and 'admin' on teams, allow everything else. - objects: - roles