129 lines
5.3 KiB
Bash
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."
|