From 0cad272d46dfc016dcde720b2af0e438444adb53 Mon Sep 17 00:00:00 2001 From: Jeremie Fraeys Date: Fri, 6 Mar 2026 14:30:01 -0500 Subject: [PATCH] refactor(hardening): update security handlers and tasks - Update hardening handlers for service restart management - Modify hardening tasks for improved security configurations - Align with container scanning integration Part of: Infrastructure hardening improvements --- roles/hardening/handlers/main.yml | 5 ++ roles/hardening/tasks/main.yml | 123 +++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/roles/hardening/handlers/main.yml b/roles/hardening/handlers/main.yml index b91e5e0..14b5014 100644 --- a/roles/hardening/handlers/main.yml +++ b/roles/hardening/handlers/main.yml @@ -3,3 +3,8 @@ service: name: rsyslog state: restarted + +- name: Restart SSH + service: + name: ssh + state: restarted diff --git a/roles/hardening/tasks/main.yml b/roles/hardening/tasks/main.yml index 75784f1..daded0d 100644 --- a/roles/hardening/tasks/main.yml +++ b/roles/hardening/tasks/main.yml @@ -1,4 +1,48 @@ --- +- name: Install unattended-upgrades for automatic security updates + apt: + name: + - unattended-upgrades + - apt-listchanges + state: present + update_cache: true + +- name: Configure unattended-upgrades for security updates only + copy: + dest: /etc/apt/apt.conf.d/50unattended-upgrades + owner: root + group: root + mode: "0644" + content: | + Unattended-Upgrade::Allowed-Origins { + "${distro_id}:${distro_codename}-security"; + "${distro_id}ESMApps:${distro_codename}-apps-security"; + "${distro_id}ESM:${distro_codename}-infra-security"; + }; + Unattended-Upgrade::Package-Blacklist { + }; + Unattended-Upgrade::AutoFixInterruptedDpkg "true"; + Unattended-Upgrade::MinimalSteps "true"; + Unattended-Upgrade::InstallOnShutdown "false"; + Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; + Unattended-Upgrade::Remove-Unused-Dependencies "true"; + Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; + Unattended-Upgrade::SyslogEnable "true"; + Unattended-Upgrade::SyslogFacility "daemon"; + Unattended-Upgrade::OnlyOnACPower "false"; + +- name: Enable automatic updates via periodic apt configuration + copy: + dest: /etc/apt/apt.conf.d/20auto-upgrades + owner: root + group: root + mode: "0644" + content: | + APT::Periodic::Update-Package-Lists "1"; + APT::Periodic::Unattended-Upgrade "1"; + APT::Periodic::Download-Upgradeable-Packages "1"; + APT::Periodic::AutocleanInterval "7"; + - name: Install rsyslog apt: name: rsyslog @@ -11,6 +55,18 @@ state: started enabled: true +- name: Check if reboot is required + stat: + path: /var/run/reboot-required + register: reboot_required_file + changed_when: false + +- name: Display reboot required message + debug: + msg: "REBOOT REQUIRED: Security updates installed requiring system restart" + when: reboot_required_file.stat.exists + changed_when: false + - name: Configure rsyslog to write UFW kernel logs to /var/log/ufw.log copy: dest: /etc/rsyslog.d/20-ufw.conf @@ -51,8 +107,67 @@ endscript } -- name: Set UFW logging level to low - command: ufw logging low - register: ufw_logging - changed_when: "'Logging enabled' in ufw_logging.stdout or 'Logging:' in ufw_logging.stdout" +- name: Tighten sudo privileges for ansible user + copy: + dest: /etc/sudoers.d/90-ansible + owner: root + group: root + mode: "0440" + validate: /usr/sbin/visudo -csf %s + content: | + ansible ALL=(ALL) NOPASSWD: /usr/bin/apt-get *, /usr/bin/apt *, /usr/bin/dpkg * + ansible ALL=(ALL) NOPASSWD: /usr/bin/systemctl *, /bin/systemctl * + ansible ALL=(ALL) NOPASSWD: /usr/bin/docker *, /usr/local/bin/docker * + ansible ALL=(ALL) NOPASSWD: /usr/bin/mkdir /opt/*, /bin/mkdir /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/chown /opt/*, /bin/chown /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/chmod /opt/*, /bin/chmod /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/*, /bin/cp /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/mv /opt/*, /bin/mv /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/rm /opt/*, /bin/rm /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/tee /opt/*, /bin/tee /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/bin/cat /opt/*, /bin/cat /opt/* + ansible ALL=(ALL) NOPASSWD: /usr/sbin/ufw * + ansible ALL=(ALL) NOPASSWD: /sbin/sysctl *, /usr/sbin/sysctl * failed_when: false + +- name: Include container scanning tasks + include_tasks: container-scanning.yml + when: enable_container_scanning | default(true) | bool + +- name: Ensure SSH directory exists + file: + path: /etc/ssh/sshd_config.d + state: directory + mode: "0755" + +- name: Configure SSH hardening + copy: + dest: /etc/ssh/sshd_config.d/99-infra-ansible.conf + owner: root + group: root + mode: "0644" + content: | + # Ansible-managed SSH hardening + PermitRootLogin no + PasswordAuthentication no + KbdInteractiveAuthentication no + ChallengeResponseAuthentication no + PubkeyAuthentication yes + UsePAM yes + ClientAliveInterval 180 + LoginGraceTime 30 + MaxAuthTries 3 + MaxSessions 10 + MaxStartups 10:30:60 + StrictModes yes + AuthorizedKeysFile .ssh/authorized_keys + notify: Restart SSH + +- name: Ensure fail2ban SSH jail port matches actual SSH port + lineinfile: + path: /etc/ssh/sshd_config.d/99-infra-ansible.conf + regexp: '^Port\s+' + line: "Port {{ ansible_port | default(22) }}" + create: false + notify: Restart SSH + when: ansible_port is defined and ansible_port != 22