lint: ansible-lint suggested updates

This commit is contained in:
Iain Learmonth 2025-11-01 15:07:36 +00:00
parent 2ba6c6691b
commit 1d5d39d1c2
20 changed files with 140 additions and 56 deletions

12
.ansible-lint.yml Normal file
View file

@ -0,0 +1,12 @@
---
exclude_paths:
- .ansible/
- .cache/
- .forgejo/
- venv/
skip_list:
- package-latest # it would be lovely to pin versions and test before deployment but lack resources
- no-changed-when # TODO: remove this eventually, needs more thinking case by case
- risky-file-permissions # TODO: remove this eventually, needs more thinking case by case
- yaml[line-length] # TODO: remove this eventually, whitespace changes can cause unforseen problems
- galaxy[no-changelog] # TODO: remove this once we tag a release

View file

@ -0,0 +1,41 @@
---
name: Ansible Lint Check
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: docker
container:
image: ghcr.io/catthehacker/ubuntu:runner-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Create a virtual environment
run: |
python -m venv venv
- name: Install Ansible and ansible-dev-tools
run: |
source venv/bin/activate
pip install --upgrade pip
pip install ansible ansible-dev-tools
shell: bash
- name: Run ansible-lint
run: |
source venv/bin/activate
ansible-lint
shell: bash

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.ansible
.cache

View file

@ -12,3 +12,23 @@ roles:
- src: git+https://github.com/ansible-lockdown/RHEL9-CIS.git - src: git+https://github.com/ansible-lockdown/RHEL9-CIS.git
version: "2.0.0" version: "2.0.0"
``` ```
## Licence
Copyright © SR2 Communications Limited 2021-2025.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -16,3 +16,7 @@ dependencies:
community.crypto: "*" community.crypto: "*"
community.general: "*" community.general: "*"
freeipa.ansible_freeipa: "1.15.1" freeipa.ansible_freeipa: "1.15.1"
tags:
- linux
- infrastructure
- security

2
meta/runtime.yml Normal file
View file

@ -0,0 +1,2 @@
---
requires_ansible: ">=2.15.0"

View file

@ -1,5 +1,6 @@
--- ---
- hosts: - name: Deploy and update the FreeIPA servers
hosts:
- ipaservers - ipaservers
become: true # Required by FreeIPA roles become: true # Required by FreeIPA roles
vars: vars:
@ -31,12 +32,13 @@
rhel9cis_rule_5_4_2_6: false rhel9cis_rule_5_4_2_6: false
rhel9cis_rule_5_4_3_3: false rhel9cis_rule_5_4_3_3: false
roles: roles:
- name: sr2c.core.baseline - role: sr2c.core.baseline
tags: bootstrap tags: bootstrap
- name: sr2c.core.freeipa - role: sr2c.core.freeipa
tags: freeipa tags: freeipa
- hosts: - name: Deploy and update the Keycloak server
hosts:
- keycloak - keycloak
become: true become: true
vars: vars:
@ -50,10 +52,10 @@
rhel9cis_allow_authselect_updates: false rhel9cis_allow_authselect_updates: false
podman_host_rootless_users: ["identity"] podman_host_rootless_users: ["identity"]
roles: roles:
- name: sr2c.core.baseline - role: sr2c.core.baseline
tags: bootstrap tags: bootstrap
- name: freeipa.ansible_freeipa.ipaclient - role: freeipa.ansible_freeipa.ipaclient
state: present state: present
tags: bootstrap tags: bootstrap
- name: sr2c.core.podman_keycloak - role: sr2c.core.podman_keycloak
tags: keycloak tags: keycloak

View file

@ -4,6 +4,7 @@
cmd: grub2-mkconfig -o /boot/grub2/grub.cfg cmd: grub2-mkconfig -o /boot/grub2/grub.cfg
- name: Restart systemd-resolved - name: Restart systemd-resolved
service: ansible.builtin.systemd_service:
name: systemd-resolved name: systemd-resolved
state: restarted state: restarted
daemon_reload: true

View file

