259 lines
7.1 KiB
Bash
Executable File
259 lines
7.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# checkms - Analyse von MS-Fehlercodes im Exim-Log + Code-Management
|
|
# Maintainer: ljp
|
|
|
|
set -euo pipefail
|
|
|
|
# --- Defaults ---
|
|
DATE="$(date +%Y-%m-%d)"
|
|
MINUTES=0
|
|
HOURS=0
|
|
CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/checkms"
|
|
ADD_CODES=()
|
|
DEL_INDEXES=()
|
|
SET_USER=""
|
|
SHOW_CONF=0
|
|
|
|
# --- Funktionen ---
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: $0 [OPTIONS] [YYYY-MM-DD]
|
|
|
|
Analyse von MS-Fehlercodes im Exim-Log und Pflege der Codes.
|
|
|
|
Analyse-Optionen:
|
|
--minutes N Analysiert nur die letzten N Minuten
|
|
--hours N Analysiert nur die letzten N Stunden
|
|
--confdir PATH Pfad zum Konfigurationsordner (Default: ~/.config/checkms)
|
|
|
|
Konfig-Optionen:
|
|
--add-code REGEX Fügt einen Code/Regex zur Config hinzu (mehrfach möglich)
|
|
--del-code ID Löscht Code mit ID (siehe --show-config)
|
|
--set-user USER Setzt den SSH-User in der Config
|
|
--show-config Zeigt die aktuelle Config (JSON + tabellarisch) und beendet
|
|
--help Diese Hilfe
|
|
USAGE
|
|
}
|
|
|
|
init_conf() {
|
|
mkdir -p "$CONF_DIR"
|
|
read -rp "Bitte SSH-User für den Logger angeben: " SSHUSER
|
|
cat >"$CONF_FILE" <<JSON
|
|
{
|
|
"ssh_user": "$SSHUSER",
|
|
"codes": [
|
|
"S3150",
|
|
"S775",
|
|
"S7771[0-9]*"
|
|
]
|
|
}
|
|
JSON
|
|
echo "Konfigurationsdatei erstellt: $CONF_FILE"
|
|
}
|
|
|
|
require_jq_write() {
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
echo "Fehler: Für --add-code/--del-code/--set-user wird 'jq' benötigt." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
show_config() {
|
|
if [[ ! -f "$CONF_FILE" ]]; then
|
|
echo "Keine Config vorhanden. Sie wird beim nächsten Run erstellt."
|
|
exit 0
|
|
fi
|
|
|
|
# Roh-JSON
|
|
if command -v jq >/dev/null 2>&1; then
|
|
echo "RAW ($CONF_FILE):"
|
|
jq . "$CONF_FILE"
|
|
else
|
|
echo "RAW ($CONF_FILE):"
|
|
cat "$CONF_FILE"
|
|
fi
|
|
echo
|
|
|
|
# Tabellarisch
|
|
if command -v jq >/dev/null 2>&1; then
|
|
SSH_USER_SHOW="$(jq -r '.ssh_user' "$CONF_FILE")"
|
|
mapfile -t CODES_SHOW < <(jq -r '.codes[]' "$CONF_FILE")
|
|
else
|
|
SSH_USER_SHOW="$(grep -o '"ssh_user":[^,}]*' "$CONF_FILE" | cut -d'"' -f4)"
|
|
CODES_LINE="$(sed -n 's/.*"codes":[[]\(.*\)[]].*/\1/p' "$CONF_FILE")"
|
|
IFS=',' read -r -a CODES_SHOW <<<"$(echo "$CODES_LINE" | tr -d ' \t"')"
|
|
fi
|
|
|
|
echo "TABELLARISCHE ANSICHT:"
|
|
printf "%-12s %s\n" "SSH_USER" "${SSH_USER_SHOW:-<unset>}"
|
|
echo
|
|
printf "%-6s %s\n" "ID" "CODE"
|
|
printf "%-6s %s\n" "----" "-----------------------"
|
|
id=0
|
|
for code in "${CODES_SHOW[@]}"; do
|
|
printf "%-6s %s\n" "$id" "$code"
|
|
id=$((id+1))
|
|
done
|
|
exit 0
|
|
}
|
|
|
|
apply_add_codes() {
|
|
[[ ${#ADD_CODES[@]} -eq 0 ]] && return 0
|
|
require_jq_write
|
|
local tmp; tmp="$(mktemp)"
|
|
cp "$CONF_FILE" "$tmp"
|
|
for code in "${ADD_CODES[@]}"; do
|
|
jq --arg code "$code" '
|
|
(.codes | any(. == $code)) as $exists
|
|
| if $exists then . else (.codes += [$code]) end
|
|
' "$tmp" > "${tmp}.new"
|
|
mv "${tmp}.new" "$tmp"
|
|
done
|
|
mv "$tmp" "$CONF_FILE"
|
|
}
|
|
|
|
apply_del_indexes() {
|
|
[[ ${#DEL_INDEXES[@]} -eq 0 ]] && return 0
|
|
require_jq_write
|
|
local tmp; tmp="$(mktemp)"
|
|
cp "$CONF_FILE" "$tmp"
|
|
IFS=$'\n' read -r -d '' -a sorted <<<"$(printf "%s\n" "${DEL_INDEXES[@]}" | sort -rn; printf '\0')"
|
|
for idx in "${sorted[@]}"; do
|
|
[[ "$idx" =~ ^[0-9]+$ ]] || { echo "Warnung: ungültige ID '$idx'"; continue; }
|
|
jq --argjson i "$idx" '
|
|
if ($i >= 0 and $i < (.codes|length))
|
|
then .codes |= del(.[$i])
|
|
else .
|
|
end
|
|
' "$tmp" > "${tmp}.new"
|
|
mv "${tmp}.new" "$tmp"
|
|
done
|
|
mv "$tmp" "$CONF_FILE"
|
|
}
|
|
|
|
apply_set_user() {
|
|
[[ -z "$SET_USER" ]] && return 0
|
|
require_jq_write
|
|
local tmp; tmp="$(mktemp)"
|
|
jq --arg u "$SET_USER" '.ssh_user = $u' "$CONF_FILE" > "$tmp"
|
|
mv "$tmp" "$CONF_FILE"
|
|
}
|
|
|
|
# --- Argumente parsen ---
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--minutes) MINUTES="$2"; shift 2 ;;
|
|
--hours) HOURS="$2"; shift 2 ;;
|
|
--confdir) CONF_DIR="$2"; shift 2 ;;
|
|
--show-config) SHOW_CONF=1; shift ;;
|
|
--add-code) ADD_CODES+=("$2"); shift 2 ;;
|
|
--del-code) DEL_INDEXES+=("$2"); shift 2 ;;
|
|
--set-user) SET_USER="$2"; shift 2 ;;
|
|
--help) usage; exit 0 ;;
|
|
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) DATE="$1"; shift ;;
|
|
*) echo "Unbekannte Option: $1" >&2; usage; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# --- Configpfade ---
|
|
CONF_FILE="${CONF_DIR}/checkms.conf"
|
|
[[ -f "$CONF_FILE" ]] || init_conf
|
|
|
|
# --- Reine Konfig-Operationen (frühzeitiger Exit!) ---
|
|
if [[ $SHOW_CONF -eq 1 && ${#ADD_CODES[@]} -eq 0 && ${#DEL_INDEXES[@]} -eq 0 && -z "$SET_USER" ]]; then
|
|
show_config
|
|
fi
|
|
|
|
# Wenn Codes/User geändert werden sollen, erst anwenden …
|
|
if [[ ${#ADD_CODES[@]} -gt 0 || ${#DEL_INDEXES[@]} -gt 0 || -n "$SET_USER" ]]; then
|
|
apply_add_codes
|
|
apply_del_indexes
|
|
apply_set_user
|
|
# Wenn nur Konfig bearbeitet wurde (keine Analyse-Flags/Datum), hier enden
|
|
if [[ $MINUTES -eq 0 && $HOURS -eq 0 && "$DATE" == "$(date +%Y-%m-%d)" ]]; then
|
|
# Optional: danach anzeigen
|
|
[[ $SHOW_CONF -eq 1 ]] && show_config
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# --- Config laden für Analyse ---
|
|
if command -v jq >/dev/null 2>&1; then
|
|
SSH_USER="$(jq -r '.ssh_user' "$CONF_FILE")"
|
|
CODES_REGEX="$(jq -r '.codes[]' "$CONF_FILE" | paste -sd'|' -)"
|
|
else
|
|
SSH_USER="$(grep -o '"ssh_user":[^,}]*' "$CONF_FILE" | cut -d'"' -f4)"
|
|
CODES_REGEX="$(
|
|
sed -n 's/.*"codes":[[]\(.*\)[]].*/\1/p' "$CONF_FILE" \
|
|
| tr -d ' \t"' | tr ',' '|'
|
|
)"
|
|
fi
|
|
[[ -n "${CODES_REGEX// }" ]] || CODES_REGEX="S3150|S775|S7771[0-9]*"
|
|
|
|
year="${DATE:0:4}"
|
|
month="${DATE:5:2}"
|
|
LOGFILE="${DATE}.log"
|
|
|
|
# --- Remote-Kommando ---
|
|
if type go >/dev/null 2>&1; then
|
|
REMOTE="go logger"
|
|
else
|
|
REMOTE="ssh ${SSH_USER}@logger.in.nessus.at"
|
|
fi
|
|
|
|
# --- Analyse (remote) ---
|
|
$REMOTE bash -s <<EOF
|
|
set -euo pipefail
|
|
cd /var/log/easyname/$year/$month
|
|
|
|
if [ -f "$LOGFILE" ]; then SRC="cat \"$LOGFILE\""
|
|
elif [ -f "$LOGFILE.gz" ]; then SRC="zcat \"$LOGFILE.gz\""
|
|
else
|
|
echo "Logfile $LOGFILE(.gz) nicht gefunden." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Analysiere $LOGFILE"
|
|
echo
|
|
printf "%-8s %-16s %-8s\n" "COUNT" "IP" "CODE"
|
|
printf "%-8s %-16s %-8s\n" "-----" "----------------" "------"
|
|
|
|
TF_CMD=""
|
|
if [ $MINUTES -gt 0 ]; then
|
|
START_TS=\$(date -d "-$MINUTES minutes" +"%b %_d %H:%M")
|
|
echo "Zeitraum: letzte $MINUTES Minuten (ab \$START_TS)"
|
|
TF_CMD="awk -v ts=\"\$START_TS\" '\$0 ~ ts,0'"
|
|
elif [ $HOURS -gt 0 ]; then
|
|
START_TS=\$(date -d "-$HOURS hours" +"%b %_d %H")
|
|
echo "Zeitraum: letzte $HOURS Stunden (ab \$START_TS)"
|
|
TF_CMD="awk -v ts=\"\$START_TS\" '\$0 ~ ts,0'"
|
|
fi
|
|
|
|
eval \$SRC \${TF_CMD:+| \$TF_CMD} \
|
|
| grep -E 'outlook|hotmail' \
|
|
| grep -E "$CODES_REGEX" \
|
|
| awk '
|
|
{
|
|
code="NA"; ip="NA";
|
|
for (i=1;i<=NF;i++) {
|
|
if (\$i ~ /\(S[0-9]+\)/) { t=\$i; gsub(/[()]/,"",t); sub(/\\.$/,"",t); code=t }
|
|
else if (\$i ~ /^S[0-9]+/) { code=\$i; sub(/\\.$/,"",code) }
|
|
if (\$i ~ /^\[[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) {
|
|
ip=\$i; gsub(/^\[|\].*$/,"",ip)
|
|
}
|
|
else if (ip == "NA" && \$i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) {
|
|
ip=\$i
|
|
}
|
|
}
|
|
if (ip ~ /^77\.244\.243\.(9|10|11|12)$/) {
|
|
next
|
|
}
|
|
print code, ip
|
|
}
|
|
' \
|
|
| sort | uniq -c | sort -nr \
|
|
| while read count code ip; do
|
|
printf "%-8s %-16s %-8s\n" "\$count" "\$ip" "\$code"
|
|
done
|
|
EOF
|