#!/usr/bin/env bash # ansipa-fetch-alerts.sh — fetch security alerts from the server SMB share. # Runs as root every 10 minutes via ansipa-fetch-alerts.timer (policy-scan-notify). # # For each alert on the server that hasn't been acknowledged yet: # - Downloads it to ~/administration// for every active login session. # - A local file that has been deleted counts as acknowledged and is removed # from the server alerts directory on the next run. # # Prerequisites: # /etc/ansipa-smb.creds — Samba credentials file (deployed by deploy-ansipa-policies.yml) # /etc/ipa/default.conf — FreeIPA client config (provides server hostname) # smbclient — from the samba-client package set -euo pipefail LOG_TAG="ansipa-fetch-alerts" ADMIN_SUBDIR="administration" CREDS_FILE="/etc/ansipa-smb.creds" SMB_SHARE="ansipa-scans" STATE_DIR="/var/lib/ansipa-policies" FETCHED_STATE="$STATE_DIR/fetched-alerts" 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; } # ── Prerequisites ───────────────────────────────────────────────────────────── if [[ ! -f "$CREDS_FILE" ]]; then warn "Credentials file $CREDS_FILE not found — run deploy-ansipa-policies.yml first." exit 0 fi if ! command -v smbclient &>/dev/null; then warn "smbclient not installed — install samba-client." exit 0 fi IPA_SERVER=$(awk '/^server[[:space:]]*=/{print $3}' /etc/ipa/default.conf 2>/dev/null || echo "") if [[ -z "$IPA_SERVER" ]]; then warn "Cannot read IPA server from /etc/ipa/default.conf — host enrolled?" exit 0 fi HOSTNAME=$(hostname -f 2>/dev/null || hostname) mkdir -p "$STATE_DIR" touch "$FETCHED_STATE" smb() { smbclient "//$IPA_SERVER/$SMB_SHARE" -A "$CREDS_FILE" "$@" 2>/dev/null; } # ── List active login sessions ──────────────────────────────────────────────── ACTIVE_USERS=() while IFS= read -r LINE; do USER=$(echo "$LINE" | awk '{print $3}') [[ -z "$USER" || "$USER" == "root" ]] && continue HOME_DIR=$(getent passwd "$USER" | cut -d: -f6) || continue [[ -d "$HOME_DIR" ]] && ACTIVE_USERS+=("$USER:$HOME_DIR") done < <(loginctl list-sessions --no-legend 2>/dev/null || who 2>/dev/null || true) # Deduplicate by user. mapfile -t ACTIVE_USERS < <(printf '%s\n' "${ACTIVE_USERS[@]}" | sort -u) # ── List alerts on server for this host ─────────────────────────────────────── SERVER_ALERTS=() while IFS= read -r LINE; do # smbclient ls output: " filename.alert A 1234 date" FILE=$(echo "$LINE" | awk '{print $1}') [[ "$FILE" == *.alert ]] && SERVER_ALERTS+=("$FILE") done < <(smb -c "ls alerts\\$HOSTNAME\\" 2>/dev/null || true) # ── Check for locally deleted alerts (acknowledged) ─────────────────────────── while IFS= read -r ALERT_NAME; do [[ -z "$ALERT_NAME" ]] && continue # If none of the active users still have this alert file, it was acknowledged. ALL_DELETED=true for USER_INFO in "${ACTIVE_USERS[@]}"; do HOME_DIR="${USER_INFO#*:}" USER="${USER_INFO%%:*}" LOCAL_FILE="$HOME_DIR/$ADMIN_SUBDIR/$HOSTNAME/$ALERT_NAME" if [[ -f "$LOCAL_FILE" ]]; then ALL_DELETED=false break fi done if [[ "$ALL_DELETED" == true ]] && [[ ${#ACTIVE_USERS[@]} -gt 0 ]]; then log "Alert acknowledged (deleted locally): $ALERT_NAME — removing from server" smb -c "del alerts\\$HOSTNAME\\$ALERT_NAME" 2>/dev/null || true # Remove from state file. sed -i "/^$ALERT_NAME\$/d" "$FETCHED_STATE" 2>/dev/null || true fi done < "$FETCHED_STATE" # ── Download new/pending alerts to user home dirs ───────────────────────────── TMP_DIR=$(mktemp -d /tmp/ansipa-alerts.XXXXXX) trap 'rm -rf "$TMP_DIR"' EXIT for ALERT_NAME in "${SERVER_ALERTS[@]}"; do TMP_FILE="$TMP_DIR/$ALERT_NAME" # Download alert content from server. smb -c "get alerts\\$HOSTNAME\\$ALERT_NAME $TMP_FILE" 2>/dev/null || { warn "Failed to download alert: $ALERT_NAME" continue } NEW=false for USER_INFO in "${ACTIVE_USERS[@]}"; do HOME_DIR="${USER_INFO#*:}" USER="${USER_INFO%%:*}" LOCAL_DIR="$HOME_DIR/$ADMIN_SUBDIR/$HOSTNAME" LOCAL_FILE="$LOCAL_DIR/$ALERT_NAME" mkdir -p "$LOCAL_DIR" chown "$USER" "$LOCAL_DIR" 2>/dev/null || true if [[ ! -f "$LOCAL_FILE" ]]; then cp "$TMP_FILE" "$LOCAL_FILE" chown "$USER" "$LOCAL_FILE" chmod 644 "$LOCAL_FILE" NEW=true fi done # Track fetched alerts so we can detect acknowledgment on the next run. if ! grep -qx "$ALERT_NAME" "$FETCHED_STATE" 2>/dev/null; then echo "$ALERT_NAME" >> "$FETCHED_STATE" fi [[ "$NEW" == true ]] && log "New alert delivered: $ALERT_NAME" done log "Done. ${#SERVER_ALERTS[@]} server alert(s) for $HOSTNAME."