@ -20,7 +20,7 @@
community.general.parted: community.general.parted:
device: "{{ baseline_second_disk_device }}" device: "{{ baseline_second_disk_device }}"
number: 1 number: 1
flags: [ lvm ] flags: [lvm]
state: present state: present
part_start: "0%" part_start: "0%"
part_end: "100%" part_end: "100%"
@ -138,6 +138,8 @@
- name: Disk Partitions | PATCH | Enter emergency mode - name: Disk Partitions | PATCH | Enter emergency mode
ansible.builtin.command: ansible.builtin.command:
cmd: systemctl isolate emergency.target cmd: systemctl isolate emergency.target
tags:
- skip_ansible_lint # Not possible with ansible.builtin.systemd_service
- name: Disk Partitions | PATCH | Unmount /var/lib/nfs/rpc_pipefs if mounted - name: Disk Partitions | PATCH | Unmount /var/lib/nfs/rpc_pipefs if mounted
ansible.posix.mount: ansible.posix.mount:
@ -158,3 +160,5 @@
- name: Disk Partitions | PATCH | Restore default mode - name: Disk Partitions | PATCH | Restore default mode
ansible.builtin.command: ansible.builtin.command:
cmd: systemctl isolate default.target cmd: systemctl isolate default.target
tags:
- skip_ansible_lint # Not possible with ansible.builtin.systemd_service

View file

@ -1,5 +1,5 @@
--- ---
- name: 'Disk Partitions | PATCH | Rename {{ baseline_second_disk_migrate_path }} to {{ baseline_second_disk_migrate_path }}.old' - name: 'Disk Partitions | PATCH | Rename directory to directory.old | {{ baseline_second_disk_migrate_path }}'
ansible.builtin.command: ansible.builtin.command:
cmd: 'mv {{ baseline_second_disk_migrate_path }} {{ baseline_second_disk_migrate_path }}.old' cmd: 'mv {{ baseline_second_disk_migrate_path }} {{ baseline_second_disk_migrate_path }}.old'
@ -12,7 +12,7 @@
state: mounted state: mounted
# TODO: systemctl daemon-reload after modifying /etc/fstab # TODO: systemctl daemon-reload after modifying /etc/fstab
- name: 'Disk Partitions | PATCH | Set {{ baseline_second_disk_migrate_path }} permissions' - name: 'Disk Partitions | PATCH | Set permissions | {{ baseline_second_disk_migrate_path }}'
ansible.builtin.file: ansible.builtin.file:
path: '{{ baseline_second_disk_migrate_path }}' path: '{{ baseline_second_disk_migrate_path }}'
owner: root owner: root
@ -20,12 +20,12 @@
mode: '0755' mode: '0755'
state: directory state: directory
- name: 'Disk Partitions | PATCH | Move {{ baseline_second_disk_migrate_path }} content' - name: 'Disk Partitions | PATCH | Move content | {{ baseline_second_disk_migrate_path }}'
ansible.builtin.shell: ansible.builtin.shell:
cmd: 'cp -ax * {{ baseline_second_disk_migrate_path }}/' cmd: 'cp -ax * {{ baseline_second_disk_migrate_path }}/'
chdir: '{{ baseline_second_disk_migrate_path }}.old' chdir: '{{ baseline_second_disk_migrate_path }}.old'
- name: 'Disk Partitions | PATCH | Delete {{ baseline_second_disk_migrate_path }}.old' - name: 'Disk Partitions | PATCH | Delete directory.old | {{ baseline_second_disk_migrate_path }}'
ansible.builtin.file: ansible.builtin.file:
path: '{{ baseline_second_disk_migrate_path }}.old' path: '{{ baseline_second_disk_migrate_path }}.old'
state: absent state: absent

View file

@ -7,15 +7,15 @@
- name: FreeIPA Client | AUDIT | Check current authselect configuration - name: FreeIPA Client | AUDIT | Check current authselect configuration
ansible.builtin.command: authselect current ansible.builtin.command: authselect current
register: freeipa_authselect_status register: _baseline_freeipa_authselect_status
changed_when: false changed_when: false
- name: FreeIPA Client | PATCH | Apply authselect profile with sssd, sudo, and mkhomedir if not set - name: FreeIPA Client | PATCH | Apply authselect profile with sssd, sudo, and mkhomedir if not set
ansible.builtin.command: authselect select sssd with-sudo with-mkhomedir --force ansible.builtin.command: authselect select sssd with-sudo with-mkhomedir --force
when: > when: >
'Profile ID: sssd' not in freeipa_authselect_status.stdout or 'Profile ID: sssd' not in _baseline_freeipa_authselect_status.stdout or
'with-sudo' not in freeipa_authselect_status.stdout or 'with-sudo' not in _baseline_freeipa_authselect_status.stdout or
'with-mkhomedir' not in freeipa_authselect_status.stdout 'with-mkhomedir' not in _baseline_freeipa_authselect_status.stdout
- name: FreeIPA Client | PATCH | Enable oddjobd.service (for with-mkhomedir feature) - name: FreeIPA Client | PATCH | Enable oddjobd.service (for with-mkhomedir feature)
ansible.builtin.systemd_service: ansible.builtin.systemd_service:

