Compare commits

...

1 commit

Author SHA1 Message Date
irl
58fd3ec476 lint: ansible-lint suggested updates
Some checks failed
Ansible Lint Check / lint (push) Has been cancelled
2025-11-01 15:09:19 +00:00
20 changed files with 139 additions and 56 deletions

11
.ansible-lint.yml Normal file
View file

@ -0,0 +1,11 @@
---
exclude_paths:
- .ansible/
- .cache/
- .forgejo/
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
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.general: "*"
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
become: true # Required by FreeIPA roles
vars:
@ -31,12 +32,13 @@
rhel9cis_rule_5_4_2_6: false
rhel9cis_rule_5_4_3_3: false
roles:
- name: sr2c.core.baseline
- role: sr2c.core.baseline
tags: bootstrap
- name: sr2c.core.freeipa
- role: sr2c.core.freeipa
tags: freeipa
- hosts:
- name: Deploy and update the Keycloak server
hosts:
- keycloak
become: true
vars:
@ -50,10 +52,10 @@
rhel9cis_allow_authselect_updates: false
podman_host_rootless_users: ["identity"]
roles:
- name: sr2c.core.baseline
- role: sr2c.core.baseline
tags: bootstrap
- name: freeipa.ansible_freeipa.ipaclient
- role: freeipa.ansible_freeipa.ipaclient
state: present
tags: bootstrap
- name: sr2c.core.podman_keycloak
- role: sr2c.core.podman_keycloak
tags: keycloak

View file

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

View file

@ -20,7 +20,7 @@
community.general.parted:
device: "{{ baseline_second_disk_device }}"
number: 1
flags: [ lvm ]
flags: [lvm]
state: present
part_start: "0%"
part_end: "100%"
@ -138,6 +138,8 @@
- name: Disk Partitions | PATCH | Enter emergency mode
ansible.builtin.command:
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
ansible.posix.mount:
@ -158,3 +160,5 @@
- name: Disk Partitions | PATCH | Restore default mode
ansible.builtin.command:
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:
cmd: 'mv {{ baseline_second_disk_migrate_path }} {{ baseline_second_disk_migrate_path }}.old'
@ -12,7 +12,7 @@
state: mounted
# 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:
path: '{{ baseline_second_disk_migrate_path }}'
owner: root
@ -20,12 +20,12 @@
mode: '0755'
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:
cmd: 'cp -ax * {{ baseline_second_disk_migrate_path }}/'
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:
path: '{{ baseline_second_disk_migrate_path }}.old'
state: absent

View file

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

View file

@ -1,15 +1,10 @@
---
- name: Lockdown | AUDIT | Check current authselect configuration
command: authselect current
ansible.builtin.command: authselect current
register: baseline_lockdown_authselect_status
failed_when: false # Exit code is 2 when not configured
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)
ansible.builtin.include_role:
name: RHEL9-CIS
@ -19,11 +14,12 @@
rhel9cis_rule_1_7_4: false
# Don't restrict user SSH access in sshd_config - this is managed by FreeIPA
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
rhel9cis_set_boot_pass: false
# TODO: We intend to later deploy a remote rsyslog sink
rhel9cis_syslog: rsyslog
rhel9cis_time_synchronization_servers: "{{ baseline_ntp_servers }}"
rhel9cis_warning_banner: "{{ baseline_warning_banner }}"
rhel9cis_sshd_denyusers: "admin nobody"
when: (ansible_distribution == "Rocky") and (ansible_distribution_major_version == "9")

View file

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

View file

@ -19,14 +19,14 @@
- name: SolusVM Guest | AUDIT | Check for tuned profile
ansible.builtin.command: tuned-adm active
register: vps_tuned_profile
register: _baseline_solusvm_tuned_profile
become: true
changed_when: false
- 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
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
ansible.builtin.replace:

View file

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

View file

@ -11,11 +11,11 @@
- /root/isrgrootx1.pem
- /root/isrg-root-x2.pem
ipaserver_dirsrv_cert_name: "{{ ansible_inventory }}"
ipaserver_dirsrv_cert_files: [ "/root/server.p12" ]
ipaserver_dirsrv_cert_files: ["/root/server.p12"]
ipaserver_dirsrv_pin: ""
ipaserver_firewalld_zone: public
ipaserver_http_cert_name: "{{ ansible_inventory }}"
ipaserver_http_cert_files: [ "/root/server.p12" ]
ipaserver_http_cert_files: ["/root/server.p12"]
ipaserver_http_pin: ""
ipaserver_no_hbac_allow: true
ipaserver_no_pkinit: true
@ -30,22 +30,22 @@
- /root/isrgrootx1.pem
- /root/isrg-root-x2.pem
ipareplica_dirsrv_cert_name: "{{ ansible_inventory }}"
ipareplica_dirsrv_cert_files: [ "/root/server.p12" ]
ipareplica_dirsrv_cert_files: ["/root/server.p12"]
ipareplica_dirsrv_pin: ""
ipareplica_firewalld_zone: public
ipareplica_http_cert_name: "{{ ansible_inventory }}"
ipareplica_http_cert_files: [ "/root/server.p12" ]
ipareplica_http_cert_files: ["/root/server.p12"]
ipareplica_http_pin: ""
ipareplica_no_pkinit: true
ipareplica_setup_dns: false
- name: FreeIPA | AUDIT | Check current authselect configuration
command: authselect current
ansible.builtin.command: authselect current
register: freeipa_authselect_status
changed_when: false
- 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: >
'Profile ID: sssd' 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
regexp: '^{{ _podman_host_rootless_user }}:.*$'
state: absent
register: uid_line_found
check_mode: yes
register: _podman_host_uid_line_found
check_mode: true
failed_when: false
changed_when: false
@ -25,17 +25,17 @@
path: /etc/subgid
regexp: '^{{ _podman_host_rootless_user_group.ansible_facts.getent_group | first }}:.*$'
state: absent
register: gid_line_found
check_mode: yes
register: _podman_host_gid_line_found
check_mode: true
failed_when: false
changed_when: false
- name: Podman Host | AUDIT | Assert that user is in subuid file exactly once
ansible.builtin.assert:
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
ansible.builtin.assert:
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:
seconds: 30
- name: create ldap suffix
- name: Podman Keycloak | PATCH | Create ldap suffix
containers.podman.podman_container_exec:
name: ldap
argv:
@ -25,13 +25,13 @@
tags:
- ldap
- name: create suffix result (only when changed)
debug:
- name: Podman Keycloak | AUDIT | Create suffix result (only when changed)
ansible.builtin.debug:
msg: "Suffix was created"
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:
dn: "ou={{ item }},{{ podman_keycloak_ldap_database_suffix_dn }}"
objectClass:
@ -46,10 +46,10 @@
- People
- Groups
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
- name: enable memberOf plugin
- name: Podman Keycloak | PATCH | Enable memberOf plugin
containers.podman.podman_container_exec:
name: ldap
argv:
@ -65,7 +65,7 @@
tags:
- ldap
- name: disable anonymous bind
- name: Podman Keycloak | PATCH | Disable anonymous bind
containers.podman.podman_container_exec:
name: ldap
argv:
@ -81,7 +81,7 @@
tags:
- ldap
- name: ldap read-only administrator
- name: Podman Keycloak | PATCH | Create a read-only administrator
community.general.ldap_entry:
dn: "uid=admin,ou=Administrators,{{ podman_keycloak_ldap_database_suffix_dn }}"
objectClass:
@ -98,10 +98,10 @@
bind_pw: "{{ podman_keycloak_ldap_directory_manager_password }}"
delegate_to: localhost
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
- name: ldap access control information
- name: Podman Keycloak | PATCH | Apply LDAP permissions
community.general.ldap_attrs:
dn: "{{ podman_keycloak_ldap_database_suffix_dn }}"
attributes:
@ -111,5 +111,5 @@
bind_pw: "{{ podman_keycloak_ldap_directory_manager_password }}"
delegate_to: localhost
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