Dotfiles/setup/modules/FreeipaAnsible/ansible/ansipa-fetch-alerts.sh

129 lines
5.3 KiB
Bash

#!/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/<hostname>/ 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."