View file

@ -1,15 +1,10 @@
--- ---
- name: Lockdown | AUDIT | Check current authselect configuration - name: Lockdown | AUDIT | Check current authselect configuration
command: authselect current ansible.builtin.command: authselect current
register: baseline_lockdown_authselect_status register: baseline_lockdown_authselect_status
failed_when: false # Exit code is 2 when not configured failed_when: false # Exit code is 2 when not configured
changed_when: false changed_when: false
- name: Lockdown | AUDIT | Do not disable root login if no authselect profile configured
ansible.builtin.set_fact:
rhel9cis_rule_5_1_20: false
when: baseline_lockdown_authselect_status.rc == 2
- name: Lockdown | PATCH | Run Ansible Lockdown (RHEL9-CIS) - name: Lockdown | PATCH | Run Ansible Lockdown (RHEL9-CIS)
ansible.builtin.include_role: ansible.builtin.include_role:
name: RHEL9-CIS name: RHEL9-CIS
@ -19,11 +14,12 @@
rhel9cis_rule_1_7_4: false rhel9cis_rule_1_7_4: false
# Don't restrict user SSH access in sshd_config - this is managed by FreeIPA # Don't restrict user SSH access in sshd_config - this is managed by FreeIPA
rhel9cis_rule_5_1_7: false rhel9cis_rule_5_1_7: false
# Only disable root login once authselect is configured
rhel9cis_rule_5_1_20: "{{ baseline_lockdown_authselect_status.rc != 2 }}"
# TODO: figure out boot password # TODO: figure out boot password
rhel9cis_set_boot_pass: false rhel9cis_set_boot_pass: false
# TODO: We intend to later deploy a remote rsyslog sink # TODO: We intend to later deploy a remote rsyslog sink
rhel9cis_syslog: rsyslog rhel9cis_syslog: rsyslog
rhel9cis_time_synchronization_servers: "{{ baseline_ntp_servers }}" rhel9cis_time_synchronization_servers: "{{ baseline_ntp_servers }}"
rhel9cis_warning_banner: "{{ baseline_warning_banner }}" rhel9cis_warning_banner: "{{ baseline_warning_banner }}"
rhel9cis_sshd_denyusers: "admin nobody"
when: (ansible_distribution == "Rocky") and (ansible_distribution_major_version == "9") when: (ansible_distribution == "Rocky") and (ansible_distribution_major_version == "9")

View file

@ -20,6 +20,7 @@
when: baseline_second_disk_device is defined when: baseline_second_disk_device is defined
- name: Baseline | PATCH | Enable EPEL repository - name: Baseline | PATCH | Enable EPEL repository
when: (baseline_epel_packages_allowed is defined) and (baseline_epel_packages_allowed | length > 0)
block: block:
- name: Baseline | PATCH | Install epel-release - name: Baseline | PATCH | Install epel-release
ansible.builtin.dnf: ansible.builtin.dnf:
@ -37,7 +38,6 @@
section: epel-cisco-openh264 section: epel-cisco-openh264
option: enabled option: enabled
value: 0 value: 0
when: (baseline_epel_packages_allowed is defined) and (baseline_epel_packages_allowed | length > 0)
- name: Baseline | PATCH | Remove EPEL repository - name: Baseline | PATCH | Remove EPEL repository
ansible.builtin.dnf: ansible.builtin.dnf:

View file

@ -19,14 +19,14 @@
- name: SolusVM Guest | AUDIT | Check for tuned profile - name: SolusVM Guest | AUDIT | Check for tuned profile
ansible.builtin.command: tuned-adm active ansible.builtin.command: tuned-adm active
register: vps_tuned_profile register: _baseline_solusvm_tuned_profile
become: true become: true
changed_when: false changed_when: false
- name: SolusVM Guest | PATCH | Start tuned profile (virtual-guest) - name: SolusVM Guest | PATCH | Start tuned profile (virtual-guest)
ansible.builtin.shell: tuned-adm profile virtual-guest ansible.builtin.command: tuned-adm profile virtual-guest
become: true become: true
when: "'virtual-guest' not in vps_tuned_profile.stdout" when: "'virtual-guest' not in _baseline_solusvm_tuned_profile.stdout"
- name: SolusVM Guest | PATCH | Remove console=ttyS0,115200n8 from bootloader configurations - name: SolusVM Guest | PATCH | Remove console=ttyS0,115200n8 from bootloader configurations
ansible.builtin.replace: ansible.builtin.replace:

View file

