186 lines
7.4 KiB
Bash
Executable File
186 lines
7.4 KiB
Bash
Executable File
#!/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-<name> Block execution of <name> 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" <<WRAPPER
|
|
#!/bin/bash
|
|
# blocked by ansipa policy
|
|
echo "[$LOG_TAG] '$BIN' is blocked by system policy on this host." >&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."
|