#!/usr/bin/env bash # ansipa-enforce-policies.sh — enforce FreeIPA host-group-driven policies on this client. # # Policies are idempotent and reversible: joining a group applies the policy; # leaving the group removes it on the next run (every 30 min via systemd timer). # # Host-group naming conventions: # policy-block-binary- Block execution of via a wrapper in /usr/local/bin/ # policy-timeshift-backup Enforce a daily Timeshift snapshot (requires timeshift installed) # policy-security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans # # Notes: # - Binary blocking uses a PATH-priority wrapper in /usr/local/bin/; callers using # the full absolute path bypass it. For hard enforcement add AppArmor/SELinux rules. # - Install scan tools first: add the host to ansipa-module-anti-malware. # - Configure Timeshift (type + target device) before enabling policy-timeshift-backup. set -euo pipefail LOG_TAG="ansipa-policies" STATE_DIR="/var/lib/ansipa-policies" BLOCK_DIR="/usr/local/bin" CRON_DIR="/etc/cron.d" log() { echo "[$LOG_TAG] $*"; logger -t "$LOG_TAG" "$*" 2>/dev/null || true; } warn() { echo "[$LOG_TAG][WARN] $*" >&2; logger -t "$LOG_TAG" "WARN: $*" 2>/dev/null || true; } HOST_FQDN=$(hostname -f 2>/dev/null || hostname) if ! command -v ipa &>/dev/null; then warn "ipa command not found — host not enrolled in FreeIPA. Exiting." exit 0 fi kinit -k "host/$HOST_FQDN" &>/dev/null || true mkdir -p "$STATE_DIR" # ── Fetch host group membership ─────────────────────────────────────────────── RAW_GROUPS=$(ipa host-show "$HOST_FQDN" --all 2>/dev/null \ | grep -i "Member of host-groups:" | sed 's/.*: //' || true) # ── Parse active policy groups ──────────────────────────────────────────────── ACTIVE_BLOCK_BINARIES=() WANT_TIMESHIFT_BACKUP=false WANT_SECURITY_SCAN=false if [[ -n "$RAW_GROUPS" ]]; then while IFS=',' read -ra GRP_ARRAY; do for g in "${GRP_ARRAY[@]}"; do g="${g// /}" case "$g" in policy-block-binary-*) ACTIVE_BLOCK_BINARIES+=("${g#policy-block-binary-}") ;; policy-timeshift-backup) WANT_TIMESHIFT_BACKUP=true ;; policy-security-scan) WANT_SECURITY_SCAN=true ;; esac done done <<< "$RAW_GROUPS" fi log "Active policies — block-binary: ${ACTIVE_BLOCK_BINARIES[*]:-none}" \ "| timeshift-backup: $WANT_TIMESHIFT_BACKUP | security-scan: $WANT_SECURITY_SCAN" # ── Helper ──────────────────────────────────────────────────────────────────── in_active_list() { local needle="$1" for b in "${ACTIVE_BLOCK_BINARIES[@]}"; do [[ "$b" == "$needle" ]] && return 0 done return 1 } # ── Binary blocking ─────────────────────────────────────────────────────────── # Wrapper scripts are placed in /usr/local/bin/ (higher PATH priority than /usr/bin/). # The "blocked by ansipa policy" sentinel line lets us identify managed wrappers. BLOCK_STATE="$STATE_DIR/blocked-binaries" [[ -f "$BLOCK_STATE" ]] || touch "$BLOCK_STATE" for BIN in "${ACTIVE_BLOCK_BINARIES[@]}"; do WRAPPER="$BLOCK_DIR/$BIN" if [[ ! -f "$WRAPPER" ]] || ! grep -q "blocked by ansipa policy" "$WRAPPER" 2>/dev/null; then log "Applying block: $BIN" cat > "$WRAPPER" <&2 exit 1 WRAPPER chmod 755 "$WRAPPER" fi done # Remove wrappers for binaries no longer in any active policy group. while IFS= read -r OLD_BIN; do [[ -z "$OLD_BIN" ]] && continue if ! in_active_list "$OLD_BIN"; then WRAPPER="$BLOCK_DIR/$OLD_BIN" if [[ -f "$WRAPPER" ]] && grep -q "blocked by ansipa policy" "$WRAPPER" 2>/dev/null; then rm -f "$WRAPPER" log "Removed block: $OLD_BIN" fi fi done < "$BLOCK_STATE" # Persist current blocked list. if [[ ${#ACTIVE_BLOCK_BINARIES[@]} -gt 0 ]]; then printf '%s\n' "${ACTIVE_BLOCK_BINARIES[@]}" | sort -u > "$BLOCK_STATE" else > "$BLOCK_STATE" fi # ── Timeshift daily backup ───────────────────────────────────────────────────── TIMESHIFT_CRON="$CRON_DIR/ansipa-timeshift-backup" if [[ "$WANT_TIMESHIFT_BACKUP" == true ]]; then if [[ ! -f "$TIMESHIFT_CRON" ]]; then if ! command -v timeshift &>/dev/null; then warn "timeshift not found — add host to ansipa-module-timeshift first. Cron will be installed anyway." fi log "Enabling daily Timeshift backups" cat > "$TIMESHIFT_CRON" <<'CRON' # ansipa-policy-timeshift-backup: managed by ansipa-enforce-policies — do not edit manually. # Timeshift must be configured on this host (type + target device) before snapshots work. 0 3 * * * root /usr/bin/timeshift --create --comments "ansipa-daily" --tags D 2>&1 | logger -t timeshift-backup CRON chmod 644 "$TIMESHIFT_CRON" fi else if [[ -f "$TIMESHIFT_CRON" ]]; then rm -f "$TIMESHIFT_CRON" log "Removed Timeshift backup cron (host left policy-timeshift-backup group)" fi fi # ── Security scan ───────────────────────────────────────────────────────────── SCAN_CRON="$CRON_DIR/ansipa-security-scan" SCAN_SCRIPT="/usr/local/bin/ansipa-security-scan.sh" if [[ "$WANT_SECURITY_SCAN" == true ]]; then # (Re-)write the scan script so it stays current with this version of the enforcer. cat > "$SCAN_SCRIPT" <<'SCAN' #!/bin/bash # ansipa-security-scan — daily ClamAV / rkhunter / chkrootkit run. # Managed by ansipa-enforce-policies — do not edit manually. LOG=/var/log/ansipa-security-scan.log { echo "=== ansipa-security-scan: $(date) ===" if command -v freshclam &>/dev/null; then freshclam --quiet 2>/dev/null || true fi if command -v clamscan &>/dev/null; then clamscan -r --infected --quiet /home /etc /tmp /var/tmp 2>/dev/null || true fi if command -v rkhunter &>/dev/null; then rkhunter --update --quiet 2>/dev/null || true rkhunter --check --skip-keypress --quiet 2>/dev/null || true fi if command -v chkrootkit &>/dev/null; then chkrootkit 2>/dev/null || true fi echo "=== scan complete ===" } >> "$LOG" 2>&1 SCAN chmod 755 "$SCAN_SCRIPT" if [[ ! -f "$SCAN_CRON" ]]; then log "Enabling daily security scans (ClamAV / rkhunter / chkrootkit)" cat > "$SCAN_CRON" <<'CRON' # ansipa-policy-security-scan: managed by ansipa-enforce-policies — do not edit manually. # Install scan tools by adding the host to the ansipa-module-anti-malware group. 0 2 * * * root /usr/local/bin/ansipa-security-scan.sh CRON chmod 644 "$SCAN_CRON" fi else if [[ -f "$SCAN_CRON" ]]; then rm -f "$SCAN_CRON" rm -f "$SCAN_SCRIPT" log "Removed security scan policy (host left policy-security-scan group)" fi fi log "Policy enforcement complete."