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 <noreply@anthropic.com>main
parent
87b62f368b
commit
547c997614
|
|
@ -277,7 +277,6 @@ Keys on the SMB share are accessible only to `KeyAdmin` group members (see [SMB
|
||||||
| `policy-daemon-disable-<unit>` | `ansipa-enforce-policies.sh` | `systemctl disable --now <unit>`; reverted on leave |
|
| `policy-daemon-disable-<unit>` | `ansipa-enforce-policies.sh` | `systemctl disable --now <unit>`; reverted on leave |
|
||||||
| `policy-timeshift-backup` | `ansipa-enforce-policies.sh` | Daily Timeshift snapshot at 03:00 |
|
| `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-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 |
|
| `no_local_users` | `ansipa-enforce-policies.sh` | Lock passwords for root and all local users (UID ≥ 1000); reverted on leave |
|
||||||
| `local_sudo_<username>` | `ansipa-enforce-policies.sh` | Grant `<username>` full sudo on this specific device; reverted on leave |
|
| `local_sudo_<username>` | `ansipa-enforce-policies.sh` | Grant `<username>` 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 |
|
| Group prefix | Handled by | Effect |
|
||||||
|--------------|-----------|--------|
|
|--------------|-----------|--------|
|
||||||
| `policy-block-binary-<name>` | `ansipa-enforce-policies.sh` | Prevent group members from running `<name>` on any enrolled host; use `__` for `.` in Flatpak app IDs |
|
| `policy-block-binary-<name>` | `ansipa-enforce-policies.sh` | Prevent group members from running `<name>` 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
|
## 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)
|
### 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`.
|
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
|
### Daemon Enable / Disable
|
||||||
|
|
||||||
| Group | Effect on join | Effect on leave |
|
| Group | Effect on join | Effect on leave |
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
# If a unit appears in both enable and disable groups it is skipped.
|
# 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-timeshift-backup Enforce a daily Timeshift snapshot (requires timeshift installed)
|
||||||
# policy-security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans
|
# 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
|
# no_local_users Lock passwords for root and all local users (UID >= 1000) so
|
||||||
# that only FreeIPA domain accounts with centrally-managed sudo
|
# that only FreeIPA domain accounts with centrally-managed sudo
|
||||||
# rules can authenticate and gain elevated privileges.
|
# rules can authenticate and gain elevated privileges.
|
||||||
|
|
@ -29,6 +30,12 @@
|
||||||
# Flatpak org.gimp.Gimp). Enforced via a PATH-priority wrapper
|
# Flatpak org.gimp.Gimp). Enforced via a PATH-priority wrapper
|
||||||
# that checks group membership at runtime via SSSD/id(1).
|
# that checks group membership at runtime via SSSD/id(1).
|
||||||
# Removing the user group from FreeIPA reverts the wrapper.
|
# 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:
|
# Notes:
|
||||||
# - Install scan tools first: add the host to ansipa-module-anti-malware.
|
# - 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-daemon-disable-*) ACTIVE_DAEMON_DISABLE+=("${g#policy-daemon-disable-}") ;;
|
||||||
policy-timeshift-backup) WANT_TIMESHIFT_BACKUP=true ;;
|
policy-timeshift-backup) WANT_TIMESHIFT_BACKUP=true ;;
|
||||||
policy-security-scan) WANT_SECURITY_SCAN=true ;;
|
policy-security-scan) WANT_SECURITY_SCAN=true ;;
|
||||||
policy-scan-notify) WANT_SCAN_NOTIFY=true ;;
|
|
||||||
no_local_users) WANT_NO_LOCAL_USERS=true ;;
|
no_local_users) WANT_NO_LOCAL_USERS=true ;;
|
||||||
local_sudo_*) ACTIVE_LOCAL_SUDO_USERS+=("${g#local_sudo_}") ;;
|
local_sudo_*) ACTIVE_LOCAL_SUDO_USERS+=("${g#local_sudo_}") ;;
|
||||||
esac
|
esac
|
||||||
|
|
@ -102,12 +108,20 @@ while IFS= read -r _grp; do
|
||||||
done <<< "$_BLOCK_LIST"
|
done <<< "$_BLOCK_LIST"
|
||||||
unset _BLOCK_LIST _grp _raw
|
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}" \
|
log "Device policies — daemon-enable: ${ACTIVE_DAEMON_ENABLE[*]:-none}" \
|
||||||
"| daemon-disable: ${ACTIVE_DAEMON_DISABLE[*]:-none}" \
|
"| daemon-disable: ${ACTIVE_DAEMON_DISABLE[*]:-none}" \
|
||||||
"| timeshift-backup: $WANT_TIMESHIFT_BACKUP | security-scan: $WANT_SECURITY_SCAN" \
|
"| timeshift-backup: $WANT_TIMESHIFT_BACKUP | security-scan: $WANT_SECURITY_SCAN" \
|
||||||
"| scan-notify: $WANT_SCAN_NOTIFY | no-local-users: $WANT_NO_LOCAL_USERS" \
|
"| no-local-users: $WANT_NO_LOCAL_USERS | local-sudo: ${ACTIVE_LOCAL_SUDO_USERS[*]:-none}"
|
||||||
"| local-sudo: ${ACTIVE_LOCAL_SUDO_USERS[*]:-none}"
|
log "User policies — block-binary: ${ACTIVE_BLOCK_BINARIES[*]:-none}" \
|
||||||
log "User policies — block-binary: ${ACTIVE_BLOCK_BINARIES[*]:-none}"
|
"| scan-notify: $WANT_SCAN_NOTIFY"
|
||||||
|
|
||||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
_in_block_list() {
|
_in_block_list() {
|
||||||
|
|
@ -264,7 +278,10 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Scan notification daemon ──────────────────────────────────────────────────
|
# ── 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
|
# - Root timer (every 10 min): ansipa-fetch-alerts.sh downloads alerts from the
|
||||||
# server SMB share and places them in ~/administration/<hostname>/ per active user.
|
# server SMB share and places them in ~/administration/<hostname>/ per active user.
|
||||||
# - profile.d snippet: starts ansipa-scan-notify.sh as a user daemon on login;
|
# - profile.d snippet: starts ansipa-scan-notify.sh as a user daemon on login;
|
||||||
|
|
@ -317,10 +334,12 @@ UNIT
|
||||||
if [[ ! -f "$NOTIFY_PROFILED" ]]; then
|
if [[ ! -f "$NOTIFY_PROFILED" ]]; then
|
||||||
log "Installing /etc/profile.d/ansipa-notify.sh"
|
log "Installing /etc/profile.d/ansipa-notify.sh"
|
||||||
cat > "$NOTIFY_PROFILED" <<'PROFILED'
|
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.
|
# Managed by ansipa-enforce-policies — do not edit manually.
|
||||||
_NOTIFY_DAEMON=/usr/local/bin/ansipa-scan-notify.sh
|
_NOTIFY_DAEMON=/usr/local/bin/ansipa-scan-notify.sh
|
||||||
if [[ -x "$_NOTIFY_DAEMON" ]] && \
|
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
|
! pgrep -u "$(id -u)" -f "ansipa-scan-notify" >/dev/null 2>&1; then
|
||||||
"$_NOTIFY_DAEMON" &
|
"$_NOTIFY_DAEMON" &
|
||||||
disown
|
disown
|
||||||
|
|
@ -334,7 +353,7 @@ else
|
||||||
systemctl disable --now ansipa-fetch-alerts.timer 2>/dev/null || true
|
systemctl disable --now ansipa-fetch-alerts.timer 2>/dev/null || true
|
||||||
rm -f "$FETCH_SVC" "$FETCH_TIMER"
|
rm -f "$FETCH_SVC" "$FETCH_TIMER"
|
||||||
systemctl daemon-reload
|
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
|
fi
|
||||||
if [[ -f "$NOTIFY_PROFILED" ]]; then
|
if [[ -f "$NOTIFY_PROFILED" ]]; then
|
||||||
rm -f "$NOTIFY_PROFILED"
|
rm -f "$NOTIFY_PROFILED"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue