feat(ansipa): add daemon enable/disable policy via host-group regex
Host groups named policy-daemon-enable-<unit> and
policy-daemon-disable-<unit> are now matched by a wildcard case arm in
the group parser — no per-service configuration required.
Enforcement (every 30 min via existing timer):
enable: systemctl enable --now <unit>; state written to
/var/lib/ansipa-policies/daemon-enabled
disable: systemctl disable --now <unit>; state written to
/var/lib/ansipa-policies/daemon-disabled
revert: when a host leaves a group the opposite action is applied
on the next run (enable→disable, disable→enable)
conflict: unit in both lists is skipped with a warning
The .service suffix is optional — _svc_unit() appends it when the name
contains no dot, so all systemd unit types work as-is.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
parent
63cd59fb91
commit
aced2c754e
|
|
@ -5,12 +5,18 @@
|
|||
# leaving the group removes it on the next run (every 30 min via systemd timer).
|
||||
#
|
||||
# Host-group naming conventions:
|
||||
# policy-block-binary-<name> Block execution of <name> via two layers:
|
||||
# 1. PATH-priority wrapper in /usr/local/bin/ (catches $PATH calls)
|
||||
# 2. AppArmor deny profile in /etc/apparmor.d/ (catches absolute paths)
|
||||
# AppArmor layer is skipped silently if apparmor_parser is not present.
|
||||
# policy-timeshift-backup Enforce a daily Timeshift snapshot (requires timeshift installed)
|
||||
# policy-security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans
|
||||
# policy-block-binary-<name> Block execution of <name> via two layers:
|
||||
# 1. PATH-priority wrapper in /usr/local/bin/ (catches $PATH calls)
|
||||
# 2. AppArmor deny profile in /etc/apparmor.d/ (catches absolute paths)
|
||||
# AppArmor layer is skipped silently if apparmor_parser is not present.
|
||||
# policy-daemon-enable-<unit> Ensure <unit> is enabled and running (systemctl enable --now).
|
||||
# Leaving the group reverts: service is disabled and stopped.
|
||||
# policy-daemon-disable-<unit> Ensure <unit> is disabled and stopped (systemctl disable --now).
|
||||
# Leaving the group reverts: service is re-enabled and started.
|
||||
# <unit> may omit the .service suffix; all systemd unit types work.
|
||||
# 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
|
||||
#
|
||||
# Notes:
|
||||
# - Install scan tools first: add the host to ansipa-module-anti-malware.
|
||||
|
|
@ -43,6 +49,8 @@ RAW_GROUPS=$(ipa host-show "$HOST_FQDN" --all 2>/dev/null \
|
|||
|
||||
# ── Parse active policy groups ────────────────────────────────────────────────
|
||||
ACTIVE_BLOCK_BINARIES=()
|
||||
ACTIVE_DAEMON_ENABLE=()
|
||||
ACTIVE_DAEMON_DISABLE=()
|
||||
WANT_TIMESHIFT_BACKUP=false
|
||||
WANT_SECURITY_SCAN=false
|
||||
WANT_SCAN_NOTIFY=false
|
||||
|
|
@ -53,6 +61,8 @@ if [[ -n "$RAW_GROUPS" ]]; then
|
|||
g="${g// /}"
|
||||
case "$g" in
|
||||
policy-block-binary-*) ACTIVE_BLOCK_BINARIES+=("${g#policy-block-binary-}") ;;
|
||||
policy-daemon-enable-*) ACTIVE_DAEMON_ENABLE+=("${g#policy-daemon-enable-}") ;;
|
||||
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 ;;
|
||||
|
|
@ -62,6 +72,7 @@ if [[ -n "$RAW_GROUPS" ]]; then
|
|||
fi
|
||||
|
||||
log "Active policies — block-binary: ${ACTIVE_BLOCK_BINARIES[*]:-none}" \
|
||||
"| 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"
|
||||
|
||||
|
|
@ -347,4 +358,90 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
# ── Daemon enable / disable ───────────────────────────────────────────────────
|
||||
# policy-daemon-enable-<unit>: ensure the unit is enabled and running.
|
||||
# Leaving the group reverts: unit is disabled and stopped.
|
||||
# policy-daemon-disable-<unit>: ensure the unit is disabled and stopped.
|
||||
# Leaving the group reverts: unit is re-enabled and started.
|
||||
# <unit> may omit the .service suffix; systemd accepts both forms.
|
||||
# Conflicts (unit in both lists): logged as a warning, unit is left untouched.
|
||||
|
||||
DAEMON_ENABLE_STATE="$STATE_DIR/daemon-enabled"
|
||||
DAEMON_DISABLE_STATE="$STATE_DIR/daemon-disabled"
|
||||
[[ -f "$DAEMON_ENABLE_STATE" ]] || touch "$DAEMON_ENABLE_STATE"
|
||||
[[ -f "$DAEMON_DISABLE_STATE" ]] || touch "$DAEMON_DISABLE_STATE"
|
||||
|
||||
# Append .service only when the name has no unit-type suffix already.
|
||||
_svc_unit() { [[ "$1" == *.* ]] && echo "$1" || echo "${1}.service"; }
|
||||
|
||||
_in_enable_list() { local n="$1"; for s in "${ACTIVE_DAEMON_ENABLE[@]}"; do [[ "$s" == "$n" ]] && return 0; done; return 1; }
|
||||
_in_disable_list() { local n="$1"; for s in "${ACTIVE_DAEMON_DISABLE[@]}"; do [[ "$s" == "$n" ]] && return 0; done; return 1; }
|
||||
|
||||
# Apply enable policies
|
||||
for _SVC in "${ACTIVE_DAEMON_ENABLE[@]}"; do
|
||||
if _in_disable_list "$_SVC"; then
|
||||
warn "Conflict: '$_SVC' is in both daemon-enable and daemon-disable groups — skipped"
|
||||
continue
|
||||
fi
|
||||
_UNIT=$(_svc_unit "$_SVC")
|
||||
_EN=$(systemctl is-enabled "$_UNIT" 2>/dev/null || echo "not-found")
|
||||
_AC=$(systemctl is-active "$_UNIT" 2>/dev/null || echo "inactive")
|
||||
if [[ "$_EN" != "enabled" || "$_AC" != "active" ]]; then
|
||||
log "Enabling service: $_UNIT (enabled=$_EN active=$_AC)"
|
||||
systemctl enable --now "$_UNIT" 2>/dev/null \
|
||||
&& log "Service enabled: $_UNIT" \
|
||||
|| warn "Failed to enable $_UNIT — unit may not exist on this host"
|
||||
fi
|
||||
done
|
||||
|
||||
# Apply disable policies
|
||||
for _SVC in "${ACTIVE_DAEMON_DISABLE[@]}"; do
|
||||
if _in_enable_list "$_SVC"; then
|
||||
continue # conflict already warned above
|
||||
fi
|
||||
_UNIT=$(_svc_unit "$_SVC")
|
||||
_EN=$(systemctl is-enabled "$_UNIT" 2>/dev/null || echo "not-found")
|
||||
_AC=$(systemctl is-active "$_UNIT" 2>/dev/null || echo "inactive")
|
||||
if [[ "$_EN" == "enabled" || "$_AC" == "active" ]]; then
|
||||
log "Disabling service: $_UNIT (enabled=$_EN active=$_AC)"
|
||||
systemctl disable --now "$_UNIT" 2>/dev/null \
|
||||
&& log "Service disabled: $_UNIT" \
|
||||
|| warn "Failed to disable $_UNIT — unit may not exist on this host"
|
||||
fi
|
||||
done
|
||||
|
||||
# Revert: host left a daemon-enable group → disable and stop the service
|
||||
while IFS= read -r _OLD; do
|
||||
[[ -z "$_OLD" ]] && continue
|
||||
if ! _in_enable_list "$_OLD"; then
|
||||
_UNIT=$(_svc_unit "$_OLD")
|
||||
log "Reverting enable policy: disabling $_UNIT (host left daemon-enable group)"
|
||||
systemctl disable --now "$_UNIT" 2>/dev/null \
|
||||
|| warn "Failed to disable (revert) $_UNIT"
|
||||
fi
|
||||
done < "$DAEMON_ENABLE_STATE"
|
||||
|
||||
# Revert: host left a daemon-disable group → re-enable and start the service
|
||||
while IFS= read -r _OLD; do
|
||||
[[ -z "$_OLD" ]] && continue
|
||||
if ! _in_disable_list "$_OLD"; then
|
||||
_UNIT=$(_svc_unit "$_OLD")
|
||||
log "Reverting disable policy: enabling $_UNIT (host left daemon-disable group)"
|
||||
systemctl enable --now "$_UNIT" 2>/dev/null \
|
||||
|| warn "Failed to enable (revert) $_UNIT"
|
||||
fi
|
||||
done < "$DAEMON_DISABLE_STATE"
|
||||
|
||||
# Persist current state
|
||||
if [[ ${#ACTIVE_DAEMON_ENABLE[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${ACTIVE_DAEMON_ENABLE[@]}" | sort -u > "$DAEMON_ENABLE_STATE"
|
||||
else
|
||||
> "$DAEMON_ENABLE_STATE"
|
||||
fi
|
||||
if [[ ${#ACTIVE_DAEMON_DISABLE[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${ACTIVE_DAEMON_DISABLE[@]}" | sort -u > "$DAEMON_DISABLE_STATE"
|
||||
else
|
||||
> "$DAEMON_DISABLE_STATE"
|
||||
fi
|
||||
|
||||
log "Policy enforcement complete."
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
# Installs ansipa-enforce-policies.sh and a systemd timer that runs it every 30 minutes.
|
||||
# Policies are declared by adding hosts to the following FreeIPA host groups:
|
||||
#
|
||||
# policy-block-binary-<name> Block execution of <name> via a PATH-priority wrapper + AppArmor
|
||||
# policy-timeshift-backup Enforce daily Timeshift snapshots (03:00)
|
||||
# policy-security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans + SMB upload (02:00)
|
||||
# policy-scan-notify Fetch alerts from server, notify user every 10 min until acknowledged
|
||||
# policy-block-binary-<name> Block execution of <name> via a PATH-priority wrapper + AppArmor
|
||||
# policy-daemon-enable-<unit> Ensure <unit> is enabled and running; reverted when host leaves group
|
||||
# policy-daemon-disable-<unit> Ensure <unit> is disabled and stopped; reverted when host leaves group
|
||||
# policy-timeshift-backup Enforce daily Timeshift snapshots (03:00)
|
||||
# policy-security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans + SMB upload (02:00)
|
||||
# policy-scan-notify Fetch alerts from server, notify user every 10 min until acknowledged
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Host enrolled in FreeIPA (sssd + ipa CLI available)
|
||||
|
|
|
|||
Loading…
Reference in New Issue