diff --git a/playbooks/all.yml b/playbooks/all.yml index fcba354fa..ef99bd15f 100644 --- a/playbooks/all.yml +++ b/playbooks/all.yml @@ -118,6 +118,7 @@ - import_playbook: 'repo_remi.yml' - import_playbook: 'repo_rpmfusion.yml' - import_playbook: 'repo_sury.yml' +- import_playbook: 'rl9_cis.yml - import_playbook: 'rsyslog.yml' - import_playbook: 'selinux.yml' - import_playbook: 'setup_basic.yml' diff --git a/playbooks/rl9_cis.yml b/playbooks/rl9_cis.yml new file mode 100644 index 000000000..435f0a290 --- /dev/null +++ b/playbooks/rl9_cis.yml @@ -0,0 +1,22 @@ +- name: 'Playbook linuxfabrik.lfops.rl9_cis' + hosts: + - 'lfops_rl9_cis' + + pre_tasks: + - ansible.builtin.import_role: + name: 'shared' + tasks_from: 'log-start.yml' + tags: + - 'always' + + + roles: + - role: 'linuxfabrik.lfops.aide' + + + post_tasks: + - ansible.builtin.import_role: + name: 'shared' + tasks_from: 'log-end.yml' + tags: + - 'always' diff --git a/roles/acme_sh/tasks/main.yml b/roles/acme_sh/tasks/main.yml index 995cd984a..5b16e6dcd 100644 --- a/roles/acme_sh/tasks/main.yml +++ b/roles/acme_sh/tasks/main.yml @@ -29,6 +29,7 @@ - name: 'Deploy /etc/systemd/system/acme-sh.service' ansible.builtin.template: + backup: true src: 'etc/systemd/system/acme-sh.service.j2' dest: '/etc/systemd/system/acme-sh.service' owner: 'root' @@ -37,6 +38,7 @@ - name: 'Deploy /etc/systemd/system/acme-sh.timer' ansible.builtin.template: + backup: true src: 'etc/systemd/system/acme-sh.timer.j2' dest: '/etc/systemd/system/acme-sh.timer' owner: 'root' diff --git a/roles/aide/README.md b/roles/aide/README.md new file mode 100644 index 000000000..c8a63aace --- /dev/null +++ b/roles/aide/README.md @@ -0,0 +1,46 @@ +# Ansible Role linuxfabrik.lfops.aide + +This role ensures that AIDE is installed, configured, and scheduled for regular filesystem integrity checks. + +* The initial AIDE database is created only if `/var/lib/aide/aide.db.gz` does not already exist. + + +## Tags + +| Tag | What it does | Reload / Restart | +| --- | ------------ | ---------------- | +| `aide` | Runs all tasks of the role | - | +| `aide:configure` | Deploys the `/etc/aide.conf` configuration file | - | +| `aide:install` | Installs the AIDE package and initializes the AIDE database if it does not exist yet | - | +| `aide:update_db` | Rebuilds the AIDE database; Only runs if called explicitly | - | +| `aide:state` | Deploys and enables the `aide-check.service` and `aide-check.timer` systemd units | Reloads systemd daemon if unit files changed | + + +## Optional Role Variables + +| Variable | Description | Default Value | +| -------- | ----------- | ------------- | +| `aide__check_time_on_calendar` | Specifies at what time of the day the aide check runs. Have a look at [systemd.time(7)](https://www.freedesktop.org/software/systemd/man/systemd.time.html) for the format. | `'05:00:00'` | +| `aide__include_rules` | List of paths to monitor with their AIDE rule group. | `['/srv CONTENT_EX', '/opt/venv CONTENT']` | +| `aide__exclude_rules` | List of paths to exclude from monitoring. | `['/srv/app/tmp', '/srv/app/cache']` | + +Example: +```yaml +# optional +aide__check_time_on_calendar: '03:00:00' +aide__include_rules: + - '/etc CONTENT_EX' # Extended content + file type + access + - '/srv/app/node_modules CONTENT' # Content + file type +aide__exclude_rules: + - '/var/log' +``` + + +## License + +[The Unlicense](https://unlicense.org/) + + +## Author Information + +[Linuxfabrik GmbH, Zurich](https://www.linuxfabrik.ch) diff --git a/roles/aide/defaults/main.yml b/roles/aide/defaults/main.yml new file mode 100644 index 000000000..fde613122 --- /dev/null +++ b/roles/aide/defaults/main.yml @@ -0,0 +1,10 @@ +aide__check_time_on_calendar: '05:00:00' #5 AM + +#vars for aide.conf +aide__include_rules: + - '/srv CONTENT_EX' # Extended content + file type + access. + - '/opt/venv CONTENT' # Content + file type. + +aide__exclude_rules: + - '/srv/app/tmp' + - '/srv/app/cache' diff --git a/roles/aide/handlers/main.yml b/roles/aide/handlers/main.yml new file mode 100644 index 000000000..e56db77b9 --- /dev/null +++ b/roles/aide/handlers/main.yml @@ -0,0 +1,7 @@ +- name: 'aide: init db' + ansible.builtin.service: + name: + +- name: 'aide: enable aidecheck.service' + ansible.builtin.service: + name: 'aidecheck.service' \ No newline at end of file diff --git a/roles/aide/tasks/main.yml b/roles/aide/tasks/main.yml new file mode 100644 index 000000000..5cbb6b312 --- /dev/null +++ b/roles/aide/tasks/main.yml @@ -0,0 +1,91 @@ + # CIS_Rocky_Linux_9_Benchmark_v2.0.0 +- block: + + - name: '6.1.1 Ensure AIDE is installed' + ansible.builtin.package: + name: + - 'aide' + state: 'present' + + - name: 'Initialize AIDE database' + ansible.builtin.command: 'aide --init --before "database_out=file:/var/lib/aide/aide.db.gz"' + args: + creates: '/var/lib/aide/aide.db.gz' + + tags: + - 'aide' + - 'aide:install' + + + # CIS_Rocky_Linux_9_Benchmark_v2.0.0 +- block: + + - name: '6.1.3 Ensure cryptographic mechanisms are used to protect the integrity of audit tools' + ansible.builtin.template: + backup: true + src: 'etc/aide.conf.j2' + dest: '/etc/aide.conf' + owner: 'root' + group: 'root' + mode: 0o644 + + tags: + - 'aide' + - 'aide:configure' + + + # CIS_Rocky_Linux_9_Benchmark_v2.0.0 +- block: + + - name: 'Update AIDE database' + ansible.builtin.command: "aide --init --before 'database_out=file:/var/lib/aide/aide.db.gz'" + changed_when: "'AIDE initialized database at' in aide__dbupdate_result.stdout" + register: 'aide__dbupdate_result' + + tags: + - 'never' + - 'aide:update_db' + + + # 6.1.2 Ensure filesystem integrity is regularly checked + # CIS_Rocky_Linux_9_Benchmark_v2.0.0 +- block: + + - name: 'Create /etc/systemd/system/aide-check.service' + ansible.builtin.template: + src: 'etc/systemd/system/aide-check.service.j2' + dest: '/etc/systemd/system/aide-check.service' + owner: 'root' + group: 'root' + mode: 0o644 + register: '__aide__service_unit_result' + + - name: 'Create /etc/systemd/system/aide-check.timer' + ansible.builtin.template: + src: 'etc/systemd/system/aide-check.timer.j2' + dest: '/etc/systemd/system/aide-check.timer' + owner: 'root' + group: 'root' + mode: 0o644 + register: '__aide__timer_unit_result' + + - name: 'Reload systemd' + ansible.builtin.systemd: + daemon_reload: true + when: + - '__aide__service_unit_result is changed or __aide__timer_unit_result is changed' + + - name: 'Enable aide-check.service' + ansible.builtin.systemd: + name: 'aide-check.service' + enabled: true + + - name: 'Enable aide-check.timer' + ansible.builtin.systemd: + name: 'aide-check.timer' + state: 'started' + enabled: true + + tags: + - 'aide' + - 'aide:state' \ No newline at end of file diff --git a/roles/aide/templates/etc/aide.conf.j2 b/roles/aide/templates/etc/aide.conf.j2 new file mode 100644 index 000000000..915c60ff2 --- /dev/null +++ b/roles/aide/templates/etc/aide.conf.j2 @@ -0,0 +1,341 @@ +# {{ ansible_managed }} +# 2026030401 +# Configuration file for AIDE. + +@@define DBDIR /var/lib/aide +@@define LOGDIR /var/log/aide + +# The location of the database to be read. +database=file:@@{DBDIR}/aide.db.gz + +# The location of the database to be written. +#database_out=sql:host:port:database:login_name:passwd:table +#database_out=file:aide.db.new +database_out=file:@@{DBDIR}/aide.db.new.gz + +# Whether to gzip the output to database +gzip_dbout=yes + +# Default. +verbose=5 + +report_url=file:@@{LOGDIR}/aide.log +report_url=stdout +#report_url=stderr +#NOT IMPLEMENTED report_url=mailto:root@foo.com +#NOT IMPLEMENTED report_url=syslog:LOG_AUTH + +# These are the default rules. +# +#p: permissions +#i: inode: +#n: number of links +#u: user +#g: group +#s: size +#b: block count +#m: mtime +#a: atime +#c: ctime +#S: check for growing size +#acl: Access Control Lists +#selinux SELinux security context +#xattrs: Extended file attributes +#md5: md5 checksum +#sha1: sha1 checksum +#sha256: sha256 checksum +#sha512: sha512 checksum +#rmd160: rmd160 checksum +#tiger: tiger checksum + +#haval: haval checksum (MHASH only) +#gost: gost checksum (MHASH only) +#crc32: crc32 checksum (MHASH only) +#whirlpool: whirlpool checksum (MHASH only) + +#R: p+i+n+u+g+s+m+c+acl+selinux+xattrs+md5 +#L: p+i+n+u+g+acl+selinux+xattrs +#E: Empty group +#>: Growing logfile p+u+g+i+n+S+acl+selinux+xattrs + +# You can create custom rules like this. +# With MHASH... +# ALLXTRAHASHES = sha1+rmd160+sha256+sha512+whirlpool+tiger+haval+gost+crc32 +ALLXTRAHASHES = sha1+rmd160+sha256+sha512+tiger +# Everything but access time (Ie. all changes) +EVERYTHING = R+ALLXTRAHASHES + +# Sane +# NORMAL = R+sha512 +NORMAL = p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha512 + +# For directories, don't bother doing hashes +DIR = p+i+n+u+g+acl+selinux+xattrs + +# Access control only +PERMS = p+u+g+acl+selinux+xattrs + +# Logfile are special, in that they often change +LOG = p+u+g+n+S+acl+selinux+xattrs + +# Content + file type. +CONTENT = sha512+ftype + +# Extended content + file type + access. +CONTENT_EX = sha512+ftype+p+u+g+n+acl+selinux+xattrs + +# Some files get updated automatically, so the inode/ctime/mtime change +# but we want to know when the data inside them changes +DATAONLY = p+n+u+g+s+acl+selinux+xattrs+sha512 + +# Next decide what directories/files you want in the database. + +/boot CONTENT_EX +/opt CONTENT + +# Admins dot files constantly change, just check perms +/root/\..* PERMS +!/root/.xauth* +# Otherwise get all of /root. +/root CONTENT_EX + +# These are too volatile +!/usr/src +!/usr/tmp + +# Otherwise get all of /usr. +/usr CONTENT_EX + +# Audit Tools +/usr/sbin/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512 +/usr/sbin/auditd p+i+n+u+g+s+b+acl+xattrs+sha512 +/usr/sbin/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512 +/usr/sbin/aureport p+i+n+u+g+s+b+acl+xattrs+sha512 +/usr/sbin/autrace p+i+n+u+g+s+b+acl+xattrs+sha512 +/usr/sbin/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 + +# trusted databases +/etc/hosts$ CONTENT_EX +/etc/host.conf$ CONTENT_EX +/etc/hostname$ CONTENT_EX +/etc/issue$ CONTENT_EX +/etc/issue.net$ CONTENT_EX +/etc/protocols$ CONTENT_EX +/etc/services$ CONTENT_EX +/etc/localtime$ CONTENT_EX +/etc/alternatives CONTENT_EX +/etc/sysconfig CONTENT_EX +/etc/mime.types$ CONTENT_EX +/etc/terminfo CONTENT_EX +/etc/exports$ CONTENT_EX +/etc/fstab$ CONTENT_EX +/etc/passwd$ CONTENT_EX +/etc/group$ CONTENT_EX +/etc/gshadow$ CONTENT_EX +/etc/shadow$ CONTENT_EX +/etc/subgid$ CONTENT_EX +/etc/subuid$ CONTENT_EX +/etc/security/opasswd$ CONTENT_EX +/etc/skel CONTENT_EX +/etc/sssd CONTENT_EX +/etc/machine-id$ CONTENT_EX +/etc/swid CONTENT_EX +/etc/system-release-cpe$ CONTENT_EX +/etc/shells$ CONTENT_EX +/etc/tmux.conf$ CONTENT_EX +/etc/xattr.conf$ CONTENT_EX + +# networking +/etc/firewalld CONTENT_EX +!/etc/NetworkManager/system-connections +/etc/NetworkManager CONTENT_EX +/etc/networks$ CONTENT_EX +/etc/dhcp CONTENT_EX +/etc/wpa_supplicant CONTENT_EX +/etc/resolv.conf$ DATAONLY +/etc/nscd.conf$ CONTENT_EX + +# logins and accounts +/etc/login.defs$ CONTENT_EX +/etc/libuser.conf$ CONTENT_EX +/var/log/faillog$ PERMS +/var/log/lastlog$ PERMS +/var/run/faillock PERMS +/etc/pam.d CONTENT_EX +/etc/security CONTENT_EX +/etc/securetty$ CONTENT_EX +/etc/polkit-1 CONTENT_EX +/etc/sudo.conf$ CONTENT_EX +/etc/sudoers$ CONTENT_EX +/etc/sudoers.d CONTENT_EX + +# Shell/X startup files +/etc/profile$ CONTENT_EX +/etc/profile.d CONTENT_EX +/etc/bashrc$ CONTENT_EX +/etc/bash_completion.d CONTENT_EX +/etc/zprofile$ CONTENT_EX +/etc/zshrc$ CONTENT_EX +/etc/zlogin$ CONTENT_EX +/etc/zlogout$ CONTENT_EX +/etc/X11 CONTENT_EX + +# Pkg manager +/etc/dnf CONTENT_EX +/etc/yum.conf$ CONTENT_EX +/etc/yum CONTENT_EX +/etc/yum.repos.d CONTENT_EX + +# This gets new/removes-old filenames daily +!/var/log/sa +# As we are checking it, we've truncated yesterdays size to zero. +!/var/log/aide.log + +# auditing +# AIDE produces an audit record, so this becomes perpetual motion. +/var/log/audit PERMS +/etc/audit CONTENT_EX +/etc/libaudit.conf$ CONTENT_EX +/etc/aide.conf$ CONTENT_EX + +# System logs +/etc/rsyslog.conf$ CONTENT_EX +/etc/rsyslog.d CONTENT_EX +/etc/logrotate.conf$ CONTENT_EX +/etc/logrotate.d CONTENT_EX +/etc/systemd/journald.conf$ CONTENT_EX +/var/log LOG+ANF+ARF +/var/run/utmp LOG + +# secrets +/etc/pkcs11 CONTENT_EX +/etc/pki CONTENT_EX +/etc/crypto-policies CONTENT_EX +/etc/certmonger CONTENT_EX +/var/lib/systemd/random-seed$ PERMS + +# init system +/etc/systemd CONTENT_EX +/etc/rc.d CONTENT_EX +/etc/tmpfiles.d CONTENT_EX + +# boot config +/etc/default CONTENT_EX +/etc/grub.d CONTENT_EX +/etc/dracut.conf$ CONTENT_EX +/etc/dracut.conf.d CONTENT_EX + +# glibc linker +/etc/ld.so.cache$ CONTENT_EX +/etc/ld.so.conf$ CONTENT_EX +/etc/ld.so.conf.d CONTENT_EX +/etc/ld.so.preload$ CONTENT_EX + +# kernel config +/etc/sysctl.conf$ CONTENT_EX +/etc/sysctl.d CONTENT_EX +/etc/modprobe.d CONTENT_EX +/etc/modules-load.d CONTENT_EX +/etc/depmod.d CONTENT_EX +/etc/udev CONTENT_EX +/etc/crypttab$ CONTENT_EX + +#### Daemons #### + +# cron jobs +/etc/at.allow$ CONTENT +/etc/at.deny$ CONTENT +/etc/anacrontab$ CONTENT_EX +/etc/cron.allow$ CONTENT_EX +/etc/cron.deny$ CONTENT_EX +/etc/cron.d CONTENT_EX +/etc/cron.daily CONTENT_EX +/etc/cron.hourly CONTENT_EX +/etc/cron.monthly CONTENT_EX +/etc/cron.weekly CONTENT_EX +/etc/crontab$ CONTENT_EX +/var/spool/cron/root CONTENT + +# time keeping +/etc/chrony.conf$ CONTENT_EX +/etc/chrony.keys$ CONTENT_EX + +# mail +/etc/aliases$ CONTENT_EX +/etc/aliases.db$ CONTENT_EX +/etc/postfix CONTENT_EX + +# ssh +/etc/ssh/sshd_config$ CONTENT_EX +/etc/ssh/ssh_config$ CONTENT_EX + +# stunnel +/etc/stunnel CONTENT_EX + +# printing +/etc/cups CONTENT_EX +/etc/cupshelpers CONTENT_EX +/etc/avahi CONTENT_EX + +# web server +/etc/httpd CONTENT_EX + +# dns +/etc/named CONTENT_EX +/etc/named.conf$ CONTENT_EX +/etc/named.iscdlv.key$ CONTENT_EX +/etc/named.rfc1912.zones$ CONTENT_EX +/etc/named.root.key$ CONTENT_EX + +# xinetd +/etc/xinetd.conf$ CONTENT_EX +/etc/xinetd.d CONTENT_EX + +# IPsec +/etc/ipsec.conf$ CONTENT_EX +/etc/ipsec.secrets$ CONTENT_EX +/etc/ipsec.d CONTENT_EX + +# USB guard +/etc/usbguard CONTENT_EX + +# Ignore some files +!/boot/grub2/grubenv$ +!/etc/mtab$ +!/etc/.*~ + +# Now everything else +/etc PERMS + +# With AIDE's default verbosity level of 5, these would give lots of +# warnings upon tree traversal. It might change with future version. +# +#=/lost\+found DIR +#=/home DIR + +# Ditto same reason... + +# Admins dot files constantly change, just check perms +/root/\..* PERMS +!/root/.xauth* + +# managed by ansible +# set in defaults/main.yml +# custom include rules +{% for rule in aide__include_rules %} +{{ rule }} +{% endfor %} + +# Custom exclude rules +{% for rule in aide__exclude_rules %} +!{{ rule }} +{% endfor %} + +# Linuxfabrik +# see https://bugzilla.redhat.com/show_bug.cgi?id=1304334 +!/opt/wildfly-22.0.1.Final/standalone/tmp +!/opt/wildfly/standalone/tmp +!/root/.cache/borg +!/root/.config/borg/security +!/var/log +!/var/spool \ No newline at end of file diff --git a/roles/aide/templates/etc/systemd/system/aide-check.service.j2 b/roles/aide/templates/etc/systemd/system/aide-check.service.j2 new file mode 100644 index 000000000..c6d162d89 --- /dev/null +++ b/roles/aide/templates/etc/systemd/system/aide-check.service.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} +# 2026030401 + + +[Unit] +Description=Aide Check + +[Service] +Type=simple +ExecStart=/usr/sbin/aide --check + +[Install] +WantedBy=multi-user.target diff --git a/roles/aide/templates/etc/systemd/system/aide-check.timer.j2 b/roles/aide/templates/etc/systemd/system/aide-check.timer.j2 new file mode 100644 index 000000000..14c907e67 --- /dev/null +++ b/roles/aide/templates/etc/systemd/system/aide-check.timer.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} +# 2026030401 + + +[Unit] +Description=Aide check every day + +[Timer] +OnCalendar=*-*-* {{ aide__check_time_on_calendar }} +Unit=aidecheck.service + +[Install] +WantedBy=multi-user.target \ No newline at end of file