@ -17,7 +17,7 @@
when: freeipa_certs_existing_cert.not_after is defined when: freeipa_certs_existing_cert.not_after is defined
- name: "FreeIPA Certificates | AUDIT | Print days until expiry" - name: "FreeIPA Certificates | AUDIT | Print days until expiry"
debug: ansible.builtin.debug:
msg: "{{ freeipa_certs_days_until_expiry }}" msg: "{{ freeipa_certs_days_until_expiry }}"
when: freeipa_certs_existing_cert.not_after is defined when: freeipa_certs_existing_cert.not_after is defined

View file

@ -11,11 +11,11 @@
- /root/isrgrootx1.pem - /root/isrgrootx1.pem
- /root/isrg-root-x2.pem - /root/isrg-root-x2.pem
ipaserver_dirsrv_cert_name: "{{ ansible_inventory }}" ipaserver_dirsrv_cert_name: "{{ ansible_inventory }}"
ipaserver_dirsrv_cert_files: [ "/root/server.p12" ] ipaserver_dirsrv_cert_files: ["/root/server.p12"]
ipaserver_dirsrv_pin: "" ipaserver_dirsrv_pin: ""
ipaserver_firewalld_zone: public ipaserver_firewalld_zone: public
ipaserver_http_cert_name: "{{ ansible_inventory }}" ipaserver_http_cert_name: "{{ ansible_inventory }}"
ipaserver_http_cert_files: [ "/root/server.p12" ] ipaserver_http_cert_files: ["/root/server.p12"]
ipaserver_http_pin: "" ipaserver_http_pin: ""
ipaserver_no_hbac_allow: true ipaserver_no_hbac_allow: true
ipaserver_no_pkinit: true ipaserver_no_pkinit: true
@ -30,22 +30,22 @@
- /root/isrgrootx1.pem - /root/isrgrootx1.pem
- /root/isrg-root-x2.pem - /root/isrg-root-x2.pem
ipareplica_dirsrv_cert_name: "{{ ansible_inventory }}" ipareplica_dirsrv_cert_name: "{{ ansible_inventory }}"
ipareplica_dirsrv_cert_files: [ "/root/server.p12" ] ipareplica_dirsrv_cert_files: ["/root/server.p12"]
ipareplica_dirsrv_pin: "" ipareplica_dirsrv_pin: ""
ipareplica_firewalld_zone: public ipareplica_firewalld_zone: public
ipareplica_http_cert_name: "{{ ansible_inventory }}" ipareplica_http_cert_name: "{{ ansible_inventory }}"
ipareplica_http_cert_files: [ "/root/server.p12" ] ipareplica_http_cert_files: ["/root/server.p12"]
ipareplica_http_pin: "" ipareplica_http_pin: ""
ipareplica_no_pkinit: true ipareplica_no_pkinit: true
ipareplica_setup_dns: false ipareplica_setup_dns: false
- name: FreeIPA | AUDIT | Check current authselect configuration - name: FreeIPA | AUDIT | Check current authselect configuration
command: authselect current ansible.builtin.command: authselect current
register: freeipa_authselect_status register: freeipa_authselect_status
changed_when: false changed_when: false
- name: FreeIPA | PATCH | Apply authselect profile with sssd, sudo, and mkhomedir if not set - name: FreeIPA | PATCH | Apply authselect profile with sssd, sudo, and mkhomedir if not set
command: authselect select sssd with-sudo with-mkhomedir ansible.builtin.command: authselect select sssd with-sudo with-mkhomedir
when: > when: >
'Profile ID: sssd' not in freeipa_authselect_status.stdout or 'Profile ID: sssd' not in freeipa_authselect_status.stdout or
'with-sudo' not in freeipa_authselect_status.stdout or 'with-sudo' not in freeipa_authselect_status.stdout or

View file

@ -15,8 +15,8 @@
path: /etc/subuid path: /etc/subuid
regexp: '^{{ _podman_host_rootless_user }}:.*$' regexp: '^{{ _podman_host_rootless_user }}:.*$'
state: absent state: absent
register: uid_line_found register: _podman_host_uid_line_found
check_mode: yes check_mode: true
failed_when: false failed_when: false
changed_when: false changed_when: false
@ -25,17 +25,17 @@
path: /etc/subgid path: /etc/subgid
regexp: '^{{ _podman_host_rootless_user_group.ansible_facts.getent_group | first }}:.*$' regexp: '^{{ _podman_host_rootless_user_group.ansible_facts.getent_group | first }}:.*$'
state: absent state: absent
register: gid_line_found register: _podman_host_gid_line_found
check_mode: yes check_mode: true
failed_when: false failed_when: false
changed_when: false changed_when: false
- name: Podman Host | AUDIT | Assert that user is in subuid file exactly once - name: Podman Host | AUDIT | Assert that user is in subuid file exactly once
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- uid_line_found.found == 1 - _podman_host_uid_line_found.found == 1
- name: Podman Host | AUDIT | Assert that group is in subgid file exactly once - name: Podman Host | AUDIT | Assert that group is in subgid file exactly once
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- gid_line_found.found == 1 - _podman_host_gid_line_found.found == 1

View file

@ -1,9 +1,9 @@
--- ---
- name: wait 30 seconds for ldap server to start - name: Podman Keycloak | AUDIT | Wait 30 seconds for ldap server to start
ansible.builtin.pause: ansible.builtin.pause:
seconds: 30 seconds: 30
- name: create ldap suffix - name: Podman Keycloak | PATCH | Create ldap suffix
containers.podman.podman_container_exec: containers.podman.podman_container_exec:
name: ldap name: ldap
argv: argv:
@ -25,13 +25,13 @@
tags: tags:
- ldap - ldap
- name: create suffix result (only when changed) - name: Podman Keycloak | AUDIT | Create suffix result (only when changed)
debug: ansible.builtin.debug:
msg: "Suffix was created" msg: "Suffix was created"
when: not podman_keycloak_create_suffix.failed when: not podman_keycloak_create_suffix.failed
changed_when: not podman_keycloak_create_suffix.failed changed_when: not podman_keycloak_create_suffix.failed
- name: ldap organisational units - name: Podman Keycloak | PATCH | Create OUs
community.general.ldap_entry: community.general.ldap_entry:
dn: "ou={{ item }},{{ podman_keycloak_ldap_database_suffix_dn }}" dn: "ou={{ item }},{{ podman_keycloak_ldap_database_suffix_dn }}"
objectClass: objectClass:
@ -46,10 +46,10 @@
- People - People
- Groups - Groups
environment: environment:
- LDAPTLS_REQCERT: "{% if podman_keycloak_certbot_testing %}never{% else %}always{% endif %}" LDAPTLS_REQCERT: "{% if podman_keycloak_certbot_testing %}never{% else %}always{% endif %}"
tags: ldap tags: ldap
- name: enable memberOf plugin - name: Podman Keycloak | PATCH | Enable memberOf plugin
containers.podman.podman_container_exec: containers.podman.podman_container_exec:
name: ldap name: ldap
argv: argv:
@ -65,7 +65,7 @@
tags: tags:
- ldap - ldap
- name: disable anonymous bind - name: Podman Keycloak | PATCH | Disable anonymous bind
containers.podman.podman_container_exec: containers.podman.podman_container_exec:
name: ldap name: ldap
argv: argv:
@ -81,7 +81,7 @@
tags: tags:
- ldap - ldap
- name: ldap read-only administrator - name: Podman Keycloak | PATCH | Create a read-only administrator
community.general.ldap_entry: community.general.ldap_entry:
dn: "uid=admin,ou=Administrators,{{ podman_keycloak_ldap_database_suffix_dn }}" dn: "uid=admin,ou=Administrators,{{ podman_keycloak_ldap_database_suffix_dn }}"
objectClass: objectClass:
@ -98,10 +98,10 @@
bind_pw: "{{ podman_keycloak_ldap_directory_manager_password }}" bind_pw: "{{ podman_keycloak_ldap_directory_manager_password }}"
delegate_to: localhost delegate_to: localhost
environment: environment:
- LDAPTLS_REQCERT: "{% if podman_keycloak_certbot_testing %}never{% else %}always{% endif %}" LDAPTLS_REQCERT: "{% if podman_keycloak_certbot_testing %}never{% else %}always{% endif %}"
tags: ldap tags: ldap
- name: ldap access control information - name: Podman Keycloak | PATCH | Apply LDAP permissions
community.general.ldap_attrs: community.general.ldap_attrs:
dn: "{{ podman_keycloak_ldap_database_suffix_dn }}" dn: "{{ podman_keycloak_ldap_database_suffix_dn }}"
attributes: attributes:
@ -111,5 +111,5 @@
bind_pw: "{{ podman_keycloak_ldap_directory_manager_password }}" bind_pw: "{{ podman_keycloak_ldap_directory_manager_password }}"
delegate_to: localhost delegate_to: localhost
environment: environment:
- LDAPTLS_REQCERT: "{% if podman_keycloak_certbot_testing %}never{% else %}always{% endif %}" LDAPTLS_REQCERT: "{% if podman_keycloak_certbot_testing %}never{% else %}always{% endif %}"
tags: ldap tags: ldap