From 547c9976148fd31511c53a9d8c3945eb8d2ca0c2 Mon Sep 17 00:00:00 2001 From: The_miro Date: Wed, 20 May 2026 16:41:35 +0200 Subject: [PATCH] feat(ansipa): rework scan-notify as per-user policy policy-scan-notify is now a FreeIPA *user* group instead of a host group, so alert notifications follow the user to every enrolled machine. The fetch-alerts timer is installed fleet-wide on any host where the group exists; the profile.d snippet gates notification daemon start on runtime group membership (id(1) / SSSD) so non-members log in unaffected. Co-Authored-By: Claude Sonnet 4.6 --- docs/md/freeipa-ansible.md | 12 +++++-- .../ansible/ansipa-enforce-policies.sh | 33 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/docs/md/freeipa-ansible.md b/docs/md/freeipa-ansible.md index 375421c..76e02a2 100644 --- a/docs/md/freeipa-ansible.md +++ b/docs/md/freeipa-ansible.md @@ -277,7 +277,6 @@ Keys on the SMB share are accessible only to `KeyAdmin` group members (see [SMB | `policy-daemon-disable-` | `ansipa-enforce-policies.sh` | `systemctl disable --now `; reverted on leave | | `policy-timeshift-backup` | `ansipa-enforce-policies.sh` | Daily Timeshift snapshot at 03:00 | | `policy-security-scan` | `ansipa-enforce-policies.sh` | Daily ClamAV + rkhunter + chkrootkit scan + SMB upload | -| `policy-scan-notify` | `ansipa-enforce-policies.sh` | Fetch server alerts, notify user every 10 min until acknowledged | | `no_local_users` | `ansipa-enforce-policies.sh` | Lock passwords for root and all local users (UID ≥ 1000); reverted on leave | | `local_sudo_` | `ansipa-enforce-policies.sh` | Grant `` full sudo on this specific device; reverted on leave | @@ -286,12 +285,13 @@ Keys on the SMB share are accessible only to `KeyAdmin` group members (see [SMB | Group prefix | Handled by | Effect | |--------------|-----------|--------| | `policy-block-binary-` | `ansipa-enforce-policies.sh` | Prevent group members from running `` on any enrolled host; use `__` for `.` in Flatpak app IDs | +| `policy-scan-notify` | `ansipa-enforce-policies.sh` | Fetch server alerts and notify group members every 10 min until acknowledged; follows the user across all enrolled devices | --- ## Policy Enforcement -`ansipa-enforce-policies.sh` runs every 30 minutes on each enrolled client (deployed by `deploy-ansipa-policies.yml`). All policies are idempotent and reversible — leaving a host group undoes the policy on the next run. +`ansipa-enforce-policies.sh` runs every 30 minutes on each enrolled client (deployed by `deploy-ansipa-policies.yml`). All policies are idempotent and reversible — leaving a host/user group undoes the policy on the next run. ### Binary Blocking (per user) @@ -305,6 +305,14 @@ The enforcer queries all `policy-block-binary-*` user groups from FreeIPA on eve Deleting the IPA user group causes the wrapper to be removed on the next enforcer run. State is tracked in `/var/lib/ansipa-policies/blocked-binaries`. +### Scan Notifications (per user) + +`policy-scan-notify` is a FreeIPA **user group**, not a host group. Membership follows the user to every enrolled machine: group members receive scan alert notifications regardless of which device they log into. + +The enforcer checks whether the `policy-scan-notify` group exists in FreeIPA on every run. If it does, it installs the `ansipa-fetch-alerts` systemd timer fleet-wide (root service, fires every 10 min) and a `/etc/profile.d/ansipa-notify.sh` snippet. The profile.d snippet checks group membership at login via `id(1)` / SSSD and starts `ansipa-scan-notify.sh` as a background user daemon only for members. Non-members log in unaffected. + +Deleting the `policy-scan-notify` IPA user group causes the timer and profile.d snippet to be removed on the next enforcer run. + ### Daemon Enable / Disable | Group | Effect on join | Effect on leave | diff --git a/setup/modules/FreeipaAnsible/ansible/ansipa-enforce-policies.sh b/setup/modules/FreeipaAnsible/ansible/ansipa-enforce-policies.sh index fae558c..fe83426 100755 --- a/setup/modules/FreeipaAnsible/ansible/ansipa-enforce-policies.sh +++ b/setup/modules/FreeipaAnsible/ansible/ansipa-enforce-policies.sh @@ -13,6 +13,7 @@ # If a unit appears in both enable and disable groups it is skipped. # policy-timeshift-backup Enforce a daily Timeshift snapshot (requires timeshift installed) # policy-security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans +# policy-scan-notify (see User-group section below — treated as a user group) # no_local_users Lock passwords for root and all local users (UID >= 1000) so # that only FreeIPA domain accounts with centrally-managed sudo # rules can authenticate and gain elevated privileges. @@ -29,6 +30,12 @@ # Flatpak org.gimp.Gimp). Enforced via a PATH-priority wrapper # that checks group membership at runtime via SSSD/id(1). # Removing the user group from FreeIPA reverts the wrapper. +# policy-scan-notify Members receive scan alert notifications on every enrolled device +# they log into. The fetch-alerts timer is installed fleet-wide +# when the group exists; the notification daemon starts on login +# only for group members (checked via id(1) / SSSD). +# Deleting the IPA user group removes the timer and profile.d +# snippet on the next enforcer run. # # Notes: # - Install scan tools first: add the host to ansipa-module-anti-malware. @@ -76,7 +83,6 @@ if [[ -n "$RAW_GROUPS" ]]; then policy-daemon-disable-*) ACTIVE_DAEMON_DISABLE+=("${g#policy-daemon-disable-}") ;; policy-timeshift-backup) WANT_TIMESHIFT_BACKUP=true ;; policy-security-scan) WANT_SECURITY_SCAN=true ;; - policy-scan-notify) WANT_SCAN_NOTIFY=true ;; no_local_users) WANT_NO_LOCAL_USERS=true ;; local_sudo_*) ACTIVE_LOCAL_SUDO_USERS+=("${g#local_sudo_}") ;; esac @@ -102,12 +108,20 @@ while IFS= read -r _grp; do done <<< "$_BLOCK_LIST" unset _BLOCK_LIST _grp _raw +# ── Fetch user-group-based scan-notify policy from FreeIPA ──────────────────── +# policy-scan-notify is a FreeIPA *user* group — membership follows the user to +# every enrolled host. Install the fetch-alerts timer on any device where the +# group exists; the profile.d snippet gates daemon start on runtime membership. +if ipa group-show policy-scan-notify >/dev/null 2>&1; then + WANT_SCAN_NOTIFY=true +fi + log "Device policies — daemon-enable: ${ACTIVE_DAEMON_ENABLE[*]:-none}" \ "| daemon-disable: ${ACTIVE_DAEMON_DISABLE[*]:-none}" \ "| timeshift-backup: $WANT_TIMESHIFT_BACKUP | security-scan: $WANT_SECURITY_SCAN" \ - "| scan-notify: $WANT_SCAN_NOTIFY | no-local-users: $WANT_NO_LOCAL_USERS" \ - "| local-sudo: ${ACTIVE_LOCAL_SUDO_USERS[*]:-none}" -log "User policies — block-binary: ${ACTIVE_BLOCK_BINARIES[*]:-none}" + "| no-local-users: $WANT_NO_LOCAL_USERS | local-sudo: ${ACTIVE_LOCAL_SUDO_USERS[*]:-none}" +log "User policies — block-binary: ${ACTIVE_BLOCK_BINARIES[*]:-none}" \ + "| scan-notify: $WANT_SCAN_NOTIFY" # ── Helpers ─────────────────────────────────────────────────────────────────── _in_block_list() { @@ -264,7 +278,10 @@ else fi # ── Scan notification daemon ────────────────────────────────────────────────── -# policy-scan-notify: +# policy-scan-notify is a FreeIPA *user* group (not a host group). The fetch- +# alerts timer runs fleet-wide on any host where the group exists; the profile.d +# snippet starts the notification daemon on login only for group members +# (checked via id(1) / SSSD so no IPA query is needed at login time). # - Root timer (every 10 min): ansipa-fetch-alerts.sh downloads alerts from the # server SMB share and places them in ~/administration// per active user. # - profile.d snippet: starts ansipa-scan-notify.sh as a user daemon on login; @@ -317,10 +334,12 @@ UNIT if [[ ! -f "$NOTIFY_PROFILED" ]]; then log "Installing /etc/profile.d/ansipa-notify.sh" cat > "$NOTIFY_PROFILED" <<'PROFILED' -# ansipa-notify: launch the scan alert notification daemon on login. +# ansipa-notify: launch the scan alert notification daemon on login for +# members of the policy-scan-notify FreeIPA user group. # Managed by ansipa-enforce-policies — do not edit manually. _NOTIFY_DAEMON=/usr/local/bin/ansipa-scan-notify.sh if [[ -x "$_NOTIFY_DAEMON" ]] && \ + id -nG 2>/dev/null | grep -qw "policy-scan-notify" && \ ! pgrep -u "$(id -u)" -f "ansipa-scan-notify" >/dev/null 2>&1; then "$_NOTIFY_DAEMON" & disown @@ -334,7 +353,7 @@ else systemctl disable --now ansipa-fetch-alerts.timer 2>/dev/null || true rm -f "$FETCH_SVC" "$FETCH_TIMER" systemctl daemon-reload - log "Removed ansipa-fetch-alerts timer (host left policy-scan-notify group)" + log "Removed ansipa-fetch-alerts timer (policy-scan-notify user group no longer exists)" fi if [[ -f "$NOTIFY_PROFILED" ]]; then rm -f "$NOTIFY_PROFILED"