Compare commits

...

59 Commits

Author SHA1 Message Date
d8979faa06 install_update_dovecot-2.4.sh: increase sieve_max_redirects to 25. 2025-12-05 12:35:07 +01:00
d407b2ecf0 install_opendkim.sh,install_opendmarc.sh: Support the execution of scripts at any time. A non-existent configuration is created, and an already installed configuration is adjusted if necessary. 2025-11-26 12:36:56 +01:00
13e1d575f9 install_opendmarc.sh: remove duplicate setting 'AuthservID'. 2025-11-26 11:33:58 +01:00
a246f5009f install_opendmarc.sh: some minor changes. 2025-11-25 22:43:11 +01:00
41f5f61d32 Merge branch 'master' of https://git.oopen.de/install/mailsystem 2025-11-25 22:39:47 +01:00
eb18cf2a84 Fix errors handling DKIM signatures and DMARC proofs. 2025-11-25 22:34:40 +01:00
578db07b76 README.dovecot.index: modify documentation. 2025-11-23 12:23:49 +01:00
3b3652ec55 DOC/DMARC-Report/dmarc-server-setup.md: fix errors in documentation. 2025-11-13 22:21:28 +01:00
44c56d6083 Merge branch 'master' of git.oopen.de:install/mailsystem 2025-11-13 00:28:17 +01:00
8e9f35ed76 dmarc-server-setup.md: Adjust .. 2025-11-13 00:27:35 +01:00
0303c79b03 install_postfix_advanced.sh: fix permissions to service scripts for DMARC reporting. 2025-11-13 00:19:15 +01:00
20d4c95404 install_postfix_advanced.sh: add support for DMARC reporting. 2025-11-12 23:59:59 +01:00
3b60d1b83e Add documentation and service scripts for DMARC reporting. 2025-11-12 17:22:11 +01:00
cb9e958d39 install_update_dovecot-2.4.sh: fix errors Correct errors relating to database access. 2025-08-23 17:28:28 +02:00
761a3a3b34 Merge branch 'master' of git.oopen.de:install/mailsystem 2025-08-23 11:49:42 +02:00
ae06305069 install_update_dovecot-2.4.sh: Set 'quota_storage_size' to 9 (unlimited) - We only use personalized quotas. 2025-08-23 11:49:29 +02:00
e50851b519 install_update_dovecot-2.4.sh: fix errors concerning mail location settings of shared folders, 2025-08-23 02:19:37 +02:00
c82ff77bbe Add DOC/dovecot/README.maildir-size-fix.pl. 2025-08-22 00:18:41 +02:00
f4c7453675 install_update_dovecot-2.4.sh: add parameter 'maildir_broken_filename_sizes = yes' and 'auth_allow_weak_schemes = yes'. 2025-08-21 23:38:55 +02:00
06914fcade install_postfix_advanced.sh: add parameters for 'Postfix DSN Support' to file 'main.cf'. 2025-08-21 00:06:17 +02:00
230ab94ca6 install_update_dovecot-2.4.sh: fix quota relating errors. 2025-08-14 12:43:33 +02:00
7cc2369ea0 install_update_dovecot-2.4.sh: initial versionm. 2025-08-14 01:41:47 +02:00
4aaeae3b1a install_update_dovecot-2.4.sh: another intermediate version. 2025-08-13 01:54:56 +02:00
692e76ad1d install_update_dovecot-2.4.sh: another intermediate version. 2025-08-12 02:03:37 +02:00
6f02a45023 Rename 'README.test_mailprotocolls' to 'README.test_mailprotocols'. 2025-08-03 11:01:44 +02:00
e971f26c75 README.test_mailprotocolls: adjust / expand documentation. 2025-08-03 11:00:31 +02:00
bbc3cf87f1 install_update_dovecot-2.4.sh: another intermediate version. 2025-07-23 01:57:24 +02:00
195e3f65ee install_update_dovecot-2.4.sh: another imtermediate update. 2025-07-22 03:01:11 +02:00
0270997761 install_update_dovecot-2.4.sh: another imtermediate update 2025-07-20 13:19:59 +02:00
6e19d1a938 install_update_dovecot-2.4.sh: another intermediate version. 2025-07-19 01:46:06 +02:00
5a9dcc4b8f install_update_dovecot-2.4.sh: another intermediate version. 2025-07-18 02:31:55 +02:00
94a1895873 install_update_dovecot-2.4.sh: another intermediate version. 2025-07-14 15:17:45 +02:00
e00ba6f4ce install_update_dovecot-2.4.sh: intermediate version. 2025-07-14 01:41:53 +02:00
845325eac8 ignore '.env' files. 2025-07-14 01:41:03 +02:00
ba20cb36fe Add initial not yet running installscript for dovecot version 2.4.x 2025-07-12 12:55:16 +02:00
83ad91f77d install_update_dovecot.sh:cleanup fron debug code.. 2025-07-12 12:54:08 +02:00
3a98ac15f7 Merge branch 'master' of https://git.oopen.de/install/mailsystem 2025-07-12 11:10:26 +02:00
d811cbfbd1 Backup 'BAK/install_update_dovecot.sh' bevor uploading a new one. 2025-07-12 11:10:17 +02:00
5eebd200f4 install_update_dovecot.sh: some minor changes. 2025-07-12 11:07:57 +02:00
14ae5a3ebf install_postfix_advanced.sh: workarround for error: the query to zen.spamhaus.org was blocked due to usage of an open resolver. 2025-05-15 02:59:51 +02:00
e24fb4cad3 install_postfix_advanced.sh: fix error in creating master.cf (+policy-spf). 2025-05-14 11:18:56 +02:00
cc06fe5cfa install_amavis.sh: fix errors in if statements: replace ':' with ';'. 2025-05-14 10:26:50 +02:00
4442c6230e install_postfix_advanced.s - policyd-spf h: increase 'void lookup limit'fro '2' to '5'. 2025-04-29 17:09:27 +02:00
3f141499dc install_postfix_base.sh: update file 'sasl_passwd' instead ao renewing it. 2025-03-12 14:43:41 +01:00
9b12e32853 install_amavis.sh: Moving SPAM policies into an external file. 2025-03-03 17:57:22 +01:00
894ff4eced install_opendmarc.sh: adjust configuration file. 2025-03-03 15:42:04 +01:00
99b1205d1b install_opendkim.sh: adjust configuration file. 2025-03-03 15:41:40 +01:00
ae2b6540af install_postfix_advanced.sh: add support for postfix-policyd-spf-python . 2025-03-02 02:17:25 +01:00
6cc1848e45 install_opendkim.sh: Add an Authentication-Results: header field. 2025-02-26 12:26:19 +01:00
07231ac1c7 install_postfix_advanced.sh: ix.dnsbl.manitu.net (also known as NixSpam) has been shutdown on 2025-01-16. 2025-02-14 14:29:14 +01:00
Christoph
7b6e4c36d0 install_amavis.sh,install_postfix_advanced.sh: support additional smtp port in case of relay host. 2025-01-27 18:50:01 +01:00
ee41a335b1 install_postfix_base.sh: some really minor changes.. 2025-01-27 17:48:36 +01:00
0b410ad6d8 install_postfix_base.sh: support relaying to non standard port (other than 25). 2025-01-27 17:02:08 +01:00
aa092ea841 install_postfixadmin.sh: orioginal script now has the right charset (utf8). 2025-01-05 01:15:39 +01:00
da6c7fca0e install_postfix_base.sh: fix error if IPv4 address is a loopback address (127.x). 2024-12-18 12:04:04 +01:00
f1f56f48f6 install_amavis.sh: fix error in if statement. 2024-10-01 17:27:21 +02:00
765b16fd59 Merge branch 'master' of git.oopen.de:install/mailsystem 2024-10-01 00:21:35 +02:00
6a34a5b74c README.test_mailprotocolls: add test of RSA based TLS connection. 2024-10-01 00:21:16 +02:00
ad1d844b54 install_postfix_advanced.sh: some minor changes in writing 'main.cf'. 2024-10-01 00:20:04 +02:00
19 changed files with 18660 additions and 292 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
*.log *.log
*.swp *.swp
conf/*.conf conf/*.conf
conf/*.env
# - Postfixadmin # - Postfixadmin
postfixadmin-* postfixadmin-*

4554
BAK/install_update_dovecot.sh.00 Executable file

File diff suppressed because it is too large Load Diff

14
DOC/DMARC-Report/daily-run.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
BASE="/var/lib/dmarc"
TODAY_DIR="$BASE/reports/$(date -u +%Y/%m/%d)"
OUTDIR="$BASE/exports"
CSV="$OUTDIR/records.csv"
LOGF="$BASE/logs/scan-$(date -u +%F).log"
mkdir -p "$OUTDIR"
if [[ -d "$TODAY_DIR" ]]; then
/usr/local/bin/dmarc-scan.sh "$TODAY_DIR" --domain fluechtlingsrat-brandenburg.de --csv "$CSV" --append --top 25 --outdir "$OUTDIR" >> "$LOGF" 2>&1
fi

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail
BASE="/var/lib/dmarc"
INBOX="$BASE/reports"
PROC="$BASE/processed"
LOGF="$BASE/logs/collector.log"
umask 027
TMPDIR="$(mktemp -d)"
EML="$TMPDIR/mail.eml"
cat > "$EML"
ripmime --no-nameless --name-by-type --overwrite -i "$EML" -d "$TMPDIR" >>"$LOGF" 2>&1 || true
TODAY="$(date -u +%Y/%m/%d)"
OUTDIR="$INBOX/$TODAY"
mkdir -p "$OUTDIR"
moved=0
shopt -s nullglob
for f in "$TMPDIR"/*; do
case "$f" in
*.xml|*.XML|*.gz|*.zip)
sha="$(sha256sum "$f" | awk '{print $1}')"
base="$(basename "$f")"
dst="$OUTDIR/$(date -u +%Y%m%dT%H%M%SZ)_${sha:0:12}_$base"
mv "$f" "$dst"
echo "$(date -Is) stored $dst" >> "$LOGF"
moved=$((moved+1))
;;
*) : ;;
esac
done
mkdir -p "$PROC"
mv "$EML" "$PROC/$(date -u +%Y%m%dT%H%M%SZ)_mail.eml" || true
rm -rf "$TMPDIR"
if (( moved > 0 )); then
exit 0
else
echo "$(date -Is) no usable attachment in message" >> "$LOGF"
exit 0
fi

View File

@@ -0,0 +1,382 @@
# DMARC-Auswertungsskript `dmarc-scan.sh` Installation, Beschreibung & Verwendung
## 📖 Zweck
`dmarc-scan.sh` analysiert DMARC-Aggregatberichte (XML, .zip, .gz) und erzeugt:
- gut lesbare Tabellen im Terminal,
- eine fortschreibbare Records-CSV (`--append`),
- Top-Listen nach IPs (gesamt und je Fail-Kategorie) als CSV.
---
## ⚙️ Installation
```bash
sudo apt install -y xmlstarlet unzip gzip
sudo tee /usr/local/bin/dmarc-scan.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
#
# dmarc-scan.sh — DMARC-XML-Reports (auch .gz/.zip) einlesen, tabellarisch anzeigen,
# Records als CSV exportieren (append optional), Top-IPs ermitteln
# und Top-Listen als CSV schreiben.
#
# Nutzung:
# ./dmarc-scan.sh /pfad/zu/reports \
# [--domain DOMAIN] \
# [--csv pfad/zur/records.csv] \
# [--append] \
# [--top N] \
# [--outdir pfad/zum/ordner]
#
# Beispiele:
# ./dmarc-scan.sh /var/mail/dmarc
# ./dmarc-scan.sh /var/mail/dmarc --domain fluechtlingsrat-brandenburg.de --csv dmarc.csv --append --top 15 --outdir ./export
#
# Voraussetzungen: xmlstarlet, unzip (für .zip), gzip (für .gz)
# Debian/Ubuntu: sudo apt-get install xmlstarlet unzip gzip
#
set -euo pipefail
REPORT_DIR="${1:-}"
shift || true
# Defaults
WANT_DOMAIN=""
CSV_PATH="./dmarc-summary.csv"
APPEND=0
TOP_N=10
OUTDIR="."
# Arg-Parsing (einfach)
while [[ $# -gt 0 ]]; do
case "${1:-}" in
--domain)
WANT_DOMAIN="${2:-}"; shift 2 || true ;;
--csv)
CSV_PATH="${2:-}"; shift 2 || true ;;
--append)
APPEND=1; shift || true ;;
--top)
TOP_N="${2:-10}"; shift 2 || true ;;
--outdir)
OUTDIR="${2:-.}"; shift 2 || true ;;
*)
# Unbekannte Option ignorieren
shift || true ;;
esac
done
if [[ -z "$REPORT_DIR" || ! -d "$REPORT_DIR" ]]; then
echo "Fehler: Bitte ein Verzeichnis mit DMARC-Reports angeben."
echo "Beispiel: $0 /var/mail/dmarc --domain fluechtlingsrat-brandenburg.de --csv dmarc.csv --append --top 15 --outdir ./export"
exit 1
fi
if ! command -v xmlstarlet >/dev/null 2>&1; then
echo "Fehler: xmlstarlet nicht gefunden. Bitte installieren (z.B. 'sudo apt-get install xmlstarlet')."
exit 1
fi
mkdir -p "$OUTDIR"
# CSV-Header für Records; bei --append nur schreiben, wenn Datei noch nicht existiert
RECORDS_HEADER="report_org,policy_domain,begin_utc,end_utc,source_ip,count,disposition,spf,dkim,header_from"
ensure_records_header() {
if [[ "$APPEND" -eq 1 ]]; then
if [[ ! -f "$CSV_PATH" ]]; then
echo "$RECORDS_HEADER" > "$CSV_PATH"
fi
else
echo "$RECORDS_HEADER" > "$CSV_PATH"
fi
}
ensure_records_header
# Zähler & Sets
declare -A SEEN_IPS=()
declare -A IP_COUNTS=() # Summe pro IP
declare -A SPF_ONLY_FAIL_IP=() # nur SPF fail pro IP
declare -A DKIM_ONLY_FAIL_IP=() # nur DKIM fail pro IP
declare -A BOTH_FAIL_IP=() # SPF+DKIM fail pro IP
total_msgs=0
pass_msgs=0
fail_msgs=0
spf_only_fail=0
dkim_only_fail=0
both_fail=0
# Hilfsfunktion: Epoch -> Datum (UTC)
epoch2date() {
local e="$1"
if [[ -z "$e" ]]; then printf "-"; return; fi
date -u -d @"$e" +"%Y-%m-%d %H:%M:%S UTC" 2>/dev/null || printf "%s" "$e"
}
# CSV-escape (Felder in Anführungszeichen, doppelte Anführungszeichen verdoppeln)
csv_escape() {
local s="${1:-}"
s="${s//\"/\"\"}"
printf "\"%s\"" "$s"
}
# Eine einzelne XML-Datei parsen
parse_xml() {
local xml_input="$1"
# Domain aus policy_published, ggf. für Filter
local domain
domain=$(xmlstarlet sel -T -t -v "/feedback/policy_published/domain" "$xml_input" 2>/dev/null || true)
if [[ -n "$WANT_DOMAIN" && "$domain" != "$WANT_DOMAIN" ]]; then
return 0
fi
local org begin end pol sp aspf adkim
org=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/org_name" "$xml_input" 2>/dev/null || printf "-")
begin=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/date_range/begin" "$xml_input" 2>/dev/null || printf "")
end=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/date_range/end" "$xml_input" 2>/dev/null || printf "")
pol=$(xmlstarlet sel -T -t -v "/feedback/policy_published/p" "$xml_input" 2>/dev/null || printf "-")
sp=$(xmlstarlet sel -T -t -v "/feedback/policy_published/sp" "$xml_input" 2>/dev/null || printf "-")
aspf=$(xmlstarlet sel -T -t -v "/feedback/policy_published/aspf" "$xml_input" 2>/dev/null || printf "-")
adkim=$(xmlstarlet sel -T -t -v "/feedback/policy_published/adkim" "$xml_input" 2>/dev/null || printf "-")
# Report-Header
echo "=============================================================================="
echo "Report von: ${org}"
echo "Domain (Policy): ${domain} (p=${pol}, sp=${sp}, aspf=${aspf}, adkim=${adkim})"
echo "Zeitraum: $(epoch2date "$begin") $(epoch2date "$end")"
echo "------------------------------------------------------------------------------"
printf "%-16s %7s %-10s %-6s %-6s %s\n" "Source IP" "Count" "Disposition" "SPF" "DKIM" "Header-From"
echo "------------------------------------------------------------------------------"
# Alle <record>-Einträge tabellarisch ausgeben
while IFS='|' read -r ip cnt dispo spfres dkimres hfrom; do
[[ -z "$ip$cnt$dispo$spfres$dkimres$hfrom" ]] && continue
local n=0
if [[ -n "${cnt:-}" && "$cnt" =~ ^[0-9]+$ ]]; then n="$cnt"; fi
total_msgs=$(( total_msgs + n ))
[[ -n "$ip" ]] && SEEN_IPS["$ip"]=1
[[ -n "$ip" ]] && IP_COUNTS["$ip"]=$(( ${IP_COUNTS["$ip"]:-0} + n ))
if [[ "${spfres:-}" == "pass" && "${dkimres:-}" == "pass" ]]; then
pass_msgs=$(( pass_msgs + n ))
else
fail_msgs=$(( fail_msgs + n ))
if [[ "${spfres:-}" != "pass" && "${dkimres:-}" == "pass" ]]; then
spf_only_fail=$(( spf_only_fail + n ))
[[ -n "$ip" ]] && SPF_ONLY_FAIL_IP["$ip"]=$(( ${SPF_ONLY_FAIL_IP["$ip"]:-0} + n ))
elif [[ "${spfres:-}" == "pass" && "${dkimres:-}" != "pass" ]]; then
dkim_only_fail=$(( dkim_only_fail + n ))
[[ -n "$ip" ]] && DKIM_ONLY_FAIL_IP["$ip"]=$(( ${DKIM_ONLY_FAIL_IP["$ip"]:-0} + n ))
else
both_fail=$(( both_fail + n ))
[[ -n "$ip" ]] && BOTH_FAIL_IP["$ip"]=$(( ${BOTH_FAIL_IP["$ip"]:-0} + n ))
fi
fi
printf "%-16s %7s %-10s %-6s %-6s %s\n" "${ip:--}" "${n}" "${dispo:--}" "${spfres:--}" "${dkimres:--}" "${hfrom:--}"
local begin_human end_human
begin_human="$(epoch2date "$begin")"
end_human="$(epoch2date "$end")"
{
csv_escape "$org"; printf ","
csv_escape "$domain"; printf ","
csv_escape "$begin_human"; printf ","
csv_escape "$end_human"; printf ","
csv_escape "${ip:-}"; printf ","
printf "%s," "$n"
csv_escape "${dispo:-}"; printf ","
csv_escape "${spfres:-}"; printf ","
csv_escape "${dkimres:-}"; printf ","
csv_escape "${hfrom:-}"; printf "\n"
} >> "$CSV_PATH"
done < <(xmlstarlet sel -T -t \
-m "/feedback/record" \
-v "row/source_ip" -o "|" \
-v "row/count" -o "|" \
-v "row/policy_evaluated/disposition" -o "|" \
-v "row/policy_evaluated/spf" -o "|" \
-v "row/policy_evaluated/dkim" -o "|" \
-v "identifiers/header_from" -n \
"$xml_input" 2>/dev/null)
echo
}
# Alle Dateien im Verzeichnis verarbeiten
shopt -s nullglob
for f in "$REPORT_DIR"/*; do
case "$f" in
*.xml)
parse_xml "$f"
;;
*.gz)
if command -v gzip >/dev/null 2>&1; then
tmp="$(mktemp)"
if gzip -cd "$f" > "$tmp"; then
parse_xml "$tmp"
else
echo "Warnung: Konnte $f nicht entpacken."
fi
rm -f "$tmp"
else
echo "Warnung: gzip nicht verfügbar, überspringe $f"
fi
;;
*.zip)
if command -v unzip >/dev/null 2>&1; then
tmpdir="$(mktemp -d)"
if unzip -qq -j "$f" '*.xml' -d "$tmpdir" >/dev/null 2>&1; then
for x in "$tmpdir"/*.xml; do
[[ -e "$x" ]] || continue
parse_xml "$x"
done
else
echo "Warnung: Konnte $f nicht entpacken (oder keine XML darin)."
fi
rm -rf "$tmpdir"
else
echo "Warnung: unzip nicht verfügbar, überspringe $f"
fi
;;
*) : ;;
esac
done
# Zusammenfassung
unique_ips=${#SEEN_IPS[@]}
echo "=============================================================================="
echo "GESAMT-ZUSAMMENFASSUNG"
echo "Nachrichten gesamt: $total_msgs"
echo "Eindeutige Source-IPs: $unique_ips"
echo "Alle Auth PASS: $pass_msgs"
echo "SPF/DKIM Fehler gesamt: $fail_msgs"
echo " ├─ nur SPF-Fail: $spf_only_fail"
echo " ├─ nur DKIM-Fail: $dkim_only_fail"
echo " └─ SPF+DKIM-Fail: $both_fail"
[[ -n "$WANT_DOMAIN" ]] && echo "Gefilterte Domain: $WANT_DOMAIN"
echo "Records-CSV: $CSV_PATH"
echo "Hinweis: 'Fail' umfasst Records, bei denen SPF oder DKIM nicht 'pass' war."
# Top-Listen auf STDOUT
echo
echo "TOP $TOP_N IPs nach Anzahl (über alle Reports):"
{
for ip in "${!IP_COUNTS[@]}"; do
printf "%10d %s\n" "${IP_COUNTS[$ip]}" "$ip"
done
} | sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
if (( fail_msgs > 0 )); then
echo
echo "Top-IPs nur SPF-Fail:"
{ for ip in "${!SPF_ONLY_FAIL_IP[@]}"; do printf "%10d %s\n" "${SPF_ONLY_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
echo
echo "Top-IPs nur DKIM-Fail:"
{ for ip in "${!DKIM_ONLY_FAIL_IP[@]}"; do printf "%10d %s\n" "${DKIM_ONLY_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
echo
echo "Top-IPs SPF+DKIM-Fail:"
{ for ip in "${!BOTH_FAIL_IP[@]}"; do printf "%10d %s\n" "${BOTH_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
fi
# ---- CSV-Exporte der Top-Listen ---------------------------------------------
write_top_csv () {
local outfile="$1"; shift
local -n assoc="$1" # name reference auf assoziatives Array
echo "ip,count" > "$outfile"
if [[ "${#assoc[@]}" -eq 0 ]]; then
: # leer
else
for ip in "${!assoc[@]}"; do
printf "%s,%s\n" "$ip" "${assoc[$ip]}"
done | sort -t, -k2,2nr > "$outfile"
fi
}
# Gesamt-Top-IPs
write_top_csv "$OUTDIR/top_ips.csv" IP_COUNTS
# Top-Listen nach Fail-Kategorien
write_top_csv "$OUTDIR/top_spf_fail_ips.csv" SPF_ONLY_FAIL_IP
write_top_csv "$OUTDIR/top_dkim_fail_ips.csv" DKIM_ONLY_FAIL_IP
write_top_csv "$OUTDIR/top_both_fail_ips.csv" BOTH_FAIL_IP
echo
echo "Exportierte Top-CSV-Dateien:"
echo " $OUTDIR/top_ips.csv"
echo " $OUTDIR/top_spf_fail_ips.csv"
echo " $OUTDIR/top_dkim_fail_ips.csv"
echo " $OUTDIR/top_both_fail_ips.csv"
EOF
sudo chmod 750 /usr/local/bin/dmarc-scan.sh
```
---
## 🧩 Verwendung
Grundbeispiel:
```bash
dmarc-scan.sh /var/lib/dmarc/reports/2025/11/12
```
Mit Optionen:
```bash
dmarc-scan.sh /var/lib/dmarc/reports/2025/11/12 --domain fluechtlingsrat-brandenburg.de --csv /var/lib/dmarc/exports/records.csv --append --top 25 --outdir /var/lib/dmarc/exports/
```
### Parameterübersicht
| Parameter | Bedeutung |
|------------|------------|
| `--domain <domain>` | Filtert Berichte auf bestimmte Domain |
| `--csv <pfad>` | Pfad zur Ausgabedatei (Records-CSV) |
| `--append` | Bestehende CSV fortschreiben statt überschreiben |
| `--top <N>` | Anzahl der angezeigten Top-IPs |
| `--outdir <pfad>` | Zielverzeichnis für Top-Listen (CSV) |
---
## 📊 Ausgabedateien
| Datei | Inhalt |
|-------|---------|
| `records.csv` | Alle Einzel-Records |
| `top_ips.csv` | IPs mit den meisten Mails |
| `top_spf_fail_ips.csv` | IPs mit nur SPF-Fails |
| `top_dkim_fail_ips.csv` | IPs mit nur DKIM-Fails |
| `top_both_fail_ips.csv` | IPs mit SPF+DKIM-Fails |
---
## 🔁 Integration in den Serverbetrieb
Das Skript wird typischerweise durch den täglichen Job `/usr/local/lib/dmarc/daily-run.sh` aufgerufen.
Manuelle Nutzung ist jederzeit möglich.
---
## 🧩 Kontext Verzeichnisstruktur (empfohlen)
```
/var/lib/dmarc/
├── reports/ # Eingehende Rohdaten (XML, ZIP, GZ) nach Datum
├── exports/ # CSV- und Top-Dateien
└── logs/ # Logausgaben der täglichen Auswertung
```
---
**Autor:** oopen.de / Systemkonfiguration
**Stand:** November 2025
**Version:** 1.2

301
DOC/DMARC-Report/dmarc-scan.sh Executable file
View File

@@ -0,0 +1,301 @@
#!/usr/bin/env bash
#
# dmarc-scan.sh — DMARC-XML-Reports (auch .gz/.zip) einlesen, tabellarisch anzeigen,
# Records als CSV exportieren (append optional), Top-IPs ermitteln
# und Top-Listen als CSV schreiben.
#
# Nutzung:
# ./dmarc-scan.sh /pfad/zu/reports \
# [--domain DOMAIN] \
# [--csv pfad/zur/records.csv] \
# [--append] \
# [--top N] \
# [--outdir pfad/zum/ordner]
#
# Beispiele:
# ./dmarc-scan.sh /var/mail/dmarc
# ./dmarc-scan.sh /var/mail/dmarc --domain fluechtlingsrat-brandenburg.de --csv dmarc.csv --append --top 15 --outdir ./export
#
# Voraussetzungen: xmlstarlet, unzip (für .zip), gzip (für .gz)
# Debian/Ubuntu: sudo apt-get install xmlstarlet unzip gzip
#
set -euo pipefail
REPORT_DIR="${1:-}"
shift || true
# Defaults
WANT_DOMAIN=""
CSV_PATH="./dmarc-summary.csv"
APPEND=0
TOP_N=10
OUTDIR="."
# Arg-Parsing (einfach)
while [[ $# -gt 0 ]]; do
case "${1:-}" in
--domain)
WANT_DOMAIN="${2:-}"; shift 2 || true ;;
--csv)
CSV_PATH="${2:-}"; shift 2 || true ;;
--append)
APPEND=1; shift || true ;;
--top)
TOP_N="${2:-10}"; shift 2 || true ;;
--outdir)
OUTDIR="${2:-.}"; shift 2 || true ;;
*)
# Unbekannte Option ignorieren
shift || true ;;
esac
done
if [[ -z "$REPORT_DIR" || ! -d "$REPORT_DIR" ]]; then
echo "Fehler: Bitte ein Verzeichnis mit DMARC-Reports angeben."
echo "Beispiel: $0 /var/mail/dmarc --domain fluechtlingsrat-brandenburg.de --csv dmarc.csv --append --top 15 --outdir ./export"
exit 1
fi
if ! command -v xmlstarlet >/dev/null 2>&1; then
echo "Fehler: xmlstarlet nicht gefunden. Bitte installieren (z.B. 'sudo apt-get install xmlstarlet')."
exit 1
fi
mkdir -p "$OUTDIR"
# CSV-Header für Records; bei --append nur schreiben, wenn Datei noch nicht existiert
RECORDS_HEADER="report_org,policy_domain,begin_utc,end_utc,source_ip,count,disposition,spf,dkim,header_from"
ensure_records_header() {
if [[ "$APPEND" -eq 1 ]]; then
if [[ ! -f "$CSV_PATH" ]]; then
echo "$RECORDS_HEADER" > "$CSV_PATH"
fi
else
echo "$RECORDS_HEADER" > "$CSV_PATH"
fi
}
ensure_records_header
# Zähler & Sets
declare -A SEEN_IPS=()
declare -A IP_COUNTS=() # Summe pro IP
declare -A SPF_ONLY_FAIL_IP=() # nur SPF fail pro IP
declare -A DKIM_ONLY_FAIL_IP=() # nur DKIM fail pro IP
declare -A BOTH_FAIL_IP=() # SPF+DKIM fail pro IP
total_msgs=0
pass_msgs=0
fail_msgs=0
spf_only_fail=0
dkim_only_fail=0
both_fail=0
# Hilfsfunktion: Epoch -> Datum (UTC)
epoch2date() {
local e="$1"
if [[ -z "$e" ]]; then printf "-"; return; fi
date -u -d @"$e" +"%Y-%m-%d %H:%M:%S UTC" 2>/dev/null || printf "%s" "$e"
}
# CSV-escape (Felder in Anführungszeichen, doppelte Anführungszeichen verdoppeln)
csv_escape() {
local s="${1:-}"
s="${s//\"/\"\"}"
printf "\"%s\"" "$s"
}
# Eine einzelne XML-Datei parsen
parse_xml() {
local xml_input="$1"
# Domain aus policy_published, ggf. für Filter
local domain
domain=$(xmlstarlet sel -T -t -v "/feedback/policy_published/domain" "$xml_input" 2>/dev/null || true)
if [[ -n "$WANT_DOMAIN" && "$domain" != "$WANT_DOMAIN" ]]; then
return 0
fi
local org begin end pol sp aspf adkim
org=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/org_name" "$xml_input" 2>/dev/null || printf "-")
begin=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/date_range/begin" "$xml_input" 2>/dev/null || printf "")
end=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/date_range/end" "$xml_input" 2>/dev/null || printf "")
pol=$(xmlstarlet sel -T -t -v "/feedback/policy_published/p" "$xml_input" 2>/dev/null || printf "-")
sp=$(xmlstarlet sel -T -t -v "/feedback/policy_published/sp" "$xml_input" 2>/dev/null || printf "-")
aspf=$(xmlstarlet sel -T -t -v "/feedback/policy_published/aspf" "$xml_input" 2>/dev/null || printf "-")
adkim=$(xmlstarlet sel -T -t -v "/feedback/policy_published/adkim" "$xml_input" 2>/dev/null || printf "-")
# Report-Header
echo "=============================================================================="
echo "Report von: ${org}"
echo "Domain (Policy): ${domain} (p=${pol}, sp=${sp}, aspf=${aspf}, adkim=${adkim})"
echo "Zeitraum: $(epoch2date "$begin") $(epoch2date "$end")"
echo "------------------------------------------------------------------------------"
printf "%-16s %7s %-10s %-6s %-6s %s\n" "Source IP" "Count" "Disposition" "SPF" "DKIM" "Header-From"
echo "------------------------------------------------------------------------------"
# Alle <record>-Einträge tabellarisch ausgeben
while IFS='|' read -r ip cnt dispo spfres dkimres hfrom; do
[[ -z "$ip$cnt$dispo$spfres$dkimres$hfrom" ]] && continue
local n=0
if [[ -n "${cnt:-}" && "$cnt" =~ ^[0-9]+$ ]]; then n="$cnt"; fi
total_msgs=$(( total_msgs + n ))
[[ -n "$ip" ]] && SEEN_IPS["$ip"]=1
[[ -n "$ip" ]] && IP_COUNTS["$ip"]=$(( ${IP_COUNTS["$ip"]:-0} + n ))
if [[ "${spfres:-}" == "pass" && "${dkimres:-}" == "pass" ]]; then
pass_msgs=$(( pass_msgs + n ))
else
fail_msgs=$(( fail_msgs + n ))
if [[ "${spfres:-}" != "pass" && "${dkimres:-}" == "pass" ]]; then
spf_only_fail=$(( spf_only_fail + n ))
[[ -n "$ip" ]] && SPF_ONLY_FAIL_IP["$ip"]=$(( ${SPF_ONLY_FAIL_IP["$ip"]:-0} + n ))
elif [[ "${spfres:-}" == "pass" && "${dkimres:-}" != "pass" ]]; then
dkim_only_fail=$(( dkim_only_fail + n ))
[[ -n "$ip" ]] && DKIM_ONLY_FAIL_IP["$ip"]=$(( ${DKIM_ONLY_FAIL_IP["$ip"]:-0} + n ))
else
both_fail=$(( both_fail + n ))
[[ -n "$ip" ]] && BOTH_FAIL_IP["$ip"]=$(( ${BOTH_FAIL_IP["$ip"]:-0} + n ))
fi
fi
printf "%-16s %7s %-10s %-6s %-6s %s\n" "${ip:--}" "${n}" "${dispo:--}" "${spfres:--}" "${dkimres:--}" "${hfrom:--}"
local begin_human end_human
begin_human="$(epoch2date "$begin")"
end_human="$(epoch2date "$end")"
{
csv_escape "$org"; printf ","
csv_escape "$domain"; printf ","
csv_escape "$begin_human"; printf ","
csv_escape "$end_human"; printf ","
csv_escape "${ip:-}"; printf ","
printf "%s," "$n"
csv_escape "${dispo:-}"; printf ","
csv_escape "${spfres:-}"; printf ","
csv_escape "${dkimres:-}"; printf ","
csv_escape "${hfrom:-}"; printf "\n"
} >> "$CSV_PATH"
done < <(xmlstarlet sel -T -t \
-m "/feedback/record" \
-v "row/source_ip" -o "|" \
-v "row/count" -o "|" \
-v "row/policy_evaluated/disposition" -o "|" \
-v "row/policy_evaluated/spf" -o "|" \
-v "row/policy_evaluated/dkim" -o "|" \
-v "identifiers/header_from" -n \
"$xml_input" 2>/dev/null)
echo
}
# Alle Dateien im Verzeichnis verarbeiten
shopt -s nullglob
for f in "$REPORT_DIR"/*; do
case "$f" in
*.xml)
parse_xml "$f"
;;
*.gz)
if command -v gzip >/dev/null 2>&1; then
tmp="$(mktemp)"
if gzip -cd "$f" > "$tmp"; then
parse_xml "$tmp"
else
echo "Warnung: Konnte $f nicht entpacken."
fi
rm -f "$tmp"
else
echo "Warnung: gzip nicht verfügbar, überspringe $f"
fi
;;
*.zip)
if command -v unzip >/dev/null 2>&1; then
tmpdir="$(mktemp -d)"
if unzip -qq -j "$f" '*.xml' -d "$tmpdir" >/dev/null 2>&1; then
for x in "$tmpdir"/*.xml; do
[[ -e "$x" ]] || continue
parse_xml "$x"
done
else
echo "Warnung: Konnte $f nicht entpacken (oder keine XML darin)."
fi
rm -rf "$tmpdir"
else
echo "Warnung: unzip nicht verfügbar, überspringe $f"
fi
;;
*) : ;;
esac
done
# Zusammenfassung
unique_ips=${#SEEN_IPS[@]}
echo "=============================================================================="
echo "GESAMT-ZUSAMMENFASSUNG"
echo "Nachrichten gesamt: $total_msgs"
echo "Eindeutige Source-IPs: $unique_ips"
echo "Alle Auth PASS: $pass_msgs"
echo "SPF/DKIM Fehler gesamt: $fail_msgs"
echo " ├─ nur SPF-Fail: $spf_only_fail"
echo " ├─ nur DKIM-Fail: $dkim_only_fail"
echo " └─ SPF+DKIM-Fail: $both_fail"
[[ -n "$WANT_DOMAIN" ]] && echo "Gefilterte Domain: $WANT_DOMAIN"
echo "Records-CSV: $CSV_PATH"
echo "Hinweis: 'Fail' umfasst Records, bei denen SPF oder DKIM nicht 'pass' war."
# Top-Listen auf STDOUT
echo
echo "TOP $TOP_N IPs nach Anzahl (über alle Reports):"
{
for ip in "${!IP_COUNTS[@]}"; do
printf "%10d %s\n" "${IP_COUNTS[$ip]}" "$ip"
done
} | sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
if (( fail_msgs > 0 )); then
echo
echo "Top-IPs nur SPF-Fail:"
{ for ip in "${!SPF_ONLY_FAIL_IP[@]}"; do printf "%10d %s\n" "${SPF_ONLY_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
echo
echo "Top-IPs nur DKIM-Fail:"
{ for ip in "${!DKIM_ONLY_FAIL_IP[@]}"; do printf "%10d %s\n" "${DKIM_ONLY_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
echo
echo "Top-IPs SPF+DKIM-Fail:"
{ for ip in "${!BOTH_FAIL_IP[@]}"; do printf "%10d %s\n" "${BOTH_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
fi
# ---- CSV-Exporte der Top-Listen ---------------------------------------------
write_top_csv () {
local outfile="$1"; shift
local -n assoc="$1" # name reference auf assoziatives Array
echo "ip,count" > "$outfile"
if [[ "${#assoc[@]}" -eq 0 ]]; then
: # leer
else
for ip in "${!assoc[@]}"; do
printf "%s,%s\n" "$ip" "${assoc[$ip]}"
done | sort -t, -k2,2nr > "$outfile"
fi
}
# Gesamt-Top-IPs
write_top_csv "$OUTDIR/top_ips.csv" IP_COUNTS
# Top-Listen nach Fail-Kategorien
write_top_csv "$OUTDIR/top_spf_fail_ips.csv" SPF_ONLY_FAIL_IP
write_top_csv "$OUTDIR/top_dkim_fail_ips.csv" DKIM_ONLY_FAIL_IP
write_top_csv "$OUTDIR/top_both_fail_ips.csv" BOTH_FAIL_IP
echo
echo "Exportierte Top-CSV-Dateien:"
echo " $OUTDIR/top_ips.csv"
echo " $OUTDIR/top_spf_fail_ips.csv"
echo " $OUTDIR/top_dkim_fail_ips.csv"
echo " $OUTDIR/top_both_fail_ips.csv"

View File

@@ -0,0 +1,621 @@
# DMARC-Server-Sammelsystem Einrichtung und Betrieb
## 📖 Präambel
Dieses Dokument beschreibt die vollständige Einrichtung eines automatisierten DMARC-Report-Sammelsystems auf einem Linux-Mailserver mit **Postfix**, **Amavis** und **Dovecot (LMTP)**.
Ziel: DMARC-Aggregatberichte (XML, .gz, .zip) serverseitig empfangen, speichern und regelmäßig auswerten.
---
## 🎯 Ziel
- DMARC-Reports zentral über `dmarc-reports@oopen.de` empfangen.
- Reports automatisch extrahieren und datumsbasiert speichern.
- Regelmäßige automatische Auswertung mit `dmarc-scan.sh`.
- Speicherung der Ergebnisse in CSV-Dateien.
- Wartungsarm, sicher und robust.
---
## 📁 Verzeichnisstruktur
Alle Dateien werden unter `/var/lib/dmarc` abgelegt:
```
/var/lib/dmarc/
├── reports/ # Eingegangene XML-, GZ-, ZIP-Dateien
│ └── YYYY/MM/DD/ # Datumsbasierte Ablage
├── processed/ # Originalmails (Archiv)
├── exports/ # CSV- und Top-Auswertungen
└── logs/ # Logdateien
```
Verzeichnisse anlegen:
```bash
sudo install -d -o vmail -g vmail -m 750 /var/lib/dmarc/{reports,processed,exports,logs}
sudo install -d -o root -g root -m 750 /usr/local/lib/dmarc
```
---
## ⚙️ 1. Postfix-Integration
### 1.1 Transport und Master-Konfiguration
In `/etc/postfix/master.cf` **am Ende einfügen:**
```bash
dmarc-pipe unix - n n - - pipe
flags=Rq user=vmail argv=/usr/local/bin/dmarc-collect.sh
```
> Passe `user=` an, falls dein Mailbenutzer anders heißt (z.B. `mail` oder `amavis`).
### 1.2 Transportregel definieren
In `/etc/postfix/transport`:
```bash
dmarc-reports@oopen.de dmarc-pipe:
```
Aktivieren:
```bash
sudo postmap /etc/postfix/transport
sudo systemctl reload postfix
```
Damit weiß Postfix, dass Mails an diese Adresse an das Skript weitergegeben werden.
### 1.3 (Optional) Kopie der Mail behalten
Wenn du zusätzlich eine Kopie im IMAP-Postfach haben willst:
In `/etc/postfix/virtual_alias_maps`:
```bash
dmarc-reports@oopen.de dmarc-reports-mbox@oopen.de
```
Dann:
```bash
sudo postmap btree:/etc/postfix/virtual_alias_maps
sudo systemctl reload postfix
```
> **Merke:** `/etc/postfix/transport` = ZustellWEG, `/etc/postfix/virtual_alias_maps` = EmpfängerALIAS.
> Nur Skript? → Nur `transport`-Eintrag.
> Skript + Kopie? → `virtual`-Alias **zusätzlich**.
---
## 📨 2. DNS-Einträge (DMARC + External Reporting)
### Beispiel für eine Domain
```dns
_dmarc.fluechtlingsrat-brandenburg.de. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@oopen.de; ruf=mailto:dmarc-reports@oopen.de; fo=1; aspf=r; adkim=r"
```
### External Reporting (oopen.de)
Wenn du Berichte für mehrere Domains auf `@oopen.de` empfängst, erlaube externes Reporting:
```dns
oopen.de._report._dmarc.oopen.de. IN TXT "v=DMARC1"
*.oopen.de._report._dmarc.oopen.de. IN TXT "v=DMARC1"
```
---
## 🧰 3. Sammelskript `/usr/local/bin/dmarc-collect.sh`
**Datei anlegen:**
```bash
sudo tee /usr/local/bin/dmarc-collect.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
BASE="/var/lib/dmarc"
INBOX="$BASE/reports"
PROC="$BASE/processed"
LOGF="$BASE/logs/collector.log"
umask 027
TMPDIR="$(mktemp -d)"
EML="$TMPDIR/mail.eml"
cat > "$EML"
ripmime --no-nameless --name-by-type --overwrite -i "$EML" -d "$TMPDIR" >>"$LOGF" 2>&1 || true
TODAY="$(date -u +%Y/%m/%d)"
OUTDIR="$INBOX/$TODAY"
mkdir -p "$OUTDIR"
moved=0
shopt -s nullglob
for f in "$TMPDIR"/*; do
case "$f" in
*.xml|*.XML|*.gz|*.zip)
sha="$(sha256sum "$f" | awk '{print $1}')"
base="$(basename "$f")"
dst="$OUTDIR/$(date -u +%Y%m%dT%H%M%SZ)_${sha:0:12}_$base"
mv "$f" "$dst"
echo "$(date -Is) stored $dst" >> "$LOGF"
moved=$((moved+1))
;;
*) : ;;
esac
done
mkdir -p "$PROC"
mv "$EML" "$PROC/$(date -u +%Y%m%dT%H%M%SZ)_mail.eml" || true
rm -rf "$TMPDIR"
if (( moved > 0 )); then
exit 0
else
echo "$(date -Is) no usable attachment in message" >> "$LOGF"
exit 0
fi
EOF
sudo apt install -y ripmime
sudo chown vmail:vmail /usr/local/bin/dmarc-collect.sh
sudo chmod 750 /usr/local/bin/dmarc-collect.sh
```
Inhalt von `dmarc-collect.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
BASE="/var/lib/dmarc"
INBOX="$BASE/reports"
PROC="$BASE/processed"
LOGF="$BASE/logs/collector.log"
umask 027
TMPDIR="$(mktemp -d)"
EML="$TMPDIR/mail.eml"
cat > "$EML"
ripmime --no-nameless --name-by-type --overwrite -i "$EML" -d "$TMPDIR" >>"$LOGF" 2>&1 || true
TODAY="$(date -u +%Y/%m/%d)"
OUTDIR="$INBOX/$TODAY"
mkdir -p "$OUTDIR"
moved=0
shopt -s nullglob
for f in "$TMPDIR"/*; do
case "$f" in
*.xml|*.XML|*.gz|*.zip)
sha="$(sha256sum "$f" | awk '{print $1}')"
base="$(basename "$f")"
dst="$OUTDIR/$(date -u +%Y%m%dT%H%M%SZ)_${sha:0:12}_$base"
mv "$f" "$dst"
echo "$(date -Is) stored $dst" >> "$LOGF"
moved=$((moved+1))
;;
*) : ;;
esac
done
mkdir -p "$PROC"
mv "$EML" "$PROC/$(date -u +%Y%m%dT%H%M%SZ)_mail.eml" || true
rm -rf "$TMPDIR"
if (( moved > 0 )); then
exit 0
else
echo "$(date -Is) no usable attachment in message" >> "$LOGF"
exit 0
fi
```
---
## ⏱️ 4. Automatische tägliche Auswertung
**Datei:** `/usr/local/lib/dmarc/daily-run.sh`
```bash
sudo tee /usr/local/lib/dmarc/daily-run.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
BASE="/var/lib/dmarc"
TODAY_DIR="$BASE/reports/$(date -u +%Y/%m/%d)"
OUTDIR="$BASE/exports"
CSV="$OUTDIR/records.csv"
LOGF="$BASE/logs/scan-$(date -u +%F).log"
mkdir -p "$OUTDIR"
if [[ -d "$TODAY_DIR" ]]; then
/usr/local/bin/dmarc-scan.sh "$TODAY_DIR" --domain fluechtlingsrat-brandenburg.de --csv "$CSV" --append --top 25 --outdir "$OUTDIR" >> "$LOGF" 2>&1
fi
EOF
sudo chmod 750 /usr/local/lib/dmarc/daily-run.sh
```
Cronjob anlegen:
```bash
echo '17 3 * * * root /usr/local/lib/dmarc/daily-run.sh' | sudo tee /etc/cron.d/dmarc-daily >/dev/null
```
---
## 🧮 5. Auswertungsskript `/usr/local/bin/dmarc-scan.sh`
Installation:
```bash
sudo apt install -y xmlstarlet unzip gzip
sudo tee /usr/local/bin/dmarc-scan.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
#
# dmarc-scan.sh — DMARC-XML-Reports (auch .gz/.zip) einlesen, tabellarisch anzeigen,
# Records als CSV exportieren (append optional), Top-IPs ermitteln
# und Top-Listen als CSV schreiben.
#
# Nutzung:
# ./dmarc-scan.sh /pfad/zu/reports \
# [--domain DOMAIN] \
# [--csv pfad/zur/records.csv] \
# [--append] \
# [--top N] \
# [--outdir pfad/zum/ordner]
#
# Beispiele:
# ./dmarc-scan.sh /var/mail/dmarc
# ./dmarc-scan.sh /var/mail/dmarc --domain fluechtlingsrat-brandenburg.de --csv dmarc.csv --append --top 15 --outdir ./export
#
# Voraussetzungen: xmlstarlet, unzip (für .zip), gzip (für .gz)
# Debian/Ubuntu: sudo apt-get install xmlstarlet unzip gzip
#
set -euo pipefail
REPORT_DIR="${1:-}"
shift || true
# Defaults
WANT_DOMAIN=""
CSV_PATH="./dmarc-summary.csv"
APPEND=0
TOP_N=10
OUTDIR="."
# Arg-Parsing (einfach)
while [[ $# -gt 0 ]]; do
case "${1:-}" in
--domain)
WANT_DOMAIN="${2:-}"; shift 2 || true ;;
--csv)
CSV_PATH="${2:-}"; shift 2 || true ;;
--append)
APPEND=1; shift || true ;;
--top)
TOP_N="${2:-10}"; shift 2 || true ;;
--outdir)
OUTDIR="${2:-.}"; shift 2 || true ;;
*)
# Unbekannte Option ignorieren
shift || true ;;
esac
done
if [[ -z "$REPORT_DIR" || ! -d "$REPORT_DIR" ]]; then
echo "Fehler: Bitte ein Verzeichnis mit DMARC-Reports angeben."
echo "Beispiel: $0 /var/mail/dmarc --domain fluechtlingsrat-brandenburg.de --csv dmarc.csv --append --top 15 --outdir ./export"
exit 1
fi
if ! command -v xmlstarlet >/dev/null 2>&1; then
echo "Fehler: xmlstarlet nicht gefunden. Bitte installieren (z.B. 'sudo apt-get install xmlstarlet')."
exit 1
fi
mkdir -p "$OUTDIR"
# CSV-Header für Records; bei --append nur schreiben, wenn Datei noch nicht existiert
RECORDS_HEADER="report_org,policy_domain,begin_utc,end_utc,source_ip,count,disposition,spf,dkim,header_from"
ensure_records_header() {
if [[ "$APPEND" -eq 1 ]]; then
if [[ ! -f "$CSV_PATH" ]]; then
echo "$RECORDS_HEADER" > "$CSV_PATH"
fi
else
echo "$RECORDS_HEADER" > "$CSV_PATH"
fi
}
ensure_records_header
# Zähler & Sets
declare -A SEEN_IPS=()
declare -A IP_COUNTS=() # Summe pro IP
declare -A SPF_ONLY_FAIL_IP=() # nur SPF fail pro IP
declare -A DKIM_ONLY_FAIL_IP=() # nur DKIM fail pro IP
declare -A BOTH_FAIL_IP=() # SPF+DKIM fail pro IP
total_msgs=0
pass_msgs=0
fail_msgs=0
spf_only_fail=0
dkim_only_fail=0
both_fail=0
# Hilfsfunktion: Epoch -> Datum (UTC)
epoch2date() {
local e="$1"
if [[ -z "$e" ]]; then printf "-"; return; fi
date -u -d @"$e" +"%Y-%m-%d %H:%M:%S UTC" 2>/dev/null || printf "%s" "$e"
}
# CSV-escape (Felder in Anführungszeichen, doppelte Anführungszeichen verdoppeln)
csv_escape() {
local s="${1:-}"
s="${s//\"/\"\"}"
printf "\"%s\"" "$s"
}
# Eine einzelne XML-Datei parsen
parse_xml() {
local xml_input="$1"
# Domain aus policy_published, ggf. für Filter
local domain
domain=$(xmlstarlet sel -T -t -v "/feedback/policy_published/domain" "$xml_input" 2>/dev/null || true)
if [[ -n "$WANT_DOMAIN" && "$domain" != "$WANT_DOMAIN" ]]; then
return 0
fi
local org begin end pol sp aspf adkim
org=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/org_name" "$xml_input" 2>/dev/null || printf "-")
begin=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/date_range/begin" "$xml_input" 2>/dev/null || printf "")
end=$(xmlstarlet sel -T -t -v "/feedback/report_metadata/date_range/end" "$xml_input" 2>/dev/null || printf "")
pol=$(xmlstarlet sel -T -t -v "/feedback/policy_published/p" "$xml_input" 2>/dev/null || printf "-")
sp=$(xmlstarlet sel -T -t -v "/feedback/policy_published/sp" "$xml_input" 2>/dev/null || printf "-")
aspf=$(xmlstarlet sel -T -t -v "/feedback/policy_published/aspf" "$xml_input" 2>/dev/null || printf "-")
adkim=$(xmlstarlet sel -T -t -v "/feedback/policy_published/adkim" "$xml_input" 2>/dev/null || printf "-")
# Report-Header
echo "=============================================================================="
echo "Report von: ${org}"
echo "Domain (Policy): ${domain} (p=${pol}, sp=${sp}, aspf=${aspf}, adkim=${adkim})"
echo "Zeitraum: $(epoch2date "$begin") $(epoch2date "$end")"
echo "------------------------------------------------------------------------------"
printf "%-16s %7s %-10s %-6s %-6s %s\n" "Source IP" "Count" "Disposition" "SPF" "DKIM" "Header-From"
echo "------------------------------------------------------------------------------"
# Alle <record>-Einträge tabellarisch ausgeben
while IFS='|' read -r ip cnt dispo spfres dkimres hfrom; do
[[ -z "$ip$cnt$dispo$spfres$dkimres$hfrom" ]] && continue
local n=0
if [[ -n "${cnt:-}" && "$cnt" =~ ^[0-9]+$ ]]; then n="$cnt"; fi
total_msgs=$(( total_msgs + n ))
[[ -n "$ip" ]] && SEEN_IPS["$ip"]=1
[[ -n "$ip" ]] && IP_COUNTS["$ip"]=$(( ${IP_COUNTS["$ip"]:-0} + n ))
if [[ "${spfres:-}" == "pass" && "${dkimres:-}" == "pass" ]]; then
pass_msgs=$(( pass_msgs + n ))
else
fail_msgs=$(( fail_msgs + n ))
if [[ "${spfres:-}" != "pass" && "${dkimres:-}" == "pass" ]]; then
spf_only_fail=$(( spf_only_fail + n ))
[[ -n "$ip" ]] && SPF_ONLY_FAIL_IP["$ip"]=$(( ${SPF_ONLY_FAIL_IP["$ip"]:-0} + n ))
elif [[ "${spfres:-}" == "pass" && "${dkimres:-}" != "pass" ]]; then
dkim_only_fail=$(( dkim_only_fail + n ))
[[ -n "$ip" ]] && DKIM_ONLY_FAIL_IP["$ip"]=$(( ${DKIM_ONLY_FAIL_IP["$ip"]:-0} + n ))
else
both_fail=$(( both_fail + n ))
[[ -n "$ip" ]] && BOTH_FAIL_IP["$ip"]=$(( ${BOTH_FAIL_IP["$ip"]:-0} + n ))
fi
fi
printf "%-16s %7s %-10s %-6s %-6s %s\n" "${ip:--}" "${n}" "${dispo:--}" "${spfres:--}" "${dkimres:--}" "${hfrom:--}"
local begin_human end_human
begin_human="$(epoch2date "$begin")"
end_human="$(epoch2date "$end")"
{
csv_escape "$org"; printf ","
csv_escape "$domain"; printf ","
csv_escape "$begin_human"; printf ","
csv_escape "$end_human"; printf ","
csv_escape "${ip:-}"; printf ","
printf "%s," "$n"
csv_escape "${dispo:-}"; printf ","
csv_escape "${spfres:-}"; printf ","
csv_escape "${dkimres:-}"; printf ","
csv_escape "${hfrom:-}"; printf "\n"
} >> "$CSV_PATH"
done < <(xmlstarlet sel -T -t \
-m "/feedback/record" \
-v "row/source_ip" -o "|" \
-v "row/count" -o "|" \
-v "row/policy_evaluated/disposition" -o "|" \
-v "row/policy_evaluated/spf" -o "|" \
-v "row/policy_evaluated/dkim" -o "|" \
-v "identifiers/header_from" -n \
"$xml_input" 2>/dev/null)
echo
}
# Alle Dateien im Verzeichnis verarbeiten
shopt -s nullglob
for f in "$REPORT_DIR"/*; do
case "$f" in
*.xml)
parse_xml "$f"
;;
*.gz)
if command -v gzip >/dev/null 2>&1; then
tmp="$(mktemp)"
if gzip -cd "$f" > "$tmp"; then
parse_xml "$tmp"
else
echo "Warnung: Konnte $f nicht entpacken."
fi
rm -f "$tmp"
else
echo "Warnung: gzip nicht verfügbar, überspringe $f"
fi
;;
*.zip)
if command -v unzip >/dev/null 2>&1; then
tmpdir="$(mktemp -d)"
if unzip -qq -j "$f" '*.xml' -d "$tmpdir" >/dev/null 2>&1; then
for x in "$tmpdir"/*.xml; do
[[ -e "$x" ]] || continue
parse_xml "$x"
done
else
echo "Warnung: Konnte $f nicht entpacken (oder keine XML darin)."
fi
rm -rf "$tmpdir"
else
echo "Warnung: unzip nicht verfügbar, überspringe $f"
fi
;;
*) : ;;
esac
done
# Zusammenfassung
unique_ips=${#SEEN_IPS[@]}
echo "=============================================================================="
echo "GESAMT-ZUSAMMENFASSUNG"
echo "Nachrichten gesamt: $total_msgs"
echo "Eindeutige Source-IPs: $unique_ips"
echo "Alle Auth PASS: $pass_msgs"
echo "SPF/DKIM Fehler gesamt: $fail_msgs"
echo " ├─ nur SPF-Fail: $spf_only_fail"
echo " ├─ nur DKIM-Fail: $dkim_only_fail"
echo " └─ SPF+DKIM-Fail: $both_fail"
[[ -n "$WANT_DOMAIN" ]] && echo "Gefilterte Domain: $WANT_DOMAIN"
echo "Records-CSV: $CSV_PATH"
echo "Hinweis: 'Fail' umfasst Records, bei denen SPF oder DKIM nicht 'pass' war."
# Top-Listen auf STDOUT
echo
echo "TOP $TOP_N IPs nach Anzahl (über alle Reports):"
{
for ip in "${!IP_COUNTS[@]}"; do
printf "%10d %s\n" "${IP_COUNTS[$ip]}" "$ip"
done
} | sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
if (( fail_msgs > 0 )); then
echo
echo "Top-IPs nur SPF-Fail:"
{ for ip in "${!SPF_ONLY_FAIL_IP[@]}"; do printf "%10d %s\n" "${SPF_ONLY_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
echo
echo "Top-IPs nur DKIM-Fail:"
{ for ip in "${!DKIM_ONLY_FAIL_IP[@]}"; do printf "%10d %s\n" "${DKIM_ONLY_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
echo
echo "Top-IPs SPF+DKIM-Fail:"
{ for ip in "${!BOTH_FAIL_IP[@]}"; do printf "%10d %s\n" "${BOTH_FAIL_IP[$ip]}" "$ip"; done; } \
| sort -rn | head -n "$TOP_N" | awk '{printf " %2d) %-16s %7s\n", NR, $2, $1}'
fi
# ---- CSV-Exporte der Top-Listen ---------------------------------------------
write_top_csv () {
local outfile="$1"; shift
local -n assoc="$1" # name reference auf assoziatives Array
echo "ip,count" > "$outfile"
if [[ "${#assoc[@]}" -eq 0 ]]; then
: # leer
else
for ip in "${!assoc[@]}"; do
printf "%s,%s\n" "$ip" "${assoc[$ip]}"
done | sort -t, -k2,2nr > "$outfile"
fi
}
# Gesamt-Top-IPs
write_top_csv "$OUTDIR/top_ips.csv" IP_COUNTS
# Top-Listen nach Fail-Kategorien
write_top_csv "$OUTDIR/top_spf_fail_ips.csv" SPF_ONLY_FAIL_IP
write_top_csv "$OUTDIR/top_dkim_fail_ips.csv" DKIM_ONLY_FAIL_IP
write_top_csv "$OUTDIR/top_both_fail_ips.csv" BOTH_FAIL_IP
echo
echo "Exportierte Top-CSV-Dateien:"
echo " $OUTDIR/top_ips.csv"
echo " $OUTDIR/top_spf_fail_ips.csv"
echo " $OUTDIR/top_dkim_fail_ips.csv"
echo " $OUTDIR/top_both_fail_ips.csv"
EOF
sudo chmod 750 /usr/local/bin/dmarc-scan.sh
```
Beschreibung: Das Skript liest XML/ZIP/GZ-Reports, zeigt eine Tabelle pro Report, schreibt eine Records-CSV (mit `--append` fortsetzbar) und exportiert Top-Listen als CSV in `--outdir`.
**Wichtige Parameter:**
- `--domain DOMAIN` (Filter)
- `--csv PFAD` (Records-CSV)
- `--append` (anhängen statt überschreiben)
- `--top N` (Top-Liste Größe)
- `--outdir PFAD` (Top-CSV Ziel)
**Beispiel:**
```bash
dmarc-scan.sh /var/lib/dmarc/reports/2025/11/12 --domain fluechtlingsrat-brandenburg.de --csv /var/lib/dmarc/exports/records.csv --append --top 25 --outdir /var/lib/dmarc/exports/
```
---
## 🔁 6. Logrotate
**Datei:** `/etc/logrotate.d/dmarc`
```bash
/var/lib/dmarc/logs/*.log {
weekly
rotate 12
compress
delaycompress
missingok
notifempty
create 640 vmail vmail
sharedscripts
postrotate
systemctl reload postfix >/dev/null 2>&1 || true
endscript
}
```
---
## ✅ 7. Test
```bash
sudo -u vmail /usr/local/bin/dmarc-collect.sh < testmail.eml
```
---
**Autor:** oopen.de / Systemkonfiguration
**Stand:** November 2025
**Version:** 1.2

View File

@@ -1,16 +0,0 @@
## -------------------------------------- ##
## - some litle tests ##
## -------------------------------------- ##
## - test smtp (STARTTLS), submission (587) (STARTTLS), smtp (SSL),
## - pop3 (SSL), pop3 (STARTTLS), imap (SSL) and imap (STARTTLS)
## -
openssl s_client -crlf -starttls smtp -connect localhost:25 [-state -debug]
openssl s_client -crlf -starttls smtp -connect localhost:587
openssl s_client -crlf -connect localhost:465
openssl s_client -crlf -connect localhost:995
openssl s_client -crlf -starttls pop3 -connect localhost:110
openssl s_client -crlf -connect localhost:993
openssl s_client -crlf -starttls imap -connect localhost:143

View File

@@ -0,0 +1,44 @@
## -------------------------------------- ##
## - some litle tests ##
## -------------------------------------- ##
## -
## - test smtp (STARTTLS), submission (587) (STARTTLS), smtp (SSL),
## - pop3 (SSL), pop3 (STARTTLS), imap (SSL) and imap (STARTTLS)
## -
# ---
# test - localhost
# ---
openssl s_client -crlf -starttls smtp -connect localhost:25 [-state -debug]
openssl s_client -crlf -starttls smtp -connect localhost:587
openssl s_client -crlf -connect localhost:465
openssl s_client -crlf -connect localhost:995
openssl s_client -crlf -starttls pop3 -connect localhost:110
openssl s_client -crlf -connect localhost:993
openssl s_client -crlf -starttls imap -connect localhost:143
# ---
# tests - remote mailserver
# ---
mailserver="mx.gemeinschaft-altenschlirf.de"
openssl s_client -crlf -starttls smtp -connect ${mailserver}:25 [-state -debug]
openssl s_client -crlf -starttls smtp -connect ${mailserver}:587
openssl s_client -crlf -connect ${mailserver}:465
openssl s_client -crlf -connect ${mailserver}:995
openssl s_client -crlf -starttls pop3 -connect ${mailserver}:110
openssl s_client -crlf -connect ${mailserver}:993
openssl s_client -crlf -starttls imap -connect ${mailserver}:143
# Test RSA based TLS connection
#
echo "quit" | openssl s_client -connect ${mailserver}:25 -starttls smtp -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384
echo "quit" | openssl s_client -connect ${mailserver}:25 -starttls smtp -tls1_2
echo "quit" | openssl s_client -connect ${mailserver}:25 -starttls smtp

View File

@@ -19,3 +19,19 @@ doveadm index -A INBOX
# -the storage files will be also checked. # -the storage files will be also checked.
# - # -
doveadm force-resync -A INBOX doveadm force-resync -A INBOX
# - Index users .Sent Folder
# -
doveadm index -u <user@domain.ltd> .Sent
# - oder alternativ und etwas allgemeiner für egal welchen Unterordner
# - ==================================================================
# -
# - Index eines Unterverzeichnisses einer Mailbox löschen
# -
# - z.Bsp. für den 'gesendet' Ordner der Mailbox presse@mbr-berlin.de
# -
# - rm /var/vmail/mbr-berlin.de/presse/Maildir/.Sent/dovecot.index*
# - systemctl restart dovecot.service

View File

@@ -0,0 +1,61 @@
# ----------
# Fix Maildir Size
#
# falls sich die tatsächliche Größe einer E-mail im Maildir Format
# von der Größenangabe im Dateinamen unterscheidet, dann läuft
# dovecot auf fehler.
#
# Es gibt 2 Möglichekeiten das zu verhindern:
#
# 1. in der dovecot.conf (z.Bsp. in 10-mail.conf deb Parameter
# 'maildir_broken_filename_sizes' auf 'yes' setzen
#
# maildir_broken_filename_sizes = yes
#
#
# 2. Die Größenangaben im Dateinamen korrigieren. Dafür gint es ein Skript
# namens 'maildir-size-fix.pl'. Dieses Skript steht bei Githup's
# 'dovecot/tools' zur Verfügung:
#
# https://github.com/dovecot/tools
#
# Das Perl-Skript maildir-size-fix.pl ist ein Werkzeug, das im Dovecot-Projekt
# verwendet wird, um Probleme mit der Größe von Maildir-Dateien zu beheben.
#
# Nützliche Optionen
#
# - add_missing_size: Fügt die Größeninformation hinzu, wenn sie fehlt.
#
# - fix_existing_size: Überprüft und korrigiert bestehende Größeninformationen.
#
# - recursive: Scannt das Maildir rekursiv, um auch Unterordner zu berücksichtigen.
#
# - verbose: Ermöglicht detaillierte Protokollierung der Vorgänge.
#
#
# ---------------------------------------------------------------------------------------
#
# Jede E.Mail in einem Maildir Ordner ist eine eigene Datei, dess dateiname üblicherweise
# die Größe der datei selbst enthält (..,S=<dateigrösse>,W=..)
#
# ein typische Datei innerhalb eines Maildirordners sieht so aus:
#
# 1755713024.M837247P2624513.rage,S=38568,W=39101:2,Sc
# | | |
# Zeitstempel | Hostname
# eind. ID
#
# S= und ,W=: Dateigröße und "Weighted size" (für IMAP/Quota).
#
# wobei die flags folgende bedeitung haben:
#
# +-------+-----------------------------+
# | Flag | Bedeutung |
# +-------+-----------------------------+
# | S | Seen (gelesen) |
# | R | Replied (beantwortet) |
# | F | Flagged (markiert / wichtig)|
# | T | Trashed (zum Löschen mark.) |
# | D | Draft (Entwurf) |
# | P | Passed (weitergeleitet) |
# +-------+-----------------------------+

View File

@@ -216,7 +216,7 @@ elif [[ "${os_dist,,}" = "debian" ]] && [[ "$os_version" -eq 11 ]] ; then
elif [[ "${os_dist,,}" = "debian" ]] && [[ "$os_version" -eq 12 ]] ; then elif [[ "${os_dist,,}" = "debian" ]] && [[ "$os_version" -eq 12 ]] ; then
_needed_packages_clamav="$_needed_packages_clamav \ _needed_packages_clamav="$_needed_packages_clamav \
libclamunrar11" libclamunrar11"
elif else
_needed_packages_clamav="$_needed_packages_clamav \ _needed_packages_clamav="$_needed_packages_clamav \
libclamunrar13" libclamunrar13"
fi fi
@@ -4335,96 +4335,15 @@ read_hash(\%spam_lovers, '/etc/postfix/spam_lovers');
\$final_spam_destiny = D_BOUNCE; \$final_spam_destiny = D_BOUNCE;
#\$final_bad_header_destiny = D_PASS; # False-positive prone (for spam) #\$final_bad_header_destiny = D_PASS; # False-positive prone (for spam)
\$sa_tag_level_deflt = 2.0; # add spam info headers if at, or above that level
\$sa_tag2_level_deflt = 5.1; # add 'spam detected' headers at that level
\$sa_kill_level_deflt = 10.31; # reject/bounce/discard/pass
##- Moved to file '/etc/amavis/policy_banks.conf'
## - ## -
## - User / Domain specific settings ## - \$sa_tag_level_deflt = 2.0; # add spam info headers if at, or above that level
## - \$sa_tag2_level_deflt = 5.1; # add 'spam detected' headers at that level
## - \$sa_kill_level_deflt = 10.31; # reject/bounce/discard/pass
## - ## -
do "/etc/amavis/policy_banks.conf"; # Externe Datei einbinden
## - Per-recipient mapping of tag2 levels to email addresses (tag2 level):
## -
## - Set directly:
## -
#\$sa_tag2_level_deflt = {
# # oopen.de
# 'oopen.de'=>'2.1',
# 'ckubu@oopen.de'=>'2.2',
# 'argus@oopen.de'=>'2.3',
# # k8h.de
# 'k8h.de'=>'6.5',
# # default
# '.'=>'5.1'
#};
## -
## - Read from file using @spam_tag2_level_maps
## -
## - default: @spam_tag2_level_maps = (\\\$sa_tag2_level_deflt);
## -
## - Example file '/etc/postfix/tag2_level_maps.dat'
## -
## - # oopen.de
## - oopen.de 2.1
## - ckubu@oopen.de 2.2
## - argus@oopen.de 2.3
## - [..]
## - # k8h.de
## - k8h.de 6.5
## - [..]
## - # default
## - . 5.1
## -
#@spam_tag2_level_maps = ( read_hash('/etc/postfix/tag2_level_maps.dat') );
## - Per-recipient mapping of kill levels to email addresses (kill level):
## -
## - Set directly
## -
#\$sa_kill_level_deflt = {
# 'ckubu@oopen.de'=>'1500.0',
# 'ckubu-adm@oopen.de'=>'1500.0',
# # default
# '.'=>'10.31'
#};
## -
## - Read from file using @spam_kill_level_maps
## -
## - default: @spam_kill_level_maps = (\\\$sa_kill_level_deflt);
## -
## - Example file '/etc/postfix/kill_level_maps.dat'
## -
## - # oopen.de
## - ckubu@oopen.de 1500.0
## - ckubu-adm@oopen.de 1500.0
## - [..]
## - # default
## - . 10.31
## -
#@spam_kill_level_maps = ( read_hash('/etc/postfix/kill_level_maps.dat') );
## - We will inform the sender about bouncing his mail with a DSN (Delivery
## - StatusNotification). That DSN message will no be send, if the spamvalue
## - exceeds the value of sa_dsn_cutoff_level
## -
#\$sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
\$sa_dsn_cutoff_level = 20;
## - change the default server response if mail was blocked
## - because of spam.
## -
## - results in (is an example):
## - <ckubu@so36.net>: host 127.0.0.1[127.0.0.1] said: 554 5.7.0 Reject, Mailserver
## - at a.mx.oopen.de: identified as SPAM - (in reply to end of DATA command)
## -
%smtp_reason_by_ccat = (
CC_SPAM, "Mailserver at \$myhostname: identified as SPAM - %x"
);
\$sa_spam_subject_tag = undef;
#\$sa_spam_subject_tag = '***SPAM*** ';
## - QUARANTINE ## - QUARANTINE
@@ -4672,6 +4591,153 @@ else
fi fi
## - Create File containing policy settings
## -
_config_policy_banks_file=/etc/amavis/policy_banks.conf
echononl " Create File \"${_config_policy_banks_file}\""
if [[ -f "${_config_policy_banks_file}" ]]; then
echo_skipped
else
cat << EOF > ${_config_policy_banks_file}
# Externe Richtliniendatei für amavisd
use strict;
# ---
# add spam info headers if at, or above that level
# ---
## - All recipients with identical the same setting:
## -
#\$sa_tag_level_deflt = 2.0;
## - Per-recipient mapping of tag2 levels to email addresses (tag2 level):
## -
## - Set directly:
## -
\$sa_tag_level_deflt = {
'oopen.de' => '-4.5',
# default
'.'=>'2.0'
};
## - Read from file using @spam_tag2_level_maps
## -
## - default: @spam_tag2_level_maps = (\$sa_tag2_level_deflt);
## -
## - Example file '/etc/postfix/tag2_level_maps.dat'
## -
## - # oopen.de
## - oopen.de 2.1
## - ckubu@oopen.de 2.2
## - argus@oopen.de 2.3
## - [..]
## - # k8h.de
## - k8h.de 6.5
## - [..]
## - # default
## - . 5.1
## -
#@spam_tag2_level_maps = ( read_hash('/etc/postfix/tag2_level_maps.dat') );
#\$sa_spam_subject_tag = '***SPAM*** '; # Spam-Betreff-Tag
\$sa_spam_subject_tag = undef;
# ---
# add 'spam detected' headers at that level
# ---
## - All recipients with identical the same setting:
## -
#\$sa_tag2_level_deflt = 5.1; # add 'spam detected' headers at that level
## - Per-recipient mapping of kill levels to email addresses (kill level):
## -
## - Set directly
## -
\$sa_tag2_level_deflt = {
'oopen.de' => '3.1',
'123comics.net' => '4.1',
'info@123comics.net' => '3.1',
# default
'.' => '5.1',
};
## - Read from file using @spam_kill_level_maps
## -
## - default: @spam_kill_level_maps = (\$sa_kill_level_deflt);
## -
## - Example file '/etc/postfix/kill_level_maps.dat'
## -
## - # oopen.de
## - ckubu@oopen.de 1500.0
## - ckubu-adm@oopen.de 1500.0
## - [..]
## - # default
## - . 10.31
## -
#@spam_kill_level_maps = ( read_hash('/etc/postfix/kill_level_maps.dat') );
# ---
# adding more detailed spam-related headers.
# ---
## - All recipients with identical the same setting:
## -
\$sa_tag3_level_deflt = 7.0; # threshold for sa_tag3_level_deflt
## - Note
## - Like 'sa_tag2_level_deflt' above per-recipient also possible
@sa_tag3_level_maps = (
['^Subject:', '\[HIGH-SPAM\] $&'], # Modify subject
['HEADER', 'X-High-Spam-Flag', 'YES'], # Add a custom header
);
# ---
# spam score threshold at which amavisd-new will reject (kill) an email.
# ---
## - All recipients with identical the same setting:
## -
\$sa_kill_level_deflt = 10.31; # reject/bounce/discard/pass
## - Note
## - Like 'sa_tag2_level_deflt' above per-recipient also possible
# ---
# The threshold for sending a delivery status notification (DSN) to the sender
# ---
## - We will inform the sender about bouncing his mail with a DSN (Delivery
## - StatusNotification). That DSN message will no be send, if the spamvalue
## - exceeds the value of sa_dsn_cutoff_level
## -
#\$sa_dsn_cutoff_level = 10; # spam level beyond which a DSN is not sent
\$sa_dsn_cutoff_level = 20;
#------------ Do not modify anything below this line -------------
1; # ensure a defined return
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
fi
fi
## - Configure syslogd matching the configuration od amavisd ## - Configure syslogd matching the configuration od amavisd
## - ## -
echononl " Configure syslogd matching the configuration of amavis" echononl " Configure syslogd matching the configuration of amavis"
@@ -5034,6 +5100,12 @@ if grep -iq -E "^amavisfeed\s+" $postfix_master_cf > /dev/null 2>&1 ; then
else else
amavisfeed_present=false amavisfeed_present=false
fi fi
if grep -iq -E "^[0-9]{2,5}\s+inet.*smtpd" $postfix_master_cf > /dev/null 2>&1 ; then
listen_on_additional_smtp_port=true
additional_smtp_port="$(grep -E "^[0-9]{2,5}\s+inet.*smtpd" /etc/postfix/master.cf | grep -o -E "^[0-9]{2,5}")"
else
listen_on_additional_smtp_port=false
fi
> $postfix_master_cf > $postfix_master_cf
while IFS='' read -r _line || [[ -n $_line ]] ; do while IFS='' read -r _line || [[ -n $_line ]] ; do
@@ -5044,6 +5116,8 @@ while IFS='' read -r _line || [[ -n $_line ]] ; do
smtp inet n - y - - smtpd smtp inet n - y - - smtpd
-o smtpd_proxy_filter=127.0.0.1:10024 -o smtpd_proxy_filter=127.0.0.1:10024
-o content_filter= -o content_filter=
-o smtpd_milters=
-o non_smtpd_milters=
EOF EOF
if [[ "$SASL_AUTH_ENABLED" = "no" ]] ; then if [[ "$SASL_AUTH_ENABLED" = "no" ]] ; then
cat >> $postfix_master_cf << EOF cat >> $postfix_master_cf << EOF
@@ -5051,6 +5125,19 @@ EOF
EOF EOF
fi fi
if ${listen_on_additional_smtp_port} ; then
cat >> $postfix_master_cf << EOF
${additional_smtp_port} inet n - y - - smtpd
-o smtpd_proxy_filter=127.0.0.1:10024
-o content_filter=
EOF
if [[ "$SASL_AUTH_ENABLED" = "no" ]] ; then
cat >> $postfix_master_cf << EOF
-o smtpd_sasl_auth_enable=no
EOF
fi
fi
if ! $submission_present && ! $smtps_present && ! $localhost_10025_present ; then if ! $submission_present && ! $smtps_present && ! $localhost_10025_present ; then
cat >> $postfix_master_cf << EOF cat >> $postfix_master_cf << EOF
localhost:10025 inet n - y - - smtpd localhost:10025 inet n - y - - smtpd
@@ -5305,6 +5392,13 @@ else
fi fi
fi fi
if ${listen_on_additional_smtp_port}; then
echo ""
warn "Please do not forget to allow incomming traffic on port \033[1m${additional_smtp_port}\033[m.
Check your firewall settings.."
fi
#fi # if $ommit ; then #fi # if $ommit ; then
# ------------------------------- # -------------------------------

View File

@@ -14,6 +14,8 @@ echo -e "\n \033[32mStart Installation of OpenDKIM..\033[m"
log_file="$(mktemp)" log_file="$(mktemp)"
backup_date="$(date +%Y-%m-%d-%H%M)"
_opendkim_packages="opendkim opendkim-tools" _opendkim_packages="opendkim opendkim-tools"
opendkim_base_dir="/etc/opendkim" opendkim_base_dir="/etc/opendkim"
@@ -28,6 +30,7 @@ opendkim_socket_file="${opendkim_socket_dir}/opendkim.sock"
postfix_needs_restart=false postfix_needs_restart=false
opendkim_needs_restart=false opendkim_needs_restart=false
# ------------- # -------------
# --- Some functions # --- Some functions
# ------------- # -------------
@@ -175,6 +178,18 @@ else
echo_skipped echo_skipped
fi fi
echononl " Backup existing file '${opendkim_conf_file}'.."
if [[ -f "${opendkim_conf_file}" ]] ; then
mv "${opendkim_conf_file}" "${opendkim_conf_file}.${backup_date}"
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Create OpenDKIM configuration # - Create OpenDKIM configuration
# - # -
@@ -189,10 +204,18 @@ else
cat <<EOF > $opendkim_conf_file 2> $log_file cat <<EOF > $opendkim_conf_file 2> $log_file
# Datei $opendkim_conf_file # Datei $opendkim_conf_file
# Sets the "authserv-id" to use when generating the Authentication-Results:
# header field after verifying a message. The default is to use the name of
# the MTA processing the message. If the string "HOSTNAME" is provided, the
# name of the host running the filter (as returned by the gethostname(3)
# function) will be used.
AuthservID "DKIM check $(hostname -f)"
# OpenDKIM agiert als Mail Filter (= Milter) in den # OpenDKIM agiert als Mail Filter (= Milter) in den
# Modi signer (s) und verifier (v) und verwendet eine # Modi signer (s) und verifier (v) und verwendet eine
# Socket-Datei zur Kommunikation (alternativ: lokaler Port) # Socket-Datei zur Kommunikation (alternativ: lokaler Port)
Mode sv Mode sv
# Socket local:/var/run/opendkim/opendkim.sock # Socket local:/var/run/opendkim/opendkim.sock
# Socket local:$opendkim_socket_file # Socket local:$opendkim_socket_file
# Socket inet:12345@localhost # Socket inet:12345@localhost
@@ -237,6 +260,21 @@ SignatureAlgorithm rsa-sha256
# because it is often the identity key used by reputation systems and thus # because it is often the identity key used by reputation systems and thus
# somewhat security sensitive. # somewhat security sensitive.
OversignHeaders From OversignHeaders From
# Add an "Authentication-Results:" header field even to unsigned messages
# from domains with no "signs all" policy. The reported DKIM result will be
# "none" in such cases. Normally unsigned mail from non-strict domains does
# not cause the results header field to be added.
AlwaysAddARHeader yes
# Causes opendkim to fork and exits immediately, leaving the service running
# in the background. The default is "true".
Background yes
# Sets the DNS timeout in seconds. A value of 0 causes an infinite wait. The
# default is 5. Ignored if not using an asynchronous resolver package. See
# also the NOTES section below.
DNSTimeout 5
EOF EOF
opendkim_needs_restart=true opendkim_needs_restart=true
if [[ $? -eq 0 ]] ; then if [[ $? -eq 0 ]] ; then
@@ -470,6 +508,35 @@ milter_protocol = 6
# #
#smtpd_milters = local:/opendkim/opendkim.sock #smtpd_milters = local:/opendkim/opendkim.sock
smtpd_milters = smtpd_milters =
# Was sind non_smtpd_milters?
#
# non_smtpd_milters gilt für alle Postfix-Prozesse, die Mails verarbeiten, aber NICHT
# der smtpd-Daemon sind.
#
# Das betrifft z. B.:
#
# cleanup Header/Content-Bereinigung
# qmgr Queue-Manager
# lmtp / smtp Auslieferung nach extern
# local lokale Zustellung
#
# Das sind z. B.:
#
# - interne Bounces (MAILER-DAEMON)
#
# - Cron-Mails vom Server
#
# - Weiterleitungen, die Postfix selbst generiert
#
# - Mails, die über sendmail CLI gesendet werden
#
# - Mails, die Amavis über LMTP zurückgibt
#
# - etc.
#
#
# DKIM soll auch die ausgehenden Mails signieren, die nicht über smtpd daemon versendet werden.
non_smtpd_milters = local:/opendkim/opendkim.sock non_smtpd_milters = local:/opendkim/opendkim.sock
EOF EOF
postfix_needs_restart=true postfix_needs_restart=true
@@ -520,9 +587,14 @@ while IFS='' read -r _line || [[ -n $_line ]] ; do
if $_found && echo "$_line" | grep -i -q -E "^\s*-o\s+smtpd_milters=\s*" ; then if $_found && echo "$_line" | grep -i -q -E "^\s*-o\s+smtpd_milters=\s*" ; then
_found=false _found=false
if ! echo "$_line" | grep -i -q -E "^\s*-o\s+smtpd_milters=\s*local:/opendkim/opendkim.sock\s*$" ; then
echo " -o smtpd_milters=local:/opendkim/opendkim.sock" >> "$tmp_master_file"
_changed=true
continue
fi
fi fi
if echo "$_line" | grep -i -q -E "^\s*(127.0.0.1|localhost):10025\s+inet\s+" 2> /dev/null ; then if echo "$_line" | grep -i -q -E "^\s*(submission|smtps)\s+inet\s+" 2> /dev/null ; then
_found=true _found=true
fi fi
@@ -540,7 +612,7 @@ if $_changed ; then
fi fi
else else
echo_skipped echo_skipped
warn "Postfix (master.cf) seems already be configured." info "Postfix (master.cf) was not changed - seems already be configured right."
echononl " Delete previosly saved file '/etc/postfix/master.cf'.." echononl " Delete previosly saved file '/etc/postfix/master.cf'.."
rm /etc/postfix/master.cf.$backup_date 2> $log_file rm /etc/postfix/master.cf.$backup_date 2> $log_file
if [[ $? -eq 0 ]] ; then if [[ $? -eq 0 ]] ; then

View File

@@ -3,6 +3,7 @@
clear clear
echo -e "\n \033[32mStart Installation of OpenDMARC..\033[m" echo -e "\n \033[32mStart Installation of OpenDMARC..\033[m"
overwrite_config_files=true
# ------------- # -------------
@@ -23,19 +24,21 @@ opendmarc_socket_dir="${postfix_spool_dir}/opendmarc"
opendmarc_socket_file="${opendmarc_socket_dir}/opendmarc.sock" opendmarc_socket_file="${opendmarc_socket_dir}/opendmarc.sock"
config_file_name_value_parameters=" config_file_name_value_parameters="
AuthservID|OpenDMARC AuthservID|$(hostname -f)
TrustedAuthservIDs|$(hostname -f)
PidFile|/run/opendmarc/opendmarc.pid PidFile|/run/opendmarc/opendmarc.pid
RejectFailures|true RejectFailures|true
Syslog|true Syslog|true
SyslogFacility|mail SyslogFacility|mail
TrustedAuthservIDs|$(hostname -f) IgnoreHosts|${opendmarc_base_dir}/ignore.hosts
IgnoreHosts|/etc/opendmarc/ignore.hosts IgnoreMailFrom|${opendmarc_base_dir}/ignore.mailfrom
IgnoreAuthenticatedClients|true IgnoreAuthenticatedClients|true
RequiredHeaders|false RequiredHeaders|false
UMask|002 UMask|002
FailureReports|false FailureReports|false
AutoRestart|true AutoRestart|true
HistoryFile|/run/opendmarc/opendmarc.dat HistoryFile|/run/opendmarc/opendmarc.dat
SPFIgnoreResults|false
SPFSelfValidate|true SPFSelfValidate|true
Socket|${opendmarc_socket_file} Socket|${opendmarc_socket_file}
" "
@@ -182,6 +185,230 @@ else
fi fi
# - Add 'IgnoreHosts' with default value to the original opendmarc.conf file
#
echononl " Add 'IgnoreHosts' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^IgnoreHosts\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## Specifies the path to a file that contains a list of hostnames, IP addresses,
## and/or CIDR expressions identifying hosts whose SMTP connections are to be
## ignored by the filter. If not specified, defaults to "127.0.0.1" only.
#
IgnoreHosts 127.0.0.1
# Optional - auch nach Absender-Domain ignorieren:
IgnoreMailFrom ${opendmarc_base_dir}/ignore.mailfrom
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'IgnoreAuthenticatedClients' with default value to the original opendmarc.conf file
#
_param="IgnoreAuthenticatedClients"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## If set, causes mail from authenticated clients (i.e., those that used
## SMTP AUTH) to be ignored by the filter. The default is "false".
#
IgnoreAuthenticatedClients false
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'TrustedAuthservIDs' with default value to the original opendmarc.conf file
#
_param="TrustedAuthservIDs"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
# Provides a list of authserv-ids that are to be used to identify Authentication-Results
# header fields whose contents are to be assumed as valid input for the DMARC assessment.
# To provide a list, separate values by commas. If the string "HOSTNAME" is provided,
# the name of the host running the filter (as returned by the gethostname(3) function)
# will be used. Matching against this list is case-insensitive. The default is to use the
# value of AuthservID.
#
TrustedAuthservIDs OpenDMARC
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'RequiredHeaders' with default value to the original opendmarc.conf file
#
_param="IgnoreAuthenticatedClients"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## If set, causes mail from authenticated clients (i.e., those that used
## SMTP AUTH) to be ignored by the filter. The default is "false".
#
IgnoreAuthenticatedClients false
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'RequiredHeaders' with default value to the original opendmarc.conf file
#
_param="RequiredHeaders"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## If set, the filter will ensure the header of the message conforms to the basic
## header field count restrictions laid out in RFC5322, Section 3.6. Messages
## failing this test are rejected without further processing. A From: field from
## which no domain name could be extracted will also be rejected.
#
RequiredHeaders false
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'AutoRestart' with default value to the original opendmarc.conf file
#
_param="AutoRestart"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## Automatically re-start on failures. Use with caution; if the filter fails
## instantly after it starts, this can cause a tight fork(2) loop.
#
AutoRestart false
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'HistoryFile' with default value to the original opendmarc.conf file
#
_param="HistoryFile"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## If set, specifies the location of a text file to which records are written
## that can be used to generate DMARC aggregate reports. Records are batches of
## rows containing information about a single received message, and include all
## relevant information needed to generate a DMARC aggregate report. It is
## expected that this will not be used in its raw form, but rather periodically
## imported into a relational database from which the aggregate reports can be
## extracted using opendmarc-importstats(8).
#
HistoryFile /run/opendmarc/opendmarc.dat
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'SPFIgnoreResults' with default value to the original opendmarc.conf file
#
_param="SPFIgnoreResults"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## Causes the filter to ignore any SPF results in the header of the message. This
## is useful if you want the filter to perform SPF checks itself, or because you
## don't trust the arriving header. The default is "false".
#
SPFIgnoreResults false
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Add 'SPFSelfValidate' with default value to the original opendmarc.conf file
#
_param="SPFSelfValidate"
echononl " Add '${_param}' with default value to the opendmarc.conf file.."
if ! $(grep -q -E "^${_param}\s+" ${opendmarc_conf_file} 2> /dev/null) ; then
cat << EOF >> ${opendmarc_conf_file}
## Causes the filter to perform a fallback SPF check itself when it can find no
## SPF results in the message header. If SPFIgnoreResults is also set, it never
## looks for SPF results in headers and always performs the SPF check itself when
## this is set. The default is "false".
#
SPFSelfValidate false
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Save configuration file from distribution # - Save configuration file from distribution
# - # -
echononl " Save configuration file from distribution" echononl " Save configuration file from distribution"
@@ -277,6 +504,18 @@ else
fi fi
echononl " Backup existing file '${opendmarc_base_dir}/ignore.hosts'.."
if [[ -f "${opendmarc_base_dir}/ignore.hosts" ]] ; then
mv "${opendmarc_base_dir}/ignore.hosts" "${opendmarc_base_dir}/ignore.hosts.${backup_date}"
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
# - Create the file ${opendmarc_base_dir}/ignore.hosts # - Create the file ${opendmarc_base_dir}/ignore.hosts
# - # -
@@ -285,11 +524,68 @@ if [[ -f "${opendmarc_base_dir}/ignore.hosts" ]] ; then
echo_skipped echo_skipped
else else
cat <<EOF > ${opendmarc_base_dir}/ignore.hosts 2> $log_file cat <<EOF > ${opendmarc_base_dir}/ignore.hosts 2> $log_file
# We are using AmaViS at 'localhost 127.0.0.1 . So we cannot bypass them # /etc/opendmarc/ignore.hosts
# #
# 127.0.0.1 # Aktuell hat OpenDMARC seinen Milter nur am Dienst
# localhost # 'localhost:10025' hängen. Dort ist der Client
$(hostname -f) # immer 127.0.0.1, nicht die externe Gegenstelle.
#
# Deshalb macht es in diesem Setup keinen Sinn,
# hier IP-Adressen von externen Diensten (CRSend etc.)
# einzutragen sie würden nie matchen.
#
# WICHTIG:
# - KEIN 127.0.0.1
# - KEIN localhost
# - KEIN ::1
#
# Eintrag dieser Adressen würde DMARC komplett deaktivieren.
#
# ==> Datei bleibt absichtlich leer.
EOF
opendmarc_needs_restart=true
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
fi
# - Create the file ${opendmarc_base_dir}/ignore.hosts
# -
echononl " Backup existing file '${opendmarc_base_dir}/ignore.mailfrom'.."
if [[ -f "${opendmarc_base_dir}/ignore.mailfrom" ]] ; then
mv "${opendmarc_base_dir}/ignore.mailfrom" "${opendmarc_base_dir}/ignore.mailfrom.${backup_date}"
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl " Create file '${opendmarc_base_dir}/ignore.mailfrom'.."
if [[ -f "${opendmarc_base_dir}/ignore.mailfrom" ]] ; then
echo_skipped
else
cat <<EOF > ${opendmarc_base_dir}/ignore.mailfrom 2> $log_file
# /etc/opendmarc/ignore.mailfrom
#
# Hier könnte man Absender-Domains von der DMARC-Prüfung
# ausnehmen (z. B. problematische Partner-Domains).
#
# Aktuell ist das für dein Setup nicht notwendig.
#
# Beispiele (NICHT aktiv!):
# @example.org
# example.org
#
# ==> Datei bleibt absichtlich leer.
EOF EOF
opendmarc_needs_restart=true opendmarc_needs_restart=true
if [[ $? -eq 0 ]] ; then if [[ $? -eq 0 ]] ; then
@@ -393,6 +689,101 @@ else
fi fi
echo ""
# - Edit /etc/postfix/main.cf and add a section to activate
# - processing of e-mail through the OpenDKIM daemon:
# -
backup_date="$(date +%Y-%m-%d-%H%M)"
echononl " Backup existing postfix configuration (main.cf).."
cp -a /etc/postfix/main.cf /etc/postfix/main.cf.$backup_date 2> $log_file
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
echononl " Activate processing of e-mail through the OpenDKIM daemon.."
if grep -q -E "milter_default_action\s*=\s*accept" /etc/postfix/main.cf ; then
echo_skipped
info "Postfix (main.cf) was not changed - seems already be configured right."
echononl " Delete previosly saved Postfix configuration.."
rm /etc/postfix/main.cf.$backup_date 2> $log_file
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
cat <<EOF >> /etc/postfix/main.cf 2> $log_file
# ======= Milter configuration =======
# OpenDKIM
milter_default_action = accept
# Postfix ≥ 2.6 milter_protocol = 6, Postfix ≤ 2.5 milter_protocol = 2
milter_protocol = 6
# Note:
# We will sign AFTER sending through AmaVIS, just befor sending out. So
# set 'smtpd_milters =' to an emty string here and add to localhost:10025
# section in master.cf: 'smtpd_milters=local:/opendkim/opendkim.sock'
#
# If you want sign mails before sending through AmaVIS, set
# 'smtpd_milters = local:/opendkim/opendkim.sock' here and add to
# localhost:10025 section in master.cf: 'smtpd_milters='
#
#smtpd_milters = local:/opendkim/opendkim.sock
smtpd_milters =
# Was sind non_smtpd_milters?
#
# non_smtpd_milters gilt für alle Postfix-Prozesse, die Mails verarbeiten, aber NICHT
# der smtpd-Daemon sind.
#
# Das betrifft z. B.:
#
# cleanup Header/Content-Bereinigung
# qmgr Queue-Manager
# lmtp / smtp Auslieferung nach extern
# local lokale Zustellung
#
# Das sind z. B.:
#
# - interne Bounces (MAILER-DAEMON)
#
# - Cron-Mails vom Server
#
# - Weiterleitungen, die Postfix selbst generiert
#
# - Mails, die über sendmail CLI gesendet werden
#
# - Mails, die Amavis über LMTP zurückgibt
#
# - etc.
#
#
# DKIM soll auch die ausgehenden Mails signieren, die nicht über smtpd daemon versendet werden.
non_smtpd_milters = local:/opendkim/opendkim.sock
EOF
postfix_needs_restart=true
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
fi
echo ""
# - Prevent Postfix from setting the DMARC Header twice (one befor # - Prevent Postfix from setting the DMARC Header twice (one befor
# - and one after processing amavis # - and one after processing amavis
# - # -
@@ -418,24 +809,58 @@ else
fi fi
echononl " Adjust /etc/postfix/master.cf. Set DMARC after sending throuh AmaVIS.." echononl " Adjust /etc/postfix/master.cf. Set DMARC after sending throuh AmaVIS.."
if $(grep -q -E "^\s*-o\s+smtpd_milters\s*=\s*.*opendkim.sock" /etc/postfix/master.cf 2> /dev/null) ; then _found=false
if $(grep -q -E "^\s*-o\s+smtpd_milters\s*=\s*.*$(basename ${opendmarc_socket_file})" /etc/postfix/master.cf); then _changed=false
echo_skipped tmp_master_file="/tmp/postfix_master.cf"
else > $tmp_master_file
perl -i -n -p -e "s&(^\s*-o\s+smtpd_milters\s*=.*)&\1,local:/$(basename "${opendmarc_socket_dir}")/$(basename "${opendmarc_socket_file}")&" \ while IFS='' read -r _line || [[ -n $_line ]] ; do
/etc/postfix/master.cf > $log_file 2>&1
if $_found && ! echo "$_line" | grep -i -q -E "^\s*-o" 2> /dev/null ; then
echo " -o smtpd_milters=local:/opendmarc/opendmarc.sock" >> "$tmp_master_file"
_changed=true
_found=false
fi
if $_found && echo "$_line" | grep -i -q -E "^\s*-o\s+smtpd_milters=\s*" ; then
_found=false
if ! echo "$_line" | grep -i -q -E "^\s*-o\s+smtpd_milters=\s*local:/opendmarc/opendmarc.sock\s*$" ; then
echo " -o smtpd_milters=local:/opendmarc/opendmarc.sock" >> "$tmp_master_file"
_changed=true
continue
fi
fi
if echo "$_line" | grep -i -q -E "^\s*(localhost|127.0.0.1):10025\s+inet\s+" 2> /dev/null ; then
_found=true
fi
echo "$_line" >> "$tmp_master_file"
done < "/etc/postfix/master.cf"
if $_changed ; then
cp $tmp_master_file /etc/postfix/master.cf 2> $log_file
postfix_needs_restart=true
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
info "Postfix (master.cf) was not changed - seems already be configured right."
echononl " Delete previosly saved file '/etc/postfix/master.cf'.."
rm /etc/postfix/master.cf.$backup_date 2> $log_file
if [[ $? -eq 0 ]] ; then if [[ $? -eq 0 ]] ; then
echo_ok echo_ok
postfix_needs_restart=true
else else
echo_failed echo_failed
error "$(cat $log_file)" error "$(cat $log_file)"
fi fi
fi fi
else rm -f $tmp_master_file
echo_skipped
warn "Postfix is not adjusted. Complete Postfix configuration (master.cf) manually\!"
fi
echo "" echo ""
@@ -503,20 +928,6 @@ else
echo_skipped echo_skipped
fi fi
echo ""
if [[ -f "/etc/postfix/master.cf.${backup_date}" ]] ; then
if $(diff "/etc/postfix/master.cf" "/etc/postfix/master.cf.${backup_date}"> /dev/null 2>&1) ; then
info "File \033[1m/etc/postfix/master.cf\033[m has not changed.\n\t Removing previos created backup.."
rm "/etc/postfix/master.cf.${backup_date}"
fi
fi
if [[ -f "/etc/postfix/main.cf.${backup_date}" ]] ; then
if $(diff "/etc/postfix/main.cf" "/etc/postfix/main.cf.${backup_date}"> /dev/null 2>&1) ; then
info "File \033[1m/etc/postfix/main.cf\033[m has not changed.\n\t Removing previos created backup.."
rm "/etc/postfix/main.cf.${backup_date}"
fi
fi
echo "" echo ""
rm -f "$log_file" rm -f "$log_file"

View File

@@ -125,9 +125,14 @@ trap clean_up SIGHUP SIGINT SIGTERM
# - # -
DEFAULT_ADMIN_EMAIL="argus@oopen.de" DEFAULT_ADMIN_EMAIL="argus@oopen.de"
DEFAULT_IS_RELAY_HOST=false DEFAULT_IS_RELAY_HOST=false
DEFAULT_ADDITIONAL_RELAY_PORT=2525
DEFAULT_IS_SYMPA_LIST_SERVER=no DEFAULT_IS_SYMPA_LIST_SERVER=no
DEFAULT_SASL_AUTH_ENABLED=no DEFAULT_SASL_AUTH_ENABLED=no
DEFAULT_LISTEN_ON_ADDITIONAL_RELAY_PORT=false
DEFAULT_INSTALL_DMARC_REPORT_SUPPORT=false
# - Is this a systemd system? # - Is this a systemd system?
# - # -
@@ -167,6 +172,15 @@ else
_IS_RELAY_HOST="$_RELAY_HOST" _IS_RELAY_HOST="$_RELAY_HOST"
fi fi
if [[ -z "$_LISTEN_ON_ADDITIONAL_RELAY_PORT" ]] ; then
_LISTEN_ON_ADDITIONAL_RELAY_PORT=${DEFAULT_LISTEN_ON_ADDITIONAL_RELAY_PORT}
fi
if [[ -z "$_INSTALL_DMARC_REPORT_SUPPORT" ]] ; then
_INSTALL_DMARC_REPORT_SUPPORT=${DEFAULT_INSTALL_DMARC_REPORT_SUPPORT}
fi
echo "" echo ""
echo "" echo ""
echo "" echo ""
@@ -394,12 +408,66 @@ if $IS_RELAY_HOST ; then
done done
fi fi
ADDITIONAL_RELAY_LISTEN_PORT=
echo ""
echo -e "\033[32m--\033[m"
echo ""
echo "Should this mail relay server listen on an additional port?"
echo ""
if [[ -n "$_ADDITIONAL_RELAY_LISTEN_PORT" ]]; then
echo "Type:"
echo -e "\t\033[33mNone\033[m for no additional listen port."
else
echo "Type:"
echo -e "\t\033[33mNone\033[m or lrave empty for no additional listen port."
fi
echo ""
if [[ -n "$_ADDITIONAL_RELAY_LISTEN_PORT" ]]; then
echononl "additional listen port [${_ADDITIONAL_RELAY_LISTEN_PORT}]: "
read ADDITIONAL_RELAY_LISTEN_PORT
if [[ "X${ADDITIONAL_RELAY_LISTEN_PORT}" = "X" ]]; then
ADDITIONAL_RELAY_LISTEN_PORT=$_ADDITIONAL_RELAY_LISTEN_PORT
LISTEN_ON_ADDITIONAL_RELAY_PORT=true
fi
if [[ "${ADDITIONAL_RELAY_LISTEN_PORT,,}" = "none" ]] ; then
ADDITIONAL_RELAY_LISTEN_PORT=""
LISTEN_ON_ADDITIONAL_RELAY_PORT=false
fi
else
echononl "additional listen port: "
read ADDITIONAL_RELAY_LISTEN_PORT
if [[ "X${ADDITIONAL_RELAY_LISTEN_PORT}" = "X" ]] || [[ "${ADDITIONAL_RELAY_LISTEN_PORT,,}" = "none" ]]; then
ADDITIONAL_RELAY_LISTEN_PORT=""
LISTEN_ON_ADDITIONAL_RELAY_PORT=false
else
LISTEN_ON_ADDITIONAL_RELAY_PORT=true
fi
fi
else else
IS_SYMPA_LIST_SERVER=false IS_SYMPA_LIST_SERVER=false
fi fi
if ! ${IS_RELAY_HOST} ; then
INSTALL_DMARC_REPORT_SUPPORT=false
echo ""
echo -e "\033[32m--\033[m"
echo ""
echo "Should this mail server support DMARC reporting?"
echo ""
echononl "Support DMARC reporting ? [ ${_INSTALL_DMARC_REPORT_SUPPORT} ]: "
read INPUT
if [[ "X${INPUT}" == "X" ]]; then
INPUT=$_INSTALL_DMARC_REPORT_SUPPORT
fi
if [[ "${INPUT,,}" == "yes" || "${INPUT,,}" == "true" ]]; then
INSTALL_DMARC_REPORT_SUPPORT=true
fi
fi
ADMIN_EMAIL= ADMIN_EMAIL=
echo "" echo ""
echo "" echo ""
@@ -441,9 +509,16 @@ if $IS_RELAY_HOST ; then
echo -e "\tConfigure as sympa list server?...: \033[33m\033[1m$IS_SYMPA_LIST_SERVER\033[m" echo -e "\tConfigure as sympa list server?...: \033[33m\033[1m$IS_SYMPA_LIST_SERVER\033[m"
echo "" echo ""
echo -e "\tSupport Cyrus SASL authentication.: $SASL_AUTH_ENABLED" echo -e "\tSupport Cyrus SASL authentication.: $SASL_AUTH_ENABLED"
echo ""
echo -e "\tListen on an additional port?.......: \033[33m\033[1m${LISTEN_ON_ADDITIONAL_RELAY_PORT}\033[m"
if ${LISTEN_ON_ADDITIONAL_RELAY_PORT}; then
echo -e "\tAdditional Listen Port..............: ${ADDITIONAL_RELAY_LISTEN_PORT}"
fi
else else
echo -e "\tConfigure as relay host?..........: $IS_RELAY_HOST" echo -e "\tConfigure as relay host?..........: $IS_RELAY_HOST"
echo -e "\tConfigure as complete mailserver..: \033[33m\033[1mtrue\033[m" echo -e "\tConfigure as complete mailserver..: \033[33m\033[1mtrue\033[m"
echo ""
echo -e "\tSupport DMARC reporting...........: \033[33m\033[1m${INSTALL_DMARC_REPORT_SUPPORT}\033[m"
fi fi
echo "" echo ""
echononl "einverstanden (yes/no): " echononl "einverstanden (yes/no): "
@@ -464,7 +539,7 @@ _failed=false
echononl " Save Configuration" echononl " Save Configuration"
cat << EOF > $conf_file cat << EOF > $conf_file
# --- # ---
# - Parameter Settins Postfix Relay System # - Parameter Settings Postfix Relay System
# --- # ---
_HOSTNAME=$HOSTNAME _HOSTNAME=$HOSTNAME
@@ -480,6 +555,16 @@ if $IS_RELAY_HOST ; then
cat << EOF >> $conf_file cat << EOF >> $conf_file
_SASL_AUTH_ENABLED=$SASL_AUTH_ENABLED _SASL_AUTH_ENABLED=$SASL_AUTH_ENABLED
_SYMPA_LIST_SERVER=$IS_SYMPA_LIST_SERVER _SYMPA_LIST_SERVER=$IS_SYMPA_LIST_SERVER
_LISTEN_ON_ADDITIONAL_RELAY_PORT=${LISTEN_ON_ADDITIONAL_RELAY_PORT}
EOF
if ${LISTEN_ON_ADDITIONAL_RELAY_PORT} ; then
cat << EOF >> $conf_file
_ADDITIONAL_RELAY_LISTEN_PORT=${ADDITIONAL_RELAY_LISTEN_PORT}
EOF
fi
else
cat << EOF >> $conf_file
_INSTALL_DMARC_REPORT_SUPPORT=${INSTALL_DMARC_REPORT_SUPPORT}
EOF EOF
fi fi
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
@@ -493,6 +578,9 @@ fi
[[ "$IPV6" = "disabled" ]] && IPV6="" [[ "$IPV6" = "disabled" ]] && IPV6=""
exit
clean_up 1
# - Synchronise package index files with the repository # - Synchronise package index files with the repository
# - # -
@@ -538,6 +626,9 @@ _needed_packages="postfix postfix-pgsql postfix-mysql postfix-pcre libsasl2-modu
if [[ "$SASL_AUTH_ENABLED" = "yes" ]]; then if [[ "$SASL_AUTH_ENABLED" = "yes" ]]; then
_needed_packages="$_needed_packages sasl2-bin" _needed_packages="$_needed_packages sasl2-bin"
fi fi
if ${INSTALL_DMARC_REPORT_SUPPORT} ; then
_needed_packages="$_needed_packages ripmime xmlstarlet unzip gzip"
fi
for _pkg in $_needed_packages ; do for _pkg in $_needed_packages ; do
if `dpkg -l | grep $_pkg | grep -e "^i" > /dev/null 2>&1` ; then if `dpkg -l | grep $_pkg | grep -e "^i" > /dev/null 2>&1` ; then
continue continue
@@ -930,6 +1021,120 @@ EOF
fi fi
# - Install SPF-Policy-Tools
# -
echononl " Install Postfix SPF-Policy-Tools 'postfix-policyd-spf-python'"
_pkg=postfix-policyd-spf-python
if aptitude search $_pkg | grep " $_pkg " | grep -e "^i" > /dev/null 2>&1 ; then
echo_skipped
else
DEBIAN_FRONTEND=noninteractive apt-get -y install $_pkg > /dev/null 2> $log_file
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
fi
# - Backup existing configuration file for policyd-spf daemon
# -
_file="/etc/postfix-policyd-spf-python/policyd-spf.conf"
echononl " Backup configuration file '${_file}'."
if [[ -f "${_file}" ]]; then
cp -a "${_file}" "${_file}.${backup_date}" > /dev/null 2> $log_file
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl " Creeate new configuration '${_file}'.."
cat <<EOF > "${_file}"
# For a fully commented sample config file see policyd-spf.conf.commented
# Reject and deferred reason
Reason_Message = Message {rejectdefer} due to: {spf}.
# Amount of debugging information logged. 0 logs no debugging messages
# 5 includes all debug messages.
debugLevel = 1
# HELO check rejection policy. Options are:
# HELO_reject = SPF_Not_Pass (default) - Reject if result not Pass/None/Tempfail.
# HELO_reject = Softfail - Reject if result Softfail and Fail
# HELO_reject = Fail - Reject on HELO Fail
# HELO_reject = Null - Only reject HELO Fail for Null sender (SPF Classic)
# HELO_reject = False - Never reject/defer on HELO, append header only.
# HELO_reject = No_Check - Never check HELO.
HELO_reject = SPF_Not_Pass
# Mail From rejection policy. Options are:
# Mail_From_reject = SPF_Not_Pass - Reject if result not Pass/None/Tempfail.
# Mail_From_reject = Softfail - Reject if result Softfail and Fail
# Mail_From_reject = Fail - Reject on Mail From Fail (default)
# Mail_From_reject = False - Never reject/defer on Mail From, append header only
# Mail_From_reject = No_Check - Never check Mail From/Return Path.
#
# Dieser Parameter steuert, wie der SPF-Check auf Fehler bei der Überprüfung der
# MAIL FROM-Adresse reagiert. Ein Fehler tritt auf, wenn die IP-Adresse des sendenden
# Servers nicht den SPF-Einträgen der Domain in der MAIL FROM-Adresse entspricht.
#
Mail_From_reject = Fail
# Policy for rejecting due to SPF PermError. Options are:
# PermError_reject = True
# PermError_reject = False
#
# Wirkung: Dieser Parameter bestimmt, wie der SPF-Check auf permanente Fehler (PermError)
# reagiert. Ein permanenter Fehler tritt auf, wenn die SPF-DNS-Einträge ungültig oder
# fehlerhaft sind (z. B. syntaktische Fehler oder ungültige Mechanismen).
#
# Wenn PermError_reject auf True gesetzt ist, wird die E-Mail abgewiesen (rejected),
#
PermError_reject = True
# Policy for deferring messages due to SPF TempError. Options are:
# TempError_Defer = True
# TempError_Defer = False
#
# Wirkung: Dieser Parameter bestimmt das Verhalten bei temporären SPF-Fehlern (TempError).
# Ein temporärer Fehler tritt auf, wenn der SPF-Check aufgrund von vorübergehenden
# Problemen (z. B. DNS-Auflösungsfehler oder Netzwerkprobleme) nicht durchgeführt werden kann.
#
# Wenn TempError_Defer auf True gesetzt ist, wird die E-Mail vorübergehend zurückgewiesen
# (deferred), und der empfangende Server versucht später erneut, die E-Mail zuzustelle
#
TempError_Defer = Defer
# Type of header to insert to document SPF result. Can be Received-SPF (SPF)
# or Authentication Results (AR). It cannot be both.
# Examples: (default is Received-SPF):
# Header_Type = AR
# Header_Type = SPF
Header_Type = SPF
# Do not check SPF for localhost addresses - add to skip addresses to
# skip SPF for internal networks if desired. Defaults are standard IPv4 and
# IPv6 localhost addresses.
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
# RFC 7208 adds a new processing limit called "void lookup limit" (See section
# 4.6.4). Default is 2, but it can be adjusted.
Void_Limit = 5
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
fi
## - Install Postfix Firewall Daemon from debian packages system ## - Install Postfix Firewall Daemon from debian packages system
## - ## -
echononl " Install Postfix Firewall Daemon from debian packages system" echononl " Install Postfix Firewall Daemon from debian packages system"
@@ -1998,7 +2203,7 @@ smtp_tls_mandatory_protocols = >=TLSv1.2
# Use EECDH with approximately 192 bits of security at computational cost that is # Use EECDH with approximately 192 bits of security at computational cost that is
# approximately twice as high as 128 bit strength ECC. # approximately twice as high as 128 bit strength ECC.
# #
smtpd_tls_eecdh_grade = auto #smtpd_tls_eecdh_grade = auto
# With SSLv3 and later, use the Postfix SMTP server's cipher preference order instead # With SSLv3 and later, use the Postfix SMTP server's cipher preference order instead
@@ -2219,6 +2424,45 @@ virtual_alias_domains =
btree:/etc/postfix/virtual_alias_domains btree:/etc/postfix/virtual_alias_domains
#======= Postfix DSN Support ============
#
# Use the smtpd_discard_ehlo_keyword_address_maps feature if you wish to allow DSN
# requests from trusted clients but not from random strangers
#
# smtpd_discard_ehlo_keyword_address_maps =
# cidr:/etc/postfix/esmtp_access
#
# /etc/postfix/esmtp_access:
# # Allow DSN requests from local subnet only
# 192.168.0.0/28 silent-discard
# 0.0.0.0/0 silent-discard, dsn
# ::/0 silent-discard, dsn
#
#smtpd_discard_ehlo_keyword_address_maps =
# If you want to disallow all use of DSN requests from the network, use the
# smtpd_discard_ehlo_keywords feature:
#
# /etc/postfix/main.cf:
# smtpd_discard_ehlo_keywords = silent-discard, dsn
#
#
#
# A case insensitive list of EHLO keywords (pipelining, starttls, auth, etc.) that
# the Postfix SMTP server will not send in the EHLO response to a remote SMTP client.
#
#
# Notes:
#
# Specify the silent-discard pseudo keyword to prevent this action from being logged.
#
# Use the smtpd_discard_ehlo_keyword_address_maps feature to discard EHLO keywords selectively.
#
#smtpd_discard_ehlo_keywords = silent-discard, dsn
#======= Rate Limiting ============ #======= Rate Limiting ============
# anvil_rate_time_unit (default: 60s) # anvil_rate_time_unit (default: 60s)
@@ -2444,6 +2688,20 @@ else
EOF EOF
fi fi
if [[ -n "$(which policyd-spf)" ]] ; then
cat <<EOF >> /etc/postfix/main.cf
# The time limit for delivery to '/usr/bin/policyd-spf'
#
# An entry in '/etc/postfix/master.cf' is needed:
#
# policyd-spf unix - n n - 0 spawn
# user=policyd-spf argv=/usr/bin/policyd-spf
#
policyd-spf_time_limit = 3600
EOF
fi
cat <<EOF >> /etc/postfix/main.cf cat <<EOF >> /etc/postfix/main.cf
@@ -2472,7 +2730,7 @@ smtpd_client_restrictions =
permit_mynetworks, permit_mynetworks,
# Whitelist clients # Whitelist clients
# #
check_client_access btree:/etc/postfix/client_whitelist check_client_access btree:/etc/postfix/client_whitelist,
# White- / Blacklisting # White- / Blacklisting
# #
check_sender_access btree:/etc/postfix/access_sender, check_sender_access btree:/etc/postfix/access_sender,
@@ -2496,11 +2754,16 @@ smtpd_client_restrictions =
# - reject_rbl_client: This is an IP-based blacklist. When the client IP address is backlisted, # - reject_rbl_client: This is an IP-based blacklist. When the client IP address is backlisted,
# reject the email. # reject the email.
# #
reject_rhsbl_helo dbl.spamhaus.org, # reject_rhsbl_helo dbl.spamhaus.org,
reject_rhsbl_reverse_client dbl.spamhaus.org, # reject_rhsbl_reverse_client dbl.spamhaus.org,
reject_rhsbl_sender dbl.spamhaus.org, # reject_rhsbl_sender dbl.spamhaus.org,
reject_rbl_client zen.spamhaus.org, # reject_rbl_client zen.spamhaus.org,
reject_rbl_client ix.dnsbl.manitu.net, #
reject_rhsbl_helo dbl.spamhaus.org=127.0.1.[2..99],
reject_rhsbl_reverse_client dbl.spamhaus.org=127.0.1.[2..99],
reject_rhsbl_sender dbl.spamhaus.org=127.0.1.[2..99],
reject_rbl_client zen.spamhaus.org=127.0.1.[2..99],
# Greylisting check # Greylisting check
# #
# check_policy_service inet:127.0.0.1:10023, # check_policy_service inet:127.0.0.1:10023,
@@ -2634,6 +2897,17 @@ smtpd_recipient_restrictions =
# managed by the verify(8) server; see http://www.postfix.org/ADDRESS_VERIFICATION_README.html # managed by the verify(8) server; see http://www.postfix.org/ADDRESS_VERIFICATION_README.html
# for more details # for more details
reject_unverified_recipient, reject_unverified_recipient,
EOF
if [[ -n "$(which policyd-spf)" ]] ; then
cat <<EOF >> /etc/postfix/main.cf
# Check Postfix policy service ..
#
check_policy_service unix:private/policy-spf
EOF
fi
cat <<EOF >> /etc/postfix/main.cf
# Policyd-Weight # Policyd-Weight
#check_policy_service inet:127.0.0.1:12525, #check_policy_service inet:127.0.0.1:12525,
# permit Backup MX # permit Backup MX
@@ -2693,14 +2967,28 @@ smtpd_data_restrictions =
# #
reject_unauth_pipelining reject_unauth_pipelining
## ---
## - smtpd END OF ATA Restrictions
## ---
smtpd_end_of_data_restrictions = smtpd_end_of_data_restrictions =
EOF
if [[ -n "$(which postfwd)" ]] ; then
cat <<EOF >> /etc/postfix/main.cf
# Check Postfix Firewall Daemon # Check Postfix Firewall Daemon
# #
check_policy_service inet:127.0.0.1:10040 check_policy_service inet:127.0.0.1:10040
EOF EOF
else
cat <<EOF >> /etc/postfix/main.cf
if [[ -n "$(which opendkim)" ]] ; then EOF
fi
if [[ -n "$(which opendkim)" ]] || [[ -n "$(which opendmarc)" ]] ; then
cat <<EOF >> /etc/postfix/main.cf cat <<EOF >> /etc/postfix/main.cf
# ======= Milter configuration ======= # ======= Milter configuration =======
@@ -2721,11 +3009,48 @@ milter_protocol = 6
# 'smtpd_milters = local:/opendkim/opendkim.sock' here and add to # 'smtpd_milters = local:/opendkim/opendkim.sock' here and add to
# localhost:10025 section in master.cf: 'smtpd_milters=' # localhost:10025 section in master.cf: 'smtpd_milters='
# #
#smtpd_milters = local:/opendkim/opendkim.sock
smtpd_milter_maps = cidr:/etc/postfix/smtpd_milter_map smtpd_milter_maps = cidr:/etc/postfix/smtpd_milter_map
smtpd_milters = smtpd_milters =
# Was sind non_smtpd_milters?
#
# non_smtpd_milters gilt für alle Postfix-Prozesse, die Mails verarbeiten, aber NICHT
# der smtpd-Daemon sind.
#
# Das betrifft z. B.:
#
# cleanup Header/Content-Bereinigung
# qmgr Queue-Manager
# lmtp / smtp Auslieferung nach extern
# local lokale Zustellung
#
# Das sind z. B.:
#
# - interne Bounces (MAILER-DAEMON)
#
# - Cron-Mails vom Server
#
# - Weiterleitungen, die Postfix selbst generiert
#
# - Mails, die über sendmail CLI gesendet werden
#
# - Mails, die Amavis über LMTP zurückgibt
#
# - etc.
#
#
EOF
fi
if [[ -n "$(which opendkim)" ]] ; then
cat <<EOF >> /etc/postfix/main.cf
# DKIM soll auch die ausgehenden Mails signieren, die nicht über smtpd daemon versendet werden.
#
non_smtpd_milters = local:/opendkim/opendkim.sock non_smtpd_milters = local:/opendkim/opendkim.sock
EOF EOF
else
cat <<EOF >> /etc/postfix/main.cf
non_smtpd_milters =
EOF
fi fi
@@ -3147,6 +3472,7 @@ else
fi fi
echononl " Create file \"relay_domains\"" echononl " Create file \"relay_domains\""
if [[ ! -f /etc/postfix/relay_domains ]] ; then if [[ ! -f /etc/postfix/relay_domains ]] ; then
touch /etc/postfix/relay_domains touch /etc/postfix/relay_domains
@@ -3488,6 +3814,133 @@ EOF
fi fi
if ${INSTALL_DMARC_REPORT_SUPPORT} ; then
# ----
# - Add support for DMARC report
# ----
# - /var/lib/dmarc/
# - ├── reports/ # Eingegangene XML-, GZ-, ZIP-Dateien
# - │ └── YYYY/MM/DD/ # Datumsbasierte Ablage
# - ├── processed/ # Originalmails (Archiv)
# - ├── exports/ # CSV- und Top-Auswertungen
# - └── logs/ # Logdateien
echononl "Add directory Structure for collecting and analysing DMARC reports.."
install -d -o vmail -g vmail -m 750 /var/lib/dmarc/{reports,processed,exports,logs} > /dev/null 2> $log_file
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
echononl "Add 'dmarc-pipe' entry to $postfix_master_cf .."
cat <<EOF >> /etc/postfix/transport 2> $log_file
# - Take care your master.cf file ($postfix_master_cf) contains:
# -
# - dmarc-pipe unix - n n - - pipe
# - flags=Rq user=vmail argv=/usr/local/bin/dmarc-collect.sh
# -
dmarc-reports@oopen.de dmarc-pipe:
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
echononl "Create Postfix lookup table '/etc/postfix/transport'.."
postmap btree:/etc/postfix/transport > /dev/null 2> $log_file
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
echononl "Create script '/usr/local/bin/dmarc-collect.sh'.."
tee /usr/local/bin/dmarc-collect.sh > /dev/null 2> $log_file <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
BASE="/var/lib/dmarc"
INBOX="$BASE/reports"
PROC="$BASE/processed"
LOGF="$BASE/logs/collector.log"
umask 027
TMPDIR="$(mktemp -d)"
EML="$TMPDIR/mail.eml"
cat > "$EML"
ripmime --no-nameless --name-by-type --overwrite -i "$EML" -d "$TMPDIR" >>"$LOGF" 2>&1 || true
TODAY="$(date -u +%Y/%m/%d)"
OUTDIR="$INBOX/$TODAY"
mkdir -p "$OUTDIR"
moved=0
shopt -s nullglob
for f in "$TMPDIR"/*; do
case "$f" in
*.xml|*.XML|*.gz|*.zip)
sha="$(sha256sum "$f" | awk '{print $1}')"
base="$(basename "$f")"
dst="$OUTDIR/$(date -u +%Y%m%dT%H%M%SZ)_${sha:0:12}_$base"
mv "$f" "$dst"
echo "$(date -Is) stored $dst" >> "$LOGF"
moved=$((moved+1))
;;
*) : ;;
esac
done
mkdir -p "$PROC"
mv "$EML" "$PROC/$(date -u +%Y%m%dT%H%M%SZ)_mail.eml" || true
rm -rf "$TMPDIR"
if (( moved > 0 )); then
exit 0
else
echo "$(date -Is) no usable attachment in message" >> "$LOGF"
exit 0
fi
EOF
if [[ $? -eq 0 ]] ; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
_failed=false
echononl "Set permissions for '/usr/local/bin/dmarc-collect.sh'.."
chown vmail:vmail /usr/local/bin/dmarc-collect.sh > /dev/null 2> $log_file
if [[ $? -ne 0 ]] ; then
_failed=true
fi
chmod 750 /usr/local/bin/dmarc-collect.sh > /dev/null 2>> $log_file
if [[ $? -ne 0 ]] ; then
_failed=true
fi
if ${_failed} ; then
echo_failed
error "$(cat $log_file)"
else
echo_ok
fi
fi
## - /etc/postfix/master.cf ## - /etc/postfix/master.cf
## - ## -
## - Create Listener for user authenticated smtp connection port 587 (submission) ## - Create Listener for user authenticated smtp connection port 587 (submission)
@@ -3514,6 +3967,18 @@ else
smtps_present=false smtps_present=false
fi fi
if grep -iq -E "^policyd-spf\s+" $postfix_master_cf > /dev/null 2>&1 ; then
policyd_spf_present=true
else
policyd_spf_present=false
fi
if grep -iq -E "^dmarc-pipe\s+" $postfix_master_cf > /dev/null 2>&1 ; then
dmarc_pipe_present=true
else
dmarc_pipe_present=false
fi
_found=false _found=false
echononl " Create new file \"${postfix_master_cf}\"" echononl " Create new file \"${postfix_master_cf}\""
if [[ -f "${postfix_master_cf}.$backup_date" ]]; then if [[ -f "${postfix_master_cf}.$backup_date" ]]; then
@@ -3544,6 +4009,10 @@ smtps inet n - y - - smtpd
#-o milter_macro_daemon_name=ORIGINATING #-o milter_macro_daemon_name=ORIGINATING
EOF EOF
fi fi
elif $LISTEN_ON_ADDITIONAL_RELAY_PORT ; then
cat >> $postfix_master_cf << EOF
${ADDITIONAL_RELAY_LISTEN_PORT} inet n - y - - smtpd
EOF
fi fi
continue continue
fi fi
@@ -3591,6 +4060,17 @@ EOF
done < "${postfix_master_cf}.$backup_date" done < "${postfix_master_cf}.$backup_date"
# - Add support for policyd-spf service
# -
if ! $(grep -iq -E "^policyd-spf\s+" "$postfix_master_cf" 2> /dev/null) ; then
cat <<EOF >> $postfix_master_cf
policy-spf unix - n n - 0 spawn
user=policyd-spf argv=/usr/bin/policyd-spf
EOF
fi
# - Add transport definitions for only sending over IPv4/IPv6 # - Add transport definitions for only sending over IPv4/IPv6
# - # -
if ! $(grep -iq -E "^smtp-ipv4-only\s+" "$postfix_master_cf" 2> /dev/null) ; then if ! $(grep -iq -E "^smtp-ipv4-only\s+" "$postfix_master_cf" 2> /dev/null) ; then
@@ -3609,6 +4089,18 @@ smtp-ipv6-only unix - - n - - smtp
EOF EOF
fi fi
# - Add support for DMARC reporting
# -
if ${INSTALL_DMARC_REPORT_SUPPORT} ; then
if ! $(grep -iq -E "^dmarc-pipe\s+" "$postfix_master_cf" 2> /dev/null) ; then
cat <<EOF >> $postfix_master_cf
dmarc-pipe unix - n n - - pipe
flags=Rq user=vmail argv=/usr/local/bin/dmarc-collect.sh
EOF
fi
fi
echo_done echo_done
warn "Please check file \"$postfix_master_cf\" !" warn "Please check file \"$postfix_master_cf\" !"
else else
@@ -3838,6 +4330,14 @@ else
fi fi
fi fi
if ${LISTEN_ON_ADDITIONAL_RELAY_PORT} ; then
echo ""
warn "Please do not forget to allow incomming traffic on port \033[1m${ADDITIONAL_RELAY_LISTEN_PORT}\033[m.
Check your firewall settings.."
fi
if [[ -n "$(which amavisd-new)" ]] ; then if [[ -n "$(which amavisd-new)" ]] ; then
warn "You have to run \033[1minstall_amavis.sh\033[m script to continue the configuration." warn "You have to run \033[1minstall_amavis.sh\033[m script to continue the configuration."

View File

@@ -129,6 +129,7 @@ detect_os_1 () {
DEFAULT_ADMIN_EMAIL="argus@oopen.de" DEFAULT_ADMIN_EMAIL="argus@oopen.de"
DEFAULT_RELAY_HOST="b.mx.oopen.de" DEFAULT_RELAY_HOST="b.mx.oopen.de"
DEFAULT_RELAY_PORT=25
DEFAULT_SASL_AUTH=false DEFAULT_SASL_AUTH=false
DEFAULT_REWRITE_SENDER_DOMAIN=None DEFAULT_REWRITE_SENDER_DOMAIN=None
@@ -340,11 +341,12 @@ fi
# --- Some further default values depending on sasl authentification # --- Some further default values depending on sasl authentification
# ------------- # -------------
# - Set default value for relay host if sasl authentification should be # - Set default value for relay host / relay port if sasl authentification should be
# - supported and value for _RELAY_HOST not given # - supported and value for _RELAY_HOST / _RELAY_PORT not given
# - # -
if [[ "$SASL_AUTH" = "yes" ]] || $SASL_AUTH ; then if [[ "$SASL_AUTH" = "yes" ]] || $SASL_AUTH ; then
[[ -z "$_RELAY_HOST" ]] && _RELAY_HOST="$DEFAULT_RELAY_HOST" [[ -z "$_RELAY_HOST" ]] && _RELAY_HOST="$DEFAULT_RELAY_HOST"
[[ -z "$_RELAY_PORT" ]] && _RELAY_PORT="$DEFAULT_RELAY_PORT"
fi fi
if [[ -z ${_REWRITE_SENDER_DOMAIN} ]] ; then if [[ -z ${_REWRITE_SENDER_DOMAIN} ]] ; then
@@ -415,6 +417,27 @@ if [[ "$SASL_AUTH" = "yes" ]] || $SASL_AUTH ; then
done done
fi fi
RELAY_PORT=
echo ""
echo "Insert the target port to connect to ${RELAY_HOST}"
echo ""
if [[ -n "$_RELAY_PORT" ]];then
echononl "(target) Port on ${RELAY_HOST} [$_RELAY_PORT]: "
read RELAY_PORT
if [[ "X${RELAY_PORT}" = "X" ]]; then
RELAY_PORT=$_RELAY_PORT
fi
else
while [[ "X${RELAY_PORT}" = "X" ]]; do
echononl "(target) Port on ${RELAY_HOST}: "
read RELAY_PORT
if [[ "X${RELAY_PORT}" = "X" ]]; then
echo -e "\n\t\033[33m\033[1mi(target) Port of ${RELAY_HOST} is reqired\033[m\n"
fi
done
fi
else else
SASL_AUTH=false SASL_AUTH=false
fi fi
@@ -467,6 +490,7 @@ if $SASL_AUTH ; then
echo -e "\t sasl user.............: $SASL_USER" echo -e "\t sasl user.............: $SASL_USER"
echo -e "\t sasl password.........: $SASL_PASS" echo -e "\t sasl password.........: $SASL_PASS"
echo -e "\t Relayhost.............: $RELAY_HOST" echo -e "\t Relayhost.............: $RELAY_HOST"
echo -e "\t Port on Relayhost.....: $RELAY_PORT"
fi fi
echo "" echo ""
echononl "einverstanden (yes/no): " echononl "einverstanden (yes/no): "
@@ -498,6 +522,7 @@ _SASL_AUTH=$SASL_AUTH
_SASL_USER=$SASL_USER _SASL_USER=$SASL_USER
_SASL_PASS=$SASL_PASS _SASL_PASS=$SASL_PASS
_RELAY_HOST=$RELAY_HOST _RELAY_HOST=$RELAY_HOST
_RELAY_PORT=$RELAY_PORT
_REWRITE_SENDER_DOMAIN=$REWRITE_SENDER_DOMAIN _REWRITE_SENDER_DOMAIN=$REWRITE_SENDER_DOMAIN
EOF EOF
if [[ $? -eq 0 ]] ; then if [[ $? -eq 0 ]] ; then
@@ -674,13 +699,26 @@ mydestination =
## - ## -
mynetworks = mynetworks =
127.0.0.0/8 127.0.0.0/8
EOF
if [[ ${IPV4} =~ ^127 ]] ; then
cat <<EOF >> /etc/postfix/main.cf
smtp_bind_address =
smtp_bind_address6 =
EOF
else
cat <<EOF >> /etc/postfix/main.cf
${IPV4}/32 ${IPV4}/32
smtp_bind_address = $IPV4 smtp_bind_address = $IPV4
smtp_bind_address6 = $IPV6 #smtp_bind_address6 =
EOF EOF
fi fi
fi
cat <<EOF >> /etc/postfix/main.cf cat <<EOF >> /etc/postfix/main.cf
@@ -788,9 +826,24 @@ smtp_sasl_auth_enable = yes
# Only offer SMTP AUTH when talking over an encrypted connection # Only offer SMTP AUTH when talking over an encrypted connection
smtpd_tls_auth_only = yes smtpd_tls_auth_only = yes
EOF
if [[ ${RELAY_PORT} -ne 25 ]] ; then
cat <<EOF >> /etc/postfix/main.cf
# Forwarding to the ip-adress of host b.mx.oopen.de
relayhost = [${RELAY_HOST}]:${RELAY_PORT}
EOF
else
cat <<EOF >> /etc/postfix/main.cf
# Forwarding to the ip-adress of host b.mx.oopen.de # Forwarding to the ip-adress of host b.mx.oopen.de
relayhost = [${RELAY_HOST}] relayhost = [${RELAY_HOST}]
EOF
fi
cat <<EOF >> /etc/postfix/main.cf
# File including login data # File including login data
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
@@ -1050,8 +1103,21 @@ echo_ok
echononl " Configure SASL authentification" echononl " Configure SASL authentification"
if $SASL_AUTH ; then if $SASL_AUTH ; then
_sasl_pw_file="/etc/postfix/sasl_passwd"
if [[ -f "${_sasl_pw_file}" ]] && $(grep -q -E "^\[${RELAY_HOST}\]" ${_sasl_pw_file} 2> /dev/null); then
sed -i "/^\[${RELAY_HOST}/d" ${_sasl_pw_file}
if [[ "$?" != "0" ]]; then
error "Setting \"/etc/postfix/sasl_passwd\" failed! "
_failed=true
fi
fi
_failed=false _failed=false
echo "[$RELAY_HOST] ${SASL_USER}@${RELAY_HOST}:$SASL_PASS" > /etc/postfix/sasl_passwd if [[ ${RELAY_PORT} -ne 25 ]] ; then
echo "[$RELAY_HOST]:${RELAY_PORT} ${SASL_USER}@${RELAY_HOST}:$SASL_PASS" >> /etc/postfix/sasl_passwd
else
echo "[$RELAY_HOST] ${SASL_USER}@${RELAY_HOST}:$SASL_PASS" >> /etc/postfix/sasl_passwd
fi
if [[ "$?" != "0" ]]; then if [[ "$?" != "0" ]]; then
error "Setting \"/etc/postfix/sasl_passwd\" failed! " error "Setting \"/etc/postfix/sasl_passwd\" failed! "
_failed=true _failed=true
@@ -1397,6 +1463,12 @@ else
fi fi
fi fi
if [[ ${RELAY_PORT} -ne 25 ]] ; then
echo ""
warn "Please do not forget to allow port \033[1m${RELAY_PORT}\033[m on both sides, outgoing
on this host here and incoming on the relay host '${RELAY_HOST}'."
fi
echo "" echo ""
clean_up 0 clean_up 0

View File

@@ -1842,23 +1842,25 @@ fi
# - Encoding does not work as exspected. # - Encoding does not work as exspected.
# - # -
# - Update: Encoding seems to works now
# -
# - NOTE: # - NOTE:
# - this IS NOT a fix, but a workaround # - this IS NOT a fix, but a workaround
# - # -
echononl "\tWorkaround, because encoding does not work as exspected." #echononl "\tWorkaround, because encoding does not work as exspected."
# - Vacation script changed. Since Version 3.2 we need another perl regexp. ## - Vacation script changed. Since Version 3.2 we need another perl regexp.
# - The old one was: ## - The old one was:
# - perl -i -n -p -e "s/(\s*\'ctype\'\s* =>\s*)\'text\/plain.*$/\1\'text\/plain; charset=iso-8859-1\',/" \ ## - perl -i -n -p -e "s/(\s*\'ctype\'\s* =>\s*)\'text\/plain.*$/\1\'text\/plain; charset=iso-8859-1\',/" \
# - ## -
perl -i -n -p -e "s/(\s*\'Content-Type\'\s* =>\s*)\"text\/plain.*$/\1\"text\/plain; charset=iso-8859-1\",/" \ ##perl -i -n -p -e "s/(\s*\'Content-Type\'\s* =>\s*)\"text\/plain.*$/\1\"text\/plain; charset=iso-8859-1\",/" \
/var/spool/vacation/vacation.pl > "$log_file" 2>&1 ## /var/spool/vacation/vacation.pl > "$log_file" 2>&1
if [[ $? -eq 0 ]];then #if [[ $? -eq 0 ]];then
echo_ok # echo_ok
info "This IS NOT a fix, but a workaround." # info "This IS NOT a fix, but a workaround."
else #else
echo_failed # echo_failed
error "$(cat $log_file)" # error "$(cat $log_file)"
fi #fi
echononl "\tSet Permission on vacation script" echononl "\tSet Permission on vacation script"
_failed=false _failed=false

11143
install_update_dovecot-2.4.sh Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -287,6 +287,9 @@ dovecot_main_version="$(echo $_version | cut -d '.' -f1,2)"
dovecot_major_version="$(echo $_version | cut -d '.' -f1)" dovecot_major_version="$(echo $_version | cut -d '.' -f1)"
dovecot_minor_version="$(echo $_version | cut -d '.' -f2)" dovecot_minor_version="$(echo $_version | cut -d '.' -f2)"
dovecot_patch_level="$(echo $_version | cut -d '.' -f3)" dovecot_patch_level="$(echo $_version | cut -d '.' -f3)"
dovecot_minor_patch_level="$(echo $_version | cut -d '.' -f4)"
_version_short="${_version%-*}"
#echo "" #echo ""
#echo "_version: $_version" #echo "_version: $_version"
@@ -294,7 +297,11 @@ dovecot_patch_level="$(echo $_version | cut -d '.' -f3)"
#echo "dovecot_major_version $dovecot_major_version" #echo "dovecot_major_version $dovecot_major_version"
#echo "dovecot_minor_version $dovecot_minor_version" #echo "dovecot_minor_version $dovecot_minor_version"
#echo "dovecot_patch_level $dovecot_patch_level" #echo "dovecot_patch_level $dovecot_patch_level"
#echo "dovecot_minor_patch_level $dovecot_minor_patch_level"
#echo "" #echo ""
#
#clean_up 0
# 'expire plugin'was rRemoved in version 2.3.14: This plugin is not needed. # 'expire plugin'was rRemoved in version 2.3.14: This plugin is not needed.
# Use mailbox { autoexpunge } Mailbox settings instead. # Use mailbox { autoexpunge } Mailbox settings instead.
@@ -710,6 +717,8 @@ fi
## - Download Pigeonhole for Dovecot v2.2 ## - Download Pigeonhole for Dovecot v2.2
## - ## -
if [[ ${dovecot_major_version} -eq 2 ]] && [[ ${dovecot_minor_version} -lt 4 ]] ; then
echononl "\tDownload dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz.." echononl "\tDownload dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz.."
if [ ! -f "${_src_base_dir}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz" ]; then if [ ! -f "${_src_base_dir}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz" ]; then
wget --no-check-certificate https://pigeonhole.dovecot.org/releases/${dovecot_main_version}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz > /dev/null 2>&1 wget --no-check-certificate https://pigeonhole.dovecot.org/releases/${dovecot_main_version}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz > /dev/null 2>&1
@@ -736,6 +745,44 @@ else
echo -e "$rc_skipped" echo -e "$rc_skipped"
fi fi
dovecot_pigeonhole_archiv="dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz"
else
echononl "\tDownload dovecot-pigeonhole-${_pigeonhole}.tar.gz.."
if [ ! -f "${_src_base_dir}/dovecot-pigeonhole-${_pigeonhole}.tar.gz" ]; then
wget --no-check-certificate https://pigeonhole.dovecot.org/releases/${dovecot_main_version}/dovecot-pigeonhole-${_pigeonhole}.tar.gz > /dev/null 2>&1
if [ "$?" = 0 ]; then
echo -e "$rc_done"
dovecot_pigeonhole_archiv="dovecot-pigeonhole-${_pigeonhole}.tar.gz"
else
echo -e "$rc_failed"
error "Direct download of 'Pigeonhole Sieve and ManageSieve' source archiv failed
Download Pigeonhole Sieve and ManageSieve manually and name it to
\033[1mdovecot-pigeonhole-${_pigeonhole}.tar.gz\033[m\n"
echononl "\tProceed instllation [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
fi
else
echo -e "$rc_skipped"
fi
dovecot_pigeonhole_archiv="dovecot-pigeonhole-${_pigeonhole}.tar.gz"
fi
dovecot_pigeonhole_archiv_prefix="${dovecot_pigeonhole_archiv%.tar.gz}"
dovecot_pigeonhole_archiv_dir="${dovecot_pigeonhole_archiv%.tar.gz}"
if $_new ; then if $_new ; then
@@ -901,6 +948,7 @@ config_params="
--prefix=/usr/local/dovecot-${_version} \ --prefix=/usr/local/dovecot-${_version} \
--with-${db_driver} \ --with-${db_driver} \
--with-gssapi=yes --with-gssapi=yes
--with-ldap=yes
--with-rundir=/run/dovecot" --with-rundir=/run/dovecot"
if $systemd_support ; then if $systemd_support ; then
config_params="$config_params \ config_params="$config_params \
@@ -1055,20 +1103,20 @@ fi
cd ${_src_base_dir} cd ${_src_base_dir}
echo "" echo ""
echononl "\tExtracting dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz.." echononl "\tExtracting dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz.."
gunzip < dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz | tar -xf - gunzip < ${_src_base_dir}/${dovecot_pigeonhole_archiv} | tar -C ${_src_base_dir} -xf -
if [ "$?" = 0 ]; then if [ "$?" = 0 ]; then
echo -e "$rc_done" echo -e "$rc_done"
else else
echo -e "$rc_failed" echo -e "$rc_failed"
fatal Extracting dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}.tar.gz failed fatal Extracting ${dovecot_pigeonhole_archiv} failed
fi fi
cd dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole} cd ${dovecot_pigeonhole_archiv_dir}
echononl "\tConfigure Pigeonhole ManageSieve.." echononl "\tConfigure Pigeonhole ManageSieve.."
./configure \ ./configure \
--prefix=/usr/local/dovecot-${_version} \ --prefix=/usr/local/dovecot-${_version} \
--with-dovecot=/usr/local/dovecot-${_version}/lib/dovecot > ${_log_dir}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}-configure.log 2<&1 --with-dovecot=/usr/local/dovecot-${_version}/lib/dovecot > ${_log_dir}/${dovecot_pigeonhole_archiv_prefix}-configure.log 2<&1
if [ "$?" = 0 ]; then if [ "$?" = 0 ]; then
echo -e "$rc_done" echo -e "$rc_done"
else else
@@ -1077,7 +1125,7 @@ else
fi fi
echononl "\tCompile Pigeonhole ManageSieve.." echononl "\tCompile Pigeonhole ManageSieve.."
make > ${_log_dir}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}-make.log 2<&1 make > ${_log_dir}/${dovecot_pigeonhole_archiv_prefix}-make.log 2<&1
if [ "$?" = 0 ]; then if [ "$?" = 0 ]; then
echo -e "$rc_done" echo -e "$rc_done"
else else
@@ -1086,7 +1134,7 @@ else
fi fi
echononl "\tInstall Pigeonhole ManageSieve.." echononl "\tInstall Pigeonhole ManageSieve.."
make install > ${_log_dir}/dovecot-${dovecot_main_version}-pigeonhole-${_pigeonhole}-install.log 2<&1 make install > ${_log_dir}/${dovecot_pigeonhole_archiv_prefix}-install.log 2<&1
if [ "$?" = 0 ]; then if [ "$?" = 0 ]; then
echo -e "$rc_done" echo -e "$rc_done"
else else
@@ -1095,6 +1143,7 @@ else
fi fi
## ----------------- ## -----------------
## --- Configure dovecot services ## --- Configure dovecot services