diff --git a/README.md b/README.md index 8b26da8..8505c0a 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,10 @@ collections: - src: git+https://guardianproject.dev/sr2/ansible-collection-core.git version: "main" roles: - - src: git+https://guardianproject.dev/sr2/RHEL9-CIS.git - version: "2.0.3-become" + - src: git+https://github.com/ansible-lockdown/RHEL9-CIS.git + version: "2.0.3" ``` -## Convention - -We assume that these roles will be run initially as root, and then as an unprivileged user after initial bootstrap. -Some hardening may only be performed in the second run when we can see that the unprivileged user access is configured -and root access is no longer required. -If anything fails due to permissions when running as an unprivileged user, please report that in our -[issue tracker](https://guardianproject.dev/sr2/ansible-collection-core/issues). - ## Licence Copyright © SR2 Communications Limited 2021-2025. diff --git a/playbooks/core_services.yml b/playbooks/core_services.yml index b579d96..351cb30 100644 --- a/playbooks/core_services.yml +++ b/playbooks/core_services.yml @@ -39,10 +39,3 @@ tags: bootstrap - role: sr2c.core.podman_keycloak tags: keycloak - -- name: Baseline for generic servers (manual or externally managed application deployment) - hosts: - - generic - roles: - - role: sr2c.core.baseline - tags: bootstrap diff --git a/roles/baseline/handlers/main.yml b/roles/baseline/handlers/main.yml index 894bba4..81f84d5 100644 --- a/roles/baseline/handlers/main.yml +++ b/roles/baseline/handlers/main.yml @@ -2,11 +2,9 @@ - name: Regenerate grub config ansible.builtin.command: cmd: grub2-mkconfig -o /boot/grub2/grub.cfg - become: true - name: Restart systemd-resolved ansible.builtin.systemd_service: name: systemd-resolved state: restarted daemon_reload: true - become: true diff --git a/roles/baseline/tasks/disk_partitions.yml b/roles/baseline/tasks/disk_partitions.yml index b1aa9f1..ada52ee 100644 --- a/roles/baseline/tasks/disk_partitions.yml +++ b/roles/baseline/tasks/disk_partitions.yml @@ -12,13 +12,11 @@ msg: "Variable 'baseline_second_disk_device' must be defined." - name: Disk Partitions | PATCH | Ensure lvm2 is installed - become: true ansible.builtin.package: name: lvm2 state: present - name: Disk Partitions | PATCH | Create LVM partition spanning entire disk - become: true community.general.parted: device: "{{ baseline_second_disk_device }}" number: 1 @@ -28,41 +26,35 @@ part_end: "100%" - name: Disk Partitions | PATCH | Create volume group - become: true community.general.lvg: vg: "{{ baseline_second_disk_vg_name }}" pvs: "{{ baseline_second_disk_device }}1" - name: Disk Partitions | PATCH | Create /var logical volume - become: true community.general.lvol: vg: "{{ baseline_second_disk_vg_name }}" lv: var size: "{{ baseline_second_disk_var_size }}" - name: Disk Partitions | PATCH | Create /var/log logical volume - become: true community.general.lvol: vg: "{{ baseline_second_disk_vg_name }}" lv: var_log size: "{{ baseline_second_disk_var_log_size }}" - name: Disk Partitions | PATCH | Create /var/log/audit logical volume - become: true community.general.lvol: vg: "{{ baseline_second_disk_vg_name }}" lv: var_log_audit size: "{{ baseline_second_disk_var_log_audit_size }}" - name: Disk Partitions | PATCH | Create /var/tmp logical volume - become: true community.general.lvol: vg: "{{ baseline_second_disk_vg_name }}" lv: var_tmp size: "{{ baseline_second_disk_var_tmp_size }}" - name: Disk Partitions | PATCH | Create /home logical volume with remaining space - become: true community.general.lvol: vg: "{{ baseline_second_disk_vg_name }}" lv: home @@ -70,13 +62,11 @@ size: "100%FREE" - name: Disk Partitions | PATCH | Ensure cryptsetup is installed - become: true ansible.builtin.package: name: cryptsetup state: present - name: Disk Partitions | PATCH | Encrypt /home with LUKS2 and provided passphrase - become: true community.crypto.luks_device: device: "/dev/{{ baseline_second_disk_vg_name }}/home" state: present @@ -84,7 +74,6 @@ type: luks2 - name: Disk Partitions | PATCH | Open LUKS device - become: true community.crypto.luks_device: device: "/dev/{{ baseline_second_disk_vg_name }}/home" name: home_crypt @@ -92,7 +81,6 @@ passphrase: "{{ baseline_home_luks_passphrase }}" - name: Disk Partitions | PATCH | Add /home logical volume to crypttab - become: true community.general.crypttab: backing_device: /dev/mapper/datavg-home name: home_crypt @@ -100,7 +88,6 @@ state: present - name: Disk Partitions | PATCH | Create xfs filesystems on new partitions - become: true community.general.filesystem: dev: "{{ item }}" fstype: xfs @@ -112,7 +99,6 @@ - /dev/mapper/home_crypt - name: Disk Partitions | AUDIT | Check if /home is mounted - become: true ansible.builtin.command: cmd: mountpoint -q /home register: baseline_second_disk_home_mounted @@ -120,7 +106,6 @@ failed_when: false - name: Disk Partitions | AUDIT | Check if /home is empty - become: true ansible.builtin.command: cmd: ls -A /home register: baseline_second_disk_home_files @@ -128,13 +113,11 @@ changed_when: false - name: Disk Partitions | AUDIT | Fail if /home is not mounted and not empty - become: true ansible.builtin.assert: that: - ((baseline_second_disk_home_files.skipped is defined) and baseline_second_disk_home_files.skipped) or (baseline_second_disk_home_files.stdout == "") - name: Disk Partitions | PATCH | Ensure /home is mounted - become: true ansible.posix.mount: src: "/dev/mapper/home_crypt" path: '/home' @@ -143,7 +126,6 @@ state: mounted - name: Disk Partitions | AUDIT | Check if /var is mounted - become: true ansible.builtin.command: cmd: mountpoint -q /var register: baseline_second_disk_var_mounted @@ -151,7 +133,6 @@ failed_when: false - name: Disk Partitions | PATCH | Migrate content if /var is not mounted - become: true when: baseline_second_disk_var_mounted.rc != 0 block: - name: Disk Partitions | PATCH | Enter emergency mode diff --git a/roles/baseline/tasks/disk_partitions_migrate.yml b/roles/baseline/tasks/disk_partitions_migrate.yml index 157f85f..fec3789 100644 --- a/roles/baseline/tasks/disk_partitions_migrate.yml +++ b/roles/baseline/tasks/disk_partitions_migrate.yml @@ -1,11 +1,9 @@ --- - name: 'Disk Partitions | PATCH | Rename directory to directory.old | {{ baseline_second_disk_migrate_path }}' - become: true ansible.builtin.command: cmd: 'mv {{ baseline_second_disk_migrate_path }} {{ baseline_second_disk_migrate_path }}.old' - name: 'Disk Partitions | PATCH | Mount {{ baseline_second_disk_migrate_path }}' - become: true ansible.posix.mount: src: "/dev/mapper/datavg-{{ baseline_second_disk_migrate_path | replace('/', '', 1) | replace('/', '_') }}" path: '{{ baseline_second_disk_migrate_path }}' @@ -15,7 +13,6 @@ # TODO: systemctl daemon-reload after modifying /etc/fstab - name: 'Disk Partitions | PATCH | Set permissions | {{ baseline_second_disk_migrate_path }}' - become: true ansible.builtin.file: path: '{{ baseline_second_disk_migrate_path }}' owner: root @@ -24,13 +21,11 @@ state: directory - name: 'Disk Partitions | PATCH | Move content | {{ baseline_second_disk_migrate_path }}' - become: true ansible.builtin.shell: cmd: 'cp -ax * {{ baseline_second_disk_migrate_path }}/' chdir: '{{ baseline_second_disk_migrate_path }}.old' - name: 'Disk Partitions | PATCH | Delete directory.old | {{ baseline_second_disk_migrate_path }}' - become: true ansible.builtin.file: path: '{{ baseline_second_disk_migrate_path }}.old' state: absent diff --git a/roles/baseline/tasks/dns_resolver.yml b/roles/baseline/tasks/dns_resolver.yml index 9abb787..591fe01 100644 --- a/roles/baseline/tasks/dns_resolver.yml +++ b/roles/baseline/tasks/dns_resolver.yml @@ -1,12 +1,10 @@ --- - name: DNS Resolver | PATCH | Install systemd-resolved - become: true ansible.builtin.dnf: name: systemd-resolved state: latest - name: DNS Resolver | PATCH | Ensure systemd-resolved is in use - become: true ansible.builtin.systemd_service: name: systemd-resolved state: started @@ -14,14 +12,12 @@ masked: false - name: DNS Resolver | PATCH | Remove loopback address entries containing the hostname from /etc/hosts - become: true ansible.builtin.lineinfile: path: /etc/hosts regexp: '^(127\.0\.0\.1|::1)\s.*{{ inventory_hostname }}' state: absent - name: DNS Resolver | PATCH | Enable DNSSEC and disable unwanted resolved features - become: true ansible.builtin.copy: src: resolved.conf dest: /etc/systemd/resolved.conf @@ -29,9 +25,9 @@ group: root mode: "0644" notify: "Restart systemd-resolved" + become: true - name: DNS Resolver | PATCH | Ensure /etc/systemd/system/systemd-resolved.service.d exists - become: true ansible.builtin.file: path: /etc/systemd/system/systemd-resolved.service.d state: directory @@ -40,7 +36,6 @@ mode: "0755" - name: DNS Resolver | PATCH | Disable resolved record synthesising - become: true ansible.builtin.copy: src: systemd-resolved-override.conf dest: /etc/systemd/system/systemd-resolved.service.d/override.conf @@ -48,3 +43,4 @@ group: root mode: "0644" notify: "Restart systemd-resolved" + become: true diff --git a/roles/baseline/tasks/ipaclient.yml b/roles/baseline/tasks/ipaclient.yml index 8c0c113..e301e96 100644 --- a/roles/baseline/tasks/ipaclient.yml +++ b/roles/baseline/tasks/ipaclient.yml @@ -4,28 +4,21 @@ role: freeipa.ansible_freeipa.ipaclient vars: ipaclient_hostname: "{{ inventory_hostname }}" - when: ansible_user == "root" # We've already joined if we're using an unprivileged user - name: FreeIPA Client | AUDIT | Check current authselect configuration - become: true ansible.builtin.command: authselect current register: _baseline_freeipa_authselect_status changed_when: false -- name: FreeIPA Client | PATCH | Apply authselect profile with sssd, sudo, and more if not set - become: true - ansible.builtin.command: authselect select sssd with-sudo with-mkhomedir with-subid with-faillock with-pwhistory without-nullok --force +- 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 with-subid --force when: > '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 or - 'with-subid' not in _baseline_freeipa_authselect_status.stdout or - 'with-faillock' not in _baseline_freeipa_authselect_status.stdout or - 'with-pwhistory' not in _baseline_freeipa_authselect_status.stdout or - 'without-nullok' not in _baseline_freeipa_authselect_status.stdout + 'with-subid' not in _baseline_freeipa_authselect_status.stdout - name: FreeIPA Client | PATCH | Enable oddjobd.service (for with-mkhomedir feature) - become: true ansible.builtin.systemd_service: name: oddjobd.service state: started diff --git a/roles/baseline/tasks/lockdown.yml b/roles/baseline/tasks/lockdown.yml index 7180720..5ddbc22 100644 --- a/roles/baseline/tasks/lockdown.yml +++ b/roles/baseline/tasks/lockdown.yml @@ -1,14 +1,13 @@ --- - name: Lockdown | AUDIT | Check current authselect configuration - become: true 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 | PATCH | Run Ansible Lockdown (RHEL9-CIS) - ansible.builtin.import_role: - name: RHEL9-CIS # This is the SR2 fork that includes a patch to run all tasks with become: true + ansible.builtin.include_role: + name: RHEL9-CIS vars: # Ensure message of the day is configured properly - we have our own MOTD to apply rhel9cis_rule_1_7_1: false @@ -17,7 +16,6 @@ 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 }}" - rhel9cis_rule_5_4_2_4: false # TODO: temporarily disable requirement for root password # TODO: figure out boot password rhel9cis_set_boot_pass: false # TODO: We intend to later deploy a remote rsyslog sink diff --git a/roles/baseline/tasks/main.yml b/roles/baseline/tasks/main.yml index 5bd517e..977f1a0 100644 --- a/roles/baseline/tasks/main.yml +++ b/roles/baseline/tasks/main.yml @@ -23,19 +23,16 @@ when: (baseline_epel_packages_allowed is defined) and (baseline_epel_packages_allowed | length > 0) block: - name: Baseline | PATCH | Install epel-release - become: true ansible.builtin.dnf: name: epel-release state: present - name: Baseline | PATCH | Restrict packages to be installed from EPEL - become: true community.general.ini_file: path: /etc/yum.repos.d/epel.repo section: epel option: includepkgs value: "{{ baseline_epel_packages_allowed | join(',') }}" - name: Baseline | PATCH | Disable EPEL openh264 repository - become: true community.general.ini_file: path: /etc/yum.repos.d/epel-cisco-openh264.repo section: epel-cisco-openh264 @@ -43,14 +40,12 @@ value: 0 - name: Baseline | PATCH | Remove EPEL repository - become: true ansible.builtin.dnf: name: epel-release state: absent when: (baseline_epel_packages_allowed is not defined) or (baseline_epel_packages_allowed | length == 0) - name: Baseline | PATCH | Remove cockpit-ws - become: true ansible.builtin.dnf: name: cockpit-ws state: absent @@ -64,7 +59,6 @@ when: baseline_lockdown - name: Baseline | PATCH | Ensure message of the day is configured properly (CIS 1.7.1, 1.7.4) - become: true ansible.builtin.template: src: motd.j2 dest: /etc/motd @@ -73,7 +67,6 @@ mode: 'u-x,go-wx' - name: Baseline | PATCH | Remove dhcpv6-client service from firewalld - become: true ansible.posix.firewalld: service: dhcpv6-client state: disabled @@ -82,7 +75,6 @@ zone: public - name: Baseline | PATCH | Remove mdns service from firewalld - become: true ansible.posix.firewalld: service: mdns state: disabled @@ -91,7 +83,6 @@ zone: public - name: Baseline | PATCH | Remove cockpit service from firewalld - become: true ansible.posix.firewalld: service: cockpit state: disabled diff --git a/roles/baseline/tasks/solusvm.yml b/roles/baseline/tasks/solusvm.yml index 90ce8e8..a43eb3b 100644 --- a/roles/baseline/tasks/solusvm.yml +++ b/roles/baseline/tasks/solusvm.yml @@ -1,7 +1,6 @@ --- # https://support.solusvm.com/hc/en-us/articles/21334950006807-How-to-install-Guest-Tools-manually-inside-a-VM-in-SolusVM-2 - name: SolusVM Guest | PATCH | Install required packages - become: true ansible.builtin.dnf: name: - qemu-guest-agent @@ -9,27 +8,27 @@ - tuned state: latest update_cache: true + become: true - name: SolusVM Guest | PATCH | Enable and start tuned - become: true ansible.builtin.systemd_service: name: tuned enabled: true state: started + become: true - name: SolusVM Guest | AUDIT | Check for tuned profile - become: true ansible.builtin.command: tuned-adm active register: _baseline_solusvm_tuned_profile + become: true changed_when: false - name: SolusVM Guest | PATCH | Start tuned profile (virtual-guest) - become: true ansible.builtin.command: tuned-adm profile virtual-guest + become: true when: "'virtual-guest' not in _baseline_solusvm_tuned_profile.stdout" - name: SolusVM Guest | PATCH | Remove console=ttyS0,115200n8 from bootloader configurations - become: true ansible.builtin.replace: path: "{{ item }}" regexp: 'console=ttyS0,115200n8' @@ -42,14 +41,12 @@ - Regenerate grub config - name: SolusVM Guest | AUDIT | Find all vmlinuz-* files in /boot - become: true ansible.builtin.find: paths: /boot patterns: 'vmlinuz-*' register: baseline_solusvm_kernels - name: SolusVM Guest | PATCH | Remove console=ttyS0,115200n8 from existing kernel bootloader entries - become: true ansible.builtin.command: cmd: "grubby --update-kernel={{ item.path }} --remove-args='console=ttyS0,115200n8'" with_items: "{{ baseline_solusvm_kernels.files }}" diff --git a/roles/podman_host/tasks/main.yml b/roles/podman_host/tasks/main.yml index 96254ff..26c6cdc 100644 --- a/roles/podman_host/tasks/main.yml +++ b/roles/podman_host/tasks/main.yml @@ -38,14 +38,6 @@ become_user: "{{ item }}" with_items: "{{ podman_host_rootless_users }}" -- name: Podman Host | PATCH | Set XDG_RUNTIME_DIR in .bashrc for rootless users - ansible.builtin.lineinfile: - path: "/home/{{ item }}/.bashrc" - line: "export XDG_RUNTIME_DIR=/run/user/$(id -u)" - become: true - become_user: "{{ item }}" - with_items: "{{ podman_host_rootless_users }}" - - name: Podman Host | PATCH | Enable linger for rootless users ansible.builtin.command: argv: