From a89d53ce26e7c7b6f7a4f0d9ea4aa9a8e2e61eb7 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Wed, 3 Dec 2025 17:54:32 -0600 Subject: [PATCH] update --- DMS/update_dms_config.sh | 91 +++++++++++++ basic_setup/awsses.sh | 281 ++++++++++++++++++++++----------------- 2 files changed, 251 insertions(+), 121 deletions(-) create mode 100644 DMS/update_dms_config.sh diff --git a/DMS/update_dms_config.sh b/DMS/update_dms_config.sh new file mode 100644 index 0000000..3fb73bf --- /dev/null +++ b/DMS/update_dms_config.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# update_dms_config.sh +# Fügt eine neue Domain zur lokalen DMS Konfiguration hinzu: +# 1. Ergänzt SRS_EXCLUDE_DOMAINS in docker-compose.yml +# 2. Ergänzt Whitelist in smtp_header_checks + +set -e + +DOMAIN=$1 +DOCKER_COMPOSE_FILE="./docker-compose.yml" +HEADER_CHECKS_FILE="./docker-data/dms/config/postfix/smtp_header_checks" + +if [ -z "$DOMAIN" ]; then + echo "Usage: $0 " + echo "Example: $0 cielectrical.com" + exit 1 +fi + +echo "=== Aktualisiere lokale Konfiguration für $DOMAIN ===" + +# --------------------------------------------- +# 1. Update docker-compose.yml (SRS Exclude) +# --------------------------------------------- +if [ -f "$DOCKER_COMPOSE_FILE" ]; then + echo "-> Prüfe docker-compose.yml..." + + # Prüfen, ob Domain schon in der Zeile steht + if grep -q "SRS_EXCLUDE_DOMAINS=.*$DOMAIN" "$DOCKER_COMPOSE_FILE"; then + echo " Domain bereits in SRS_EXCLUDE_DOMAINS vorhanden." + else + # Backup erstellen + cp "$DOCKER_COMPOSE_FILE" "${DOCKER_COMPOSE_FILE}.bak" + + # sed Magie: Suche Zeile mit SRS_EXCLUDE_DOMAINS, hänge ",domain" am Ende an + # Wir nutzen ein Komma als Trenner vor der neuen Domain + sed -i "s/SRS_EXCLUDE_DOMAINS=.*/&,$DOMAIN/" "$DOCKER_COMPOSE_FILE" + echo " ✅ $DOMAIN zu SRS_EXCLUDE_DOMAINS hinzugefügt." + fi +else + echo "❌ Fehler: $DOCKER_COMPOSE_FILE nicht gefunden!" + exit 1 +fi + +# --------------------------------------------- +# 2. Update smtp_header_checks (PCRE Whitelist) +# --------------------------------------------- +if [ -f "$HEADER_CHECKS_FILE" ]; then + echo "-> Prüfe smtp_header_checks..." + + # Domain für Regex escapen (der Punkt muss \. sein) + ESCAPED_DOMAIN="${DOMAIN//./\\.}" + NEW_LINE="/.*@${ESCAPED_DOMAIN}/ DUNNO" + + # Prüfen, ob Eintrag existiert + if grep -Fq "@$ESCAPED_DOMAIN/" "$HEADER_CHECKS_FILE"; then + echo " Domain bereits in smtp_header_checks vorhanden." + else + # Backup erstellen + cp "$HEADER_CHECKS_FILE" "${HEADER_CHECKS_FILE}.bak" + + # Wir fügen die Zeile oben bei den Whitelists ein (nach dem Kommentar "# 1. EIGENE...") + # Oder einfach am Anfang der Datei, falls die Reihenfolge egal ist. + # Aber bei PCRE ist Reihenfolge wichtig! Whitelist muss VOR Rewrite stehen. + + # Strategie: Wir suchen die erste Zeile, die mit /.*@ anfängt und fügen davor ein + # Oder wir hängen es einfach oben an einen definierten Marker an. + + # Einfachste sichere Methode für dein File: Nach dem Kommentarblock einfügen + # Wir suchen nach der Zeile mit "1. EIGENE DOMAINS" und fügen 3 Zeilen später ein + # Aber sed insert ist tricky. + + # Bessere Methode: Wir wissen, dass Whitelists ganz oben stehen sollen. + # Wir erstellen eine temporäre Datei. + + # 1. Header (Kommentare) behalten oder neu schreiben? + # Wir hängen es einfach GANZ OBEN in die Datei ein (vor alle anderen Regeln), + # das ist bei "DUNNO" (Whitelist) immer sicherste Variante. + + sed -i "1i $NEW_LINE" "$HEADER_CHECKS_FILE" + + echo " ✅ $DOMAIN zu smtp_header_checks hinzugefügt (ganz oben)." + fi +else + echo "⚠️ Warnung: $HEADER_CHECKS_FILE nicht gefunden. Überspringe." +fi + +echo "========================================================" +echo "Konfiguration aktualisiert." +echo "HINWEIS: Damit die Änderungen wirksam werden, führen Sie bitte aus:" +echo " docker compose up -d --force-recreate" +echo "========================================================" \ No newline at end of file diff --git a/basic_setup/awsses.sh b/basic_setup/awsses.sh index dba296b..1aea4f5 100755 --- a/basic_setup/awsses.sh +++ b/basic_setup/awsses.sh @@ -1,5 +1,22 @@ #!/bin/bash -# awsses.sh - Konfiguriert Amazon SES für eine Domain und erstellt eine Receipt Rule +# awsses.sh - Konfiguriert Amazon SES mit SNS->SQS Fanout Architektur & Outbound Tracking +# +# Ablauf: +# 1. SES Domain Identity erstellen/verifizieren +# 2. Domain mit Configuration Set verknüpfen (für Outbound Tracking) +# 3. SNS Topic erstellen +# 4. SNS Topic Policy setzen (damit SES hineinschreiben darf) +# 5. SQS Queue verbinden (Subscription) +# 6. SQS Queue Policy setzen (damit SNS hineinschreiben darf) +# 7. SES Receipt Rule erstellen (S3 Action + SNS Action) + +set -e + +# Überprüfen, ob jq installiert ist +if ! command -v jq &> /dev/null; then + echo "Fehler: 'jq' ist nicht installiert. Bitte installieren (sudo apt-get install jq)." + exit 1 +fi # Überprüfen, ob die Domain-Variable gesetzt ist if [ -z "$DOMAIN_NAME" ]; then @@ -11,7 +28,6 @@ fi # Überprüfen, ob S3_BUCKET_NAME gesetzt ist if [ -z "$S3_BUCKET_NAME" ]; then echo "Warnung: S3_BUCKET_NAME ist nicht gesetzt." - echo "Wird automatisch aus DOMAIN_NAME generiert, verwenden Sie idealerweise zuerst awss3.sh." S3_BUCKET_NAME=$(echo "$DOMAIN_NAME" | tr '.' '-' | awk '{print $0 "-emails"}') echo "Generierter Bucket-Name: $S3_BUCKET_NAME" fi @@ -19,161 +35,184 @@ fi # Konfiguration AWS_REGION=${AWS_REGION:-"us-east-2"} EMAIL_PREFIX=${EMAIL_PREFIX:-""} -RULE_NAME="store-$(echo "$DOMAIN_NAME" | tr '.' '-')-to-s3" +CONFIGURATION_SET_NAME="relay-outbound" # Name deines globalen Config Sets -echo "=== SES Konfiguration für $DOMAIN_NAME ===" +# Naming Conventions +RULE_SET_NAME="bizmatch-ruleset" +RULE_NAME="store-${DOMAIN_NAME//./-}-to-s3" +TOPIC_NAME="${DOMAIN_NAME//./-}-topic" +QUEUE_NAME="${DOMAIN_NAME//./-}-queue" + +echo "========================================================" +echo " SES Setup (Full Architecture) für $DOMAIN_NAME" +echo "========================================================" echo "Region: $AWS_REGION" echo "S3 Bucket: $S3_BUCKET_NAME" -echo "Receipt Rule Name: $RULE_NAME" +echo "Config Set: $CONFIGURATION_SET_NAME" +echo "--------------------------------------------------------" -# ------------------------ -# SES Domain-Identität erstellen # ------------------------ -echo "SES Domain-Identität prüfen/erstellen..." +# 1. SES Domain Identität +# ------------------------ +echo "[1/7] Prüfe SES Domain Identität..." -# Wir prüfen erst, ob es die Identity schon gibt (get-email-identity) -# Wenn NICHT (!), dann erstellen wir sie. if ! aws sesv2 get-email-identity --email-identity ${DOMAIN_NAME} --region ${AWS_REGION} >/dev/null 2>&1; then - - echo "Identity existiert nicht, wird erstellt..." - IDENTITY_RESULT=$(aws sesv2 create-email-identity \ - --email-identity ${DOMAIN_NAME} \ - --region ${AWS_REGION}) - - echo "Identity erstellt. Überprüfen Sie die DNS-Einträge für die Domain-Verifizierung." - echo "$IDENTITY_RESULT" | jq . - + echo "-> Erstelle Identity..." + aws sesv2 create-email-identity --email-identity ${DOMAIN_NAME} --region ${AWS_REGION} >/dev/null else - echo "Identity '${DOMAIN_NAME}' existiert bereits. Überspringe Erstellung." + echo "-> Identity existiert bereits." fi -# DKIM-Signierung aktivieren -echo "DKIM-Signierung aktivieren..." -aws sesv2 put-email-identity-dkim-attributes \ - --email-identity ${DOMAIN_NAME} \ - --signing-enabled \ - --region ${AWS_REGION} +# Config Updates (Idempotent) +echo "-> Konfiguriere DKIM & Mail-From..." +aws sesv2 put-email-identity-dkim-attributes --email-identity ${DOMAIN_NAME} --signing-enabled --region ${AWS_REGION} +aws sesv2 put-email-identity-mail-from-attributes --email-identity ${DOMAIN_NAME} --mail-from-domain "mail.${DOMAIN_NAME}" --behavior-on-mx-failure USE_DEFAULT_VALUE --region ${AWS_REGION} -# Mail-From-Domain konfigurieren -echo "Mail-From-Domain konfigurieren..." -aws sesv2 put-email-identity-mail-from-attributes \ +# ------------------------ +# 2. Configuration Set Verknüpfung (NEU!) +# ------------------------ +echo "[2/7] Verknüpfe Domain mit Outbound Configuration Set..." +# Dies sorgt dafür, dass ausgehende Mails getrackt werden (für OOO/Bounces) +aws sesv2 put-email-identity-configuration-set-attributes \ --email-identity ${DOMAIN_NAME} \ - --mail-from-domain "mail.${DOMAIN_NAME}" \ - --behavior-on-mx-failure USE_DEFAULT_VALUE \ + --configuration-set-name "$CONFIGURATION_SET_NAME" \ --region ${AWS_REGION} # ------------------------ -# Rule Set & Receipt Rule +# 3. SNS Topic erstellen # ------------------------ +echo "[3/7] Erstelle/Prüfe SNS Topic..." +TOPIC_ARN=$(aws sns create-topic --name "$TOPIC_NAME" --region "$AWS_REGION" --output text --query 'TopicArn') +echo "-> Topic ARN: $TOPIC_ARN" -# 1. Rule Set prüfen/erstellen -echo "Überprüfe Rule Set 'bizmatch-ruleset'..." -# Korrekter Befehl ist 'list-receipt-rule-sets' +# ------------------------ +# 4. SNS Policy (SES -> SNS) +# ------------------------ +echo "[4/7] Setze SNS Policy (SES darf publishen)..." +ACCOUNT_ID=$(echo "$TOPIC_ARN" | cut -d: -f5) + +SNS_POLICY=$(jq -n \ + --arg topic_arn "$TOPIC_ARN" \ + --arg account_id "$ACCOUNT_ID" \ + '{ + Version: "2008-10-17", + Id: "__default_policy_ID", + Statement: [ + { + Sid: "Allow-SES-Publish", + Effect: "Allow", + Principal: { Service: "ses.amazonaws.com" }, + Action: "sns:Publish", + Resource: $topic_arn, + Condition: { + StringEquals: { "AWS:SourceAccount": $account_id } + } + } + ] + }' | jq -c .) + +aws sns set-topic-attributes --topic-arn "$TOPIC_ARN" --attribute-name Policy --attribute-value "$SNS_POLICY" --region "$AWS_REGION" + +# ------------------------ +# 5. SQS Queue Verbindung +# ------------------------ +echo "[5/7] Verbinde SQS Queue..." + +# Queue URL & ARN holen (Queue muss existieren -> create-queue.sh vorher ausführen!) +QUEUE_URL=$(aws sqs get-queue-url --queue-name "$QUEUE_NAME" --region "$AWS_REGION" --output text --query 'QueueUrl' 2>/dev/null) + +if [ -z "$QUEUE_URL" ]; then + echo "FEHLER: Queue $QUEUE_NAME nicht gefunden!" + echo "Bitte führen Sie erst ./create-queue.sh aus." + exit 1 +fi + +QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url "$QUEUE_URL" --attribute-names QueueArn --region "$AWS_REGION" --output text --query 'Attributes.QueueArn') + +# Subscription erstellen (Idempotent) +aws sns subscribe --topic-arn "$TOPIC_ARN" --protocol sqs --notification-endpoint "$QUEUE_ARN" --region "$AWS_REGION" > /dev/null + +# ------------------------ +# 6. SQS Policy (SNS -> SQS) +# ------------------------ +echo "[6/7] Setze SQS Policy (SNS darf schreiben)..." + +SQS_POLICY=$(jq -n \ + --arg queue_arn "$QUEUE_ARN" \ + --arg topic_arn "$TOPIC_ARN" \ + '{ + Version: "2012-10-17", + Id: "SNS-to-SQS", + Statement: [ + { + Sid: "Allow-SNS-SendMessage", + Effect: "Allow", + Principal: { Service: "sns.amazonaws.com" }, + Action: "sqs:SendMessage", + Resource: $queue_arn, + Condition: { + ArnEquals: { "aws:SourceArn": $topic_arn } + } + } + ] + }' | jq -c .) + +# Policy setzen (mit Single-Quote Schutz für AWS CLI) +aws sqs set-queue-attributes --queue-url "$QUEUE_URL" --attributes Policy="'$SQS_POLICY'" --region "$AWS_REGION" + +# ------------------------ +# 7. SES Rule Set & Rule +# ------------------------ +echo "[7/7] Konfiguriere SES Receipt Rule..." + +# Rule Set prüfen RULESET_EXISTS=$(aws ses list-receipt-rule-sets --region ${AWS_REGION} | jq -r '.RuleSets[] | select(.Name == "bizmatch-ruleset") | .Name') - if [ "$RULESET_EXISTS" != "bizmatch-ruleset" ]; then - echo "Receipt Rule Set 'bizmatch-ruleset' existiert nicht, wird erstellt..." + echo "-> Erstelle Rule Set 'bizmatch-ruleset'..." aws ses create-receipt-rule-set --rule-set-name "bizmatch-ruleset" --region ${AWS_REGION} -else - echo "Receipt Rule Set 'bizmatch-ruleset' existiert bereits." fi -# 2. Receipt Rule prüfen/erstellen -echo "Überprüfe Receipt Rule '${RULE_NAME}'..." - -# Wir prüfen, ob die Regel schon existiert. Wenn NICHT (!), erstellen wir sie. -if ! aws ses describe-receipt-rule --rule-set-name "bizmatch-ruleset" --rule-name "${RULE_NAME}" --region ${AWS_REGION} >/dev/null 2>&1; then +# Rule prüfen/erstellen +if ! aws ses describe-receipt-rule --rule-set-name "$RULE_SET_NAME" --rule-name "${RULE_NAME}" --region ${AWS_REGION} >/dev/null 2>&1; then - echo "Erstelle Receipt Rule '${RULE_NAME}'..." - aws ses create-receipt-rule --rule-set-name "bizmatch-ruleset" --rule '{ + echo "-> Erstelle Receipt Rule '${RULE_NAME}'..." + + # Rule mit S3 Action UND SNS Action + # HINWEIS: Hier fügen wir initial die Hauptdomain als Recipient hinzu. + # Denke daran, später ./manage_mail_user.sh sync ... auszuführen! + aws ses create-receipt-rule --rule-set-name "$RULE_SET_NAME" --rule '{ "Name": "'"${RULE_NAME}"'", "Enabled": true, "ScanEnabled": true, - "Actions": [{ - "S3Action": { - "BucketName": "'"${S3_BUCKET_NAME}"'", - "ObjectKeyPrefix": "'"${EMAIL_PREFIX}"'" + "Actions": [ + { + "S3Action": { + "BucketName": "'"${S3_BUCKET_NAME}"'", + "ObjectKeyPrefix": "'"${EMAIL_PREFIX}"'" + } + }, + { + "SNSAction": { + "TopicArn": "'"${TOPIC_ARN}"'", + "Encoding": "UTF-8" + } } - }], + ], "TlsPolicy": "Require", "Recipients": ["'"${DOMAIN_NAME}"'"] }' --region ${AWS_REGION} else - echo "Receipt Rule '${RULE_NAME}' existiert bereits. Überspringe Erstellung." + echo "-> Receipt Rule '${RULE_NAME}' existiert bereits (Überspringe Erstellung)." fi -# 3. Rule Set aktivieren (falls noch nicht aktiv) +# Rule Set aktivieren ACTIVE_RULESET=$(aws ses describe-active-receipt-rule-set --region ${AWS_REGION} | jq -r '.Metadata.Name') - if [ "$ACTIVE_RULESET" != "bizmatch-ruleset" ]; then - echo "Aktiviere Rule Set 'bizmatch-ruleset'..." + echo "-> Aktiviere Rule Set..." aws ses set-active-receipt-rule-set --rule-set-name "bizmatch-ruleset" --region ${AWS_REGION} -else - echo "Rule Set 'bizmatch-ruleset' ist bereits aktiv." fi -# ------------------------ -# Lambda-Funktion mit SES verknüpfen -# ------------------------ -echo "Verknüpfe Lambda-Funktion 'ses-to-sqs' mit SES..." - -# Lambda ARN ermitteln -LAMBDA_ARN=$(aws lambda get-function \ - --function-name ses-to-sqs \ - --region ${AWS_REGION} \ - --query 'Configuration.FunctionArn' \ - --output text) - -if [ -z "$LAMBDA_ARN" ]; then - echo "FEHLER: Lambda-Funktion 'ses-to-sqs' nicht gefunden!" - echo "Bitte zuerst Lambda-Funktion deployen." - exit 1 -fi - -echo "Lambda ARN: $LAMBDA_ARN" - -# SES Permission für Lambda hinzufügen (falls noch nicht vorhanden) -echo "Füge SES-Berechtigung zur Lambda-Funktion hinzu..." -aws lambda add-permission \ - --function-name ses-to-sqs \ - --statement-id "AllowSESInvoke-${DOMAIN_NAME//./}" \ - --action "lambda:InvokeFunction" \ - --principal ses.amazonaws.com \ - --source-account $(aws sts get-caller-identity --query Account --output text) \ - --region ${AWS_REGION} 2>/dev/null || echo "Permission bereits vorhanden" - -# Receipt Rule UPDATE: Lambda Action hinzufügen -echo "Aktualisiere Receipt Rule mit Lambda Action..." -aws ses update-receipt-rule --rule-set-name "bizmatch-ruleset" --rule '{ - "Name": "'"${RULE_NAME}"'", - "Enabled": true, - "ScanEnabled": true, - "Actions": [ - { - "S3Action": { - "BucketName": "'"${S3_BUCKET_NAME}"'", - "ObjectKeyPrefix": "'"${EMAIL_PREFIX}"'" - } - }, - { - "LambdaAction": { - "FunctionArn": "'"${LAMBDA_ARN}"'", - "InvocationType": "Event" - } - } - ], - "TlsPolicy": "Require", - "Recipients": ["'"${DOMAIN_NAME}"'"] -}' --region ${AWS_REGION} - -echo "✅ Lambda-Funktion erfolgreich mit SES verknüpft!" - -echo "SES-Konfiguration für $DOMAIN_NAME abgeschlossen." -echo -echo "WICHTIG: Überprüfen Sie die Ausgabe oben für DNS-Einträge, die Sie bei Ihrem DNS-Provider setzen müssen:" -echo "1. DKIM-Einträge (3 CNAME-Einträge)" -echo "2. MAIL FROM MX und TXT-Einträge" -echo "3. SPF-Eintrag (TXT): v=spf1 include:amazonses.com ~all" -echo -echo "Nach dem Setzen der DNS-Einträge kann es bis zu 72 Stunden dauern, bis die Verifizierung abgeschlossen ist." \ No newline at end of file +echo "========================================================" +echo "✅ Setup erfolgreich abgeschlossen für $DOMAIN_NAME" +echo "========================================================" \ No newline at end of file