Merge pull request 'v0.6.2' (#11) from v0.6.2 into main

Reviewed-on: #11
This commit is contained in:
2026-03-24 10:31:03 +00:00
10 changed files with 54 additions and 14 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.ki-workspace

View File

@@ -128,6 +128,12 @@ REPORT_FORMAT="html"
# Mail-Befehl (z.B. "msmtp", "sendmail", "mail")
REPORT_MAIL_CMD="msmtp"
# Zeitraum für "Aktivster Tag" im Report (in Tagen)
# Bestimmt, über wie viele Tage zurück der aktivste Tag ermittelt wird.
# 30 = Aktivster Tag der letzten 30 Tage (empfohlen)
# 0 = Nur innerhalb des Berichtszeitraums (altes Verhalten)
REPORT_BUSIEST_DAY_RANGE=30
# --- Externe Blocklist (optional) ---
# Aktiviert den externen Blocklist-Worker
EXTERNAL_BLOCKLIST_ENABLED=false

View File

@@ -8,7 +8,7 @@
# Lizenz: MIT
###############################################################################
VERSION="v0.6.1"
VERSION="v0.6.2"
set -euo pipefail

View File

@@ -116,12 +116,12 @@ Bei Sperren aus der **externen Blocklist** werden Benachrichtigungen separat üb
### Service gestartet
**Überschrift:** ✅ AdGuard Shield
> 🟢 AdGuard Shield v0.6.0 wurde auf dns1 gestartet.
> 🟢 AdGuard Shield v0.6.2 wurde auf dns1 gestartet.
### Service gestoppt
**Überschrift:** 🚨 🛡️ AdGuard Shield
> 🔴 AdGuard Shield v0.6.0 wurde auf dns1 gestoppt.
> 🔴 AdGuard Shield v0.6.2 wurde auf dns1 gestoppt.
### Sperre (Ban)
**Überschrift:** 🚨 🛡️ AdGuard Shield

View File

@@ -139,6 +139,7 @@ Regelmäßige Statistik-Reports per E-Mail. Voraussetzung ist ein funktionierend
| `REPORT_EMAIL_FROM` | `adguard-shield@hostname` | E-Mail-Absender |
| `REPORT_FORMAT` | `html` | Format: `html` oder `txt` |
| `REPORT_MAIL_CMD` | `msmtp` | Mail-Befehl (`msmtp`, `sendmail`, `mail`) |
| `REPORT_BUSIEST_DAY_RANGE` | `30` | Zeitraum in Tagen für „Aktivster Tag“. `30` = letzte 30 Tage. `0` = nur Berichtszeitraum (altes Verhalten) |
> Siehe [E-Mail Report Dokumentation](report.md) für Details zu Inhalten, Templates und Befehlen.

View File

@@ -42,6 +42,7 @@ sudo /opt/adguard-shield/report-generator.sh install
| `REPORT_EMAIL_FROM` | `adguard-shield@hostname` | E-Mail-Absender |
| `REPORT_FORMAT` | `html` | Format: `html` oder `txt` |
| `REPORT_MAIL_CMD` | `msmtp` | Mail-Befehl (`msmtp`, `sendmail`, `mail`) |
| `REPORT_BUSIEST_DAY_RANGE` | `30` | Zeitraum in Tagen für „Aktivster Tag“ (0 = Berichtszeitraum) |
### Versandintervalle
@@ -86,7 +87,7 @@ Die übrigen Zeiträume laufen vom Starttag 00:00 Uhr bis zum Zeitpunkt der Repo
- Rate-Limit Sperren
- Subdomain-Flood Sperren
- Externe Blocklist Sperren
- Aktivster Tag im Berichtszeitraum
- Aktivster Tag wird über einen konfigurierbaren Zeitraum ermittelt (Standard: letzte 30 Tage, `REPORT_BUSIEST_DAY_RANGE`). Zeigt zusätzlich die Anzahl der Sperren an diesem Tag. Bei `REPORT_BUSIEST_DAY_RANGE=0` wird nur der Berichtszeitraum betrachtet.
### Top 10 Listen
- **Auffälligste IPs** — Die 10 IPs mit den meisten Sperren (mit Balkendiagramm im HTML-Format)
@@ -175,7 +176,8 @@ Die Templates verwenden Platzhalter (z.B. `{{TOTAL_BANS}}`, `{{TOP10_IPS_TABLE}}
| `{{RATELIMIT_BANS}}` | Rate-Limit Sperren |
| `{{SUBDOMAIN_FLOOD_BANS}}` | Subdomain-Flood Sperren |
| `{{EXTERNAL_BLOCKLIST_BANS}}` | Externe Blocklist Sperren |
| `{{BUSIEST_DAY}}` | Aktivster Tag |
| `{{BUSIEST_DAY}}` | Aktivster Tag (Datum + Anzahl Sperren) |
| `{{BUSIEST_DAY_LABEL}}` | Dynamisches Label für den aktivsten Tag (z.B. „Aktivster Tag (30 Tage)“) |
| `{{TOP10_IPS_TABLE}}` | Top 10 IPs (HTML-Tabelle) |
| `{{TOP10_IPS_TEXT}}` | Top 10 IPs (Text-Tabelle) |
| `{{TOP10_DOMAINS_TABLE}}` | Top 10 Domains (HTML-Tabelle) |

View File

@@ -6,7 +6,7 @@
# Lizenz: MIT
###############################################################################
VERSION="v0.6.1"
VERSION="v0.6.2"
set -euo pipefail

View File

@@ -41,6 +41,7 @@ REPORT_EMAIL_TO="${REPORT_EMAIL_TO:-}"
REPORT_EMAIL_FROM="${REPORT_EMAIL_FROM:-adguard-shield@$(hostname -f 2>/dev/null || hostname)}"
REPORT_FORMAT="${REPORT_FORMAT:-html}"
REPORT_MAIL_CMD="${REPORT_MAIL_CMD:-msmtp}"
REPORT_BUSIEST_DAY_RANGE="${REPORT_BUSIEST_DAY_RANGE:-30}"
BAN_HISTORY_FILE="${BAN_HISTORY_FILE:-/var/log/adguard-shield-bans.log}"
BAN_HISTORY_RETENTION_DAYS="${BAN_HISTORY_RETENTION_DAYS:-0}"
STATE_DIR="${STATE_DIR:-/var/lib/adguard-shield}"
@@ -325,6 +326,7 @@ calculate_stats() {
SUBDOMAIN_FLOOD_BANS=0
EXTERNAL_BLOCKLIST_BANS=0
BUSIEST_DAY=""
BUSIEST_DAY_LABEL="Aktivster Tag"
TOP10_IPS=""
TOP10_DOMAINS=""
PROTOCOL_STATS=""
@@ -333,16 +335,24 @@ calculate_stats() {
fi
# Einen einzigen awk-Pass über den Cache: alle Statistiken auf einmal
# Busiest-Day-Bereich berechnen (konfigurierbar, Standard: 30 Tage)
local busiest_start_epoch
if [[ "$REPORT_BUSIEST_DAY_RANGE" == "0" || -z "$REPORT_BUSIEST_DAY_RANGE" ]]; then
busiest_start_epoch="$start_epoch"
else
local today_midnight
today_midnight=$(get_today_midnight)
busiest_start_epoch=$((today_midnight - REPORT_BUSIEST_DAY_RANGE * 86400))
fi
local awk_result
awk_result=$(echo "$HISTORY_CACHE" | awk -F'|' -v s="$start_epoch" -v e="$end_epoch" '
awk_result=$(echo "$HISTORY_CACHE" | awk -F'|' -v s="$start_epoch" -v e="$end_epoch" -v bs="$busiest_start_epoch" '
$1 >= s && $1 <= e {
action = $3
if (action == "BAN") {
bans++
ip_count[$4]++
ip_seen[$4] = 1
day = substr($2, 1, 10)
day_count[day]++
dom = $5
if (dom != "" && dom != "-") dom_count[dom]++
proto = $8
@@ -359,11 +369,16 @@ calculate_stats() {
unbans++
}
}
# Aktivster Tag: separater Zeitraum (konfigurierbar, z.B. letzte 30 Tage)
$1 >= bs && $1 <= e && $3 == "BAN" {
bday = substr($2, 1, 10)
bday_count[bday]++
}
END {
for (ip in ip_seen) unique++
busiest = ""; max_d = 0
for (d in day_count) {
if (day_count[d] > max_d) { max_d = day_count[d]; busiest = d }
for (d in bday_count) {
if (bday_count[d] > max_d) { max_d = bday_count[d]; busiest = d; busiest_cnt = bday_count[d] }
}
print "BANS=" (bans+0)
print "UNBANS=" (unbans+0)
@@ -373,6 +388,7 @@ calculate_stats() {
print "SF=" (sf+0)
print "EB=" (eb+0)
print "BUSIEST=" busiest
print "BUSIEST_CNT=" (busiest_cnt+0)
for (ip in ip_count) print "IP\t" ip_count[ip] "\t" ip
for (d in dom_count) print "DOMAIN\t" dom_count[d] "\t" d
for (p in proto_count) print "PROTO\t" proto_count[p] "\t" p
@@ -395,12 +411,23 @@ calculate_stats() {
local busiest_raw
busiest_raw=$(echo "$awk_result" | awk -F= '$1=="BUSIEST" {print $2; exit}')
local busiest_cnt
busiest_cnt=$(echo "$awk_result" | awk -F= '$1=="BUSIEST_CNT" {print $2; exit}')
if [[ -n "$busiest_raw" ]]; then
BUSIEST_DAY=$(date -d "$busiest_raw" '+%d.%m.%Y' 2>/dev/null || echo "$busiest_raw")
local busiest_formatted
busiest_formatted=$(date -d "$busiest_raw" '+%d.%m.%Y' 2>/dev/null || echo "$busiest_raw")
BUSIEST_DAY="${busiest_formatted} (${busiest_cnt})"
else
BUSIEST_DAY=""
fi
# Dynamisches Label für den aktivsten Tag
if [[ "$REPORT_BUSIEST_DAY_RANGE" == "0" || -z "$REPORT_BUSIEST_DAY_RANGE" ]]; then
BUSIEST_DAY_LABEL="Aktivster Tag"
else
BUSIEST_DAY_LABEL="Aktivster Tag (${REPORT_BUSIEST_DAY_RANGE} Tage)"
fi
# Top-Listen: Tab-getrennte Felder sortieren und in das erwartete Format bringen
TOP10_IPS=$( echo "$awk_result" | awk -F'\t' '$1=="IP" {print $2 " " $3}' | sort -rn | head -10)
TOP10_DOMAINS=$(echo "$awk_result" | awk -F'\t' '$1=="DOMAIN" {print $2 " " $3}' | sort -rn | head -10)
@@ -786,6 +813,7 @@ generate_report() {
report="${report//\{\{SUBDOMAIN_FLOOD_BANS\}\}/$SUBDOMAIN_FLOOD_BANS}"
report="${report//\{\{EXTERNAL_BLOCKLIST_BANS\}\}/$EXTERNAL_BLOCKLIST_BANS}"
report="${report//\{\{BUSIEST_DAY\}\}/$BUSIEST_DAY}"
report="${report//\{\{BUSIEST_DAY_LABEL\}\}/$BUSIEST_DAY_LABEL}"
report="${report//\{\{TOP10_IPS_TABLE\}\}/$top10_ips_table}"
report="${report//\{\{TOP10_DOMAINS_TABLE\}\}/$top10_domains_table}"
report="${report//\{\{PROTOCOL_TABLE\}\}/$protocol_table}"
@@ -832,6 +860,7 @@ generate_report() {
report="${report//\{\{SUBDOMAIN_FLOOD_BANS\}\}/$SUBDOMAIN_FLOOD_BANS}"
report="${report//\{\{EXTERNAL_BLOCKLIST_BANS\}\}/$EXTERNAL_BLOCKLIST_BANS}"
report="${report//\{\{BUSIEST_DAY\}\}/$BUSIEST_DAY}"
report="${report//\{\{BUSIEST_DAY_LABEL\}\}/$BUSIEST_DAY_LABEL}"
report="${report//\{\{TOP10_IPS_TEXT\}\}/$top10_ips_txt}"
report="${report//\{\{TOP10_DOMAINS_TEXT\}\}/$top10_domains_txt}"
report="${report//\{\{PROTOCOL_TEXT\}\}/$protocol_txt}"
@@ -983,6 +1012,7 @@ show_cron_status() {
echo " Empfänger: ${REPORT_EMAIL_TO:-nicht konfiguriert}"
echo " Absender: ${REPORT_EMAIL_FROM}"
echo " Mail-Befehl: ${REPORT_MAIL_CMD}"
echo " Aktivster Tag: letzte ${REPORT_BUSIEST_DAY_RANGE:-30} Tage"
echo ""
if command -v "$REPORT_MAIL_CMD" &>/dev/null; then

View File

@@ -324,7 +324,7 @@
</div>
<div class="stat-card success">
<div class="stat-value">{{BUSIEST_DAY}}</div>
<div class="stat-label">Aktivster Tag</div>
<div class="stat-label">{{BUSIEST_DAY_LABEL}}</div>
</div>
</div>

View File

@@ -30,7 +30,7 @@
Rate-Limit Sperren: {{RATELIMIT_BANS}}
Subdomain-Flood Sperren: {{SUBDOMAIN_FLOOD_BANS}}
Externe Blocklist: {{EXTERNAL_BLOCKLIST_BANS}}
Aktivster Tag: {{BUSIEST_DAY}}
{{BUSIEST_DAY_LABEL}}: {{BUSIEST_DAY}}
───────────────────────────────────────────────────────────────
🏴‍☠️ TOP 10 AUFFÄLLIGSTE IPs