# SMTPGraphRelay SMTPGraphRelay ist ein nativer Windows-SMTP-Relay für interne Geräte und Anwendungen. Er nimmt klassische SMTP-Mails von Druckern, Scannern, NAS-Systemen, Servern, Monitoring-Tools und anderen Legacy-Geräten entgegen und versendet sie anschließend über **Microsoft Graph** mit **App-only OAuth2** über Microsoft 365. Der Relay läuft vollständig unter **Windows PowerShell 5.1** und benötigt keinen Docker-Container, keinen lokalen Exchange-Server und keinen klassischen SMTP-Smarthost. --- ## Überblick ```text Drucker / Scanner / NAS / Server / Monitoring | | SMTP v SMTPGraphRelay | | Store-and-Forward Queue v Microsoft Graph | | OAuth2 App-only v Microsoft 365 ``` SMTPGraphRelay wurde für den Betrieb in internen Netzwerken entwickelt. --- # Installation ## Schnellinstallation Auf dem Zielsystem **Windows PowerShell 5.1 als Administrator** öffnen und ausführen: ```powershell $u='https://me-gitea.maieredv.cloud/MAIEREDV/SMTPGraphRelay/raw/branch/main/Setup-SMTPGraphRelay.ps1';$f="$env:TEMP\Setup-SMTPGraphRelay.ps1";Invoke-WebRequest $u -UseBasicParsing -OutFile $f;& $f ``` Der Bootstrap-Installer lädt automatisch den aktuellen Stand aus dem Gitea-Repository und installiert SMTPGraphRelay nach: ```text C:\Program Files\SMTPGraphRelay ``` Es muss kein ZIP manuell heruntergeladen oder entpackt werden. --- # Setup / Manager Nach dem Start erscheint das Verwaltungsmenü: ```text [1] Neuinstallation aus Gitea [2] Installation aus Gitea reparieren [3] Nach Online-Updates suchen [4] Entra / Exchange RBAC prüfen [5] Zertifikat erneuern [6] Health Check ausführen [7] Deinstallieren [8] Status anzeigen [9] SMTP-AUTH verwalten [10] Failed Queue verwalten [11] Failure-Mode QA [0] Beenden ``` --- # Neuinstallation Die Neuinstallation erledigt automatisch: - Prüfung auf Windows PowerShell 5.1 - Prüfung auf Administratorrechte - Download der aktuellen Programmdateien aus Gitea - Installation nach `C:\Program Files\SMTPGraphRelay` - Installation der benötigten Microsoft-Graph-PowerShell-Module - Installation von `ExchangeOnlineManagement` - Erstellung eines lokalen Zertifikats - Erstellung der Entra ID App Registration - Erstellung des Entra Service Principals - Einrichtung von Exchange Online Application RBAC - Beschränkung von `Application Mail.Send` auf das konfigurierte Absenderpostfach - Erstellung der `config.json` - Erstellung der Queue-/Failed-/Log-Verzeichnisse - Erstellung der Windows-Firewallregel - Erstellung des Scheduled Tasks unter `SYSTEM` - Start des Relay - abschließender Health Check --- # Microsoft 365 / Graph SMTPGraphRelay verwendet Microsoft Graph mit App-only Authentifizierung. Der Versand erfolgt über: ```text POST /v1.0/users//sendMail ``` Die Authentifizierung erfolgt mit: ```text Tenant ID Client ID Certificate Thumbprint ``` Es wird kein Client Secret verwendet. --- # Exchange Application RBAC Der Relay verwendet bewusst **keine tenantweite Microsoft Graph `Mail.Send` Application Permission**. Stattdessen wird Exchange Online Application RBAC verwendet: ```text Application Mail.Send | v Custom Resource Scope | v konfiguriertes SenderMailbox ``` Beispiel: ```text SMTPGraphRelay App | | Application Mail.Send v info@example.com ``` Dadurch kann die Anwendung ausschließlich über das konfigurierte Absenderpostfach senden. --- # Zertifikatsauthentifizierung Das Setup erzeugt ein RSA-Zertifikat unter: ```text Cert:\LocalMachine\My ``` Eigenschaften: ```text RSA 2048 SHA256 Private Key nicht exportierbar Gültigkeit: 2 Jahre ``` Der Relay überwacht die Zertifikatslaufzeit regelmäßig. Standard: ```text Warnung: 60 Tage Kritisch: 14 Tage Prüfung: alle 12 Stunden ``` Das Zertifikat kann über den Manager erneuert werden: ```text [5] Zertifikat erneuern ``` Die Rotation erfolgt mit Überlappung: ```text altes Zertifikat | v neues Zertifikat erstellen | v neues Credential zusätzlich in Entra hinterlegen | v App-only Login mit neuem Zertifikat testen | v config.json auf neues Zertifikat umstellen | v Relay neu starten | v altes Zertifikat optional entfernen ``` --- # SMTP-Funktionen Unterstützte SMTP-Kommandos: ```text EHLO HELO MAIL FROM RCPT TO DATA RSET NOOP QUIT AUTH LOGIN AUTH PLAIN ``` STARTTLS wird bewusst nicht verwendet, da SMTPGraphRelay für interne, vertrauenswürdige Netzwerke vorgesehen ist. --- # SMTP Listener Standardmäßig: ```text ListenAddress: 0.0.0.0 Port: 2525 ``` Der Port kann in `config.json` geändert werden. --- # IP-Allowlist SMTPGraphRelay akzeptiert ausschließlich Clients aus konfigurierten Netzwerken. Beispiel: ```json "AllowedNetworks": [ "127.0.0.1/32", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ] ``` Nicht erlaubte Hosts erhalten: ```text 554 5.7.1 Client not allowed ``` Die IP-Allowlist bleibt auch bei aktiviertem SMTP-AUTH immer die erste Zugriffskontrolle. --- # SMTP-AUTH SMTPGraphRelay unterstützt: ```text AUTH LOGIN AUTH PLAIN ``` SMTP-AUTH kann optional oder verpflichtend aktiviert werden. Beispiel: ```json "RequireAuth": true ``` Clients ohne erfolgreiche Authentifizierung erhalten dann: ```text 530 5.7.0 Authentication required ``` Erfolgreiche Anmeldung: ```text 235 2.7.0 Authentication successful ``` Fehlgeschlagene Anmeldung: ```text 535 5.7.8 Authentication credentials invalid ``` Nach mehreren Fehlversuchen kann die Verbindung temporär gesperrt werden: ```text 454 4.7.0 Too many authentication failures ``` Standard: ```text AuthMaxFailures = 5 ``` --- # SMTP-Benutzerverwaltung Im Manager: ```text [9] SMTP-AUTH verwalten ``` Funktionen: ```text [1] SMTP-AUTH erforderlich EIN/AUS [2] Benutzer hinzufügen [3] Benutzer anzeigen [4] Passwort ändern [5] Benutzer löschen [6] Netze ohne Auth verwalten [7] Max. Fehlversuche ändern [0] Zurück ``` Passwörter werden nicht im Klartext gespeichert. Verwendet wird: ```text PBKDF2-HMAC-SHA256 zufälliger Salt 150000 Iterationen 32 Byte Hash ``` --- # AUTH-Ausnahmen Bestimmte Netze können trotz `RequireAuth=true` ohne SMTP-AUTH zugelassen werden: ```json "AllowUnauthenticatedNetworks": [ "10.60.10.0/24" ] ``` Diese Clients müssen trotzdem zusätzlich in `AllowedNetworks` enthalten sein. --- # Absenderkontrolle Der SMTP-Benutzer beeinflusst nicht den tatsächlichen Microsoft-365-Absender. Mit: ```json "ForceSender": true ``` wird immer `Graph.SenderMailbox` als Absender verwendet. --- # Parallele SMTP-Verbindungen SMTPGraphRelay verarbeitet mehrere SMTP-Clients parallel über einen PowerShell-Runspace-Pool. Standard: ```text MaxConcurrentClients = 20 ``` Ist das Limit erreicht: ```text 421 4.3.2 SMTPGraphRelay busy, try again later ``` --- # SMTP-Limits Standardwerte: ```text MaxRecipients = 50 MaxMessagesPerConnection = 25 MaxMessageSizeMB = 25 ``` Zu viele Empfänger: ```text 452 4.5.3 Too many recipients ``` Zu viele Nachrichten innerhalb einer Verbindung: ```text 452 4.5.3 Too many messages in this session ``` Zu große Nachricht: ```text 552 5.3.4 Message size exceeds fixed maximum message size ``` --- # Store-and-Forward Queue Queue-Struktur: ```text queue\ ├── incoming\ ├── pending\ └── processing\ failed\ ``` - `incoming`: Nachricht wird atomar geschrieben. - `pending`: vollständig gespeicherte, versandbereite Nachrichten. - `processing`: aktuell durch den Graph-Worker verarbeitete Nachrichten. - `failed`: dauerhaft fehlgeschlagene Nachrichten. --- # Queue-ID Jede Nachricht erhält eine eindeutige Queue-ID. Beispiel: ```text 7F3A91C2D441 ``` SMTP-Antwort: ```text 250 2.0.0 Message accepted for delivery; queue-id=7F3A91C2D441 ``` Die Queue-ID wird ebenfalls in den Logs verwendet. --- # Received-Header und Message-ID SMTPGraphRelay fügt einen eigenen `Received:`-Header hinzu. Besitzt eine eingehende Nachricht keine `Message-ID`, erzeugt der Relay automatisch eine. Bereits vorhandene Message-IDs bleiben unverändert. --- # Retry-Logik Standard-Retry-Zeiten: ```text 1 Minute 5 Minuten 15 Minuten 30 Minuten 60 Minuten 120 Minuten 240 Minuten 480 Minuten ``` Standard: ```text MaxRetries = 8 ``` Automatische Wiederholung erfolgt bei: ```text HTTP 408 HTTP 429 HTTP 5xx DNS-Fehler TLS-/Transportfehler Timeouts Netzwerkfehler ``` Bei `429 Too Many Requests` wird `Retry-After` berücksichtigt. Typische permanente Fehler wie `400`, `401`, `403`, `404`, `409`, `410`, `412`, `413`, `415`, `422` und `423` werden direkt nach `failed` verschoben. --- # Queue-Backpressure Standard: ```text MaxPendingMessages = 5000 MinFreeDiskSpaceMB = 1024 ``` Queue-Limit erreicht: ```text 452 4.3.1 SMTPGraphRelay queue limit reached ``` Zu wenig freier Speicher: ```text 452 4.3.1 Insufficient system storage ``` Kann eine Nachricht nach `DATA` nicht atomar gespeichert werden: ```text 451 4.3.0 SMTPGraphRelay queue temporarily unavailable ``` --- # Failed Queue Im Manager: ```text [10] Failed Queue verwalten ``` Funktionen: ```text [1] Failed Queue anzeigen [2] Details einer Mail anzeigen [3] Eine Mail erneut zustellen [4] Alle Mails erneut zustellen [5] Eine Mail endgültig löschen [6] Alle Failed-Mails endgültig löschen [0] Zurück ``` Beim erneuten Zustellen wird die Nachricht zurück nach `queue\pending` verschoben. --- # Graceful Shutdown Update, Repair und kontrollierte Stop-Vorgänge beenden den Relay nicht sofort hart. Der Manager erzeugt: ```text C:\Program Files\SMTPGraphRelay\shutdown.request ``` Der Relay schließt zuerst den Listener, nimmt keine neuen Verbindungen mehr an, lässt laufende SMTP-Sessions und den aktuellen Queue-/Graph-Versand auslaufen und beendet sich anschließend selbst. Standard: ```text GracefulShutdownSeconds = 30 ``` Bei Timeout fällt der Manager auf einen harten Scheduled-Task-Stop zurück. --- # Logging Logdatei: ```text C:\Program Files\SMTPGraphRelay\logs\SMTPGraphRelay.log ``` Log-Rotation: ```text MaxFileSizeMB = 10 RetentionDays = 30 CleanupHours = 12 ``` Archivierte Logs: ```text SMTPGraphRelay-YYYYMMDD-HHMMSS.log ``` --- # Health Check Im Manager: ```text [6] Health Check ausführen ``` oder direkt: ```powershell .\Test-SMTPGraphRelay.ps1 ``` Geprüft werden unter anderem: - Windows PowerShell 5.1 - Administratorstatus - `config.json` - Graph-PowerShell-Modul - Zertifikat und Private Key - Zertifikatslaufzeit - Queue-Verzeichnisse und Schreibrechte - Pending / Processing / Failed Queue - Scheduled Task - SMTP Listener - Graph App-only Login - SMTP-AUTH-Konfiguration Exitcodes: ```text 0 = OK 1 = Warnung 2 = Fehler ``` --- # SMTP-Testmail ```powershell .\Test-SMTPGraphRelay.ps1 ` -SendTestMail ` -TestRecipient "user@example.com" ``` Bei aktiviertem SMTP-AUTH: ```powershell .\Test-SMTPGraphRelay.ps1 ` -SendTestMail ` -TestRecipient "user@example.com" ` -SmtpUsername "scanner01" ``` Das Passwort wird interaktiv und verdeckt abgefragt. --- # Online Update Im Manager: ```text [3] Nach Online-Updates suchen ``` Der Manager prüft die `version.json` im Gitea-Repository und lädt bei einer neuen Version das aktuelle `main.zip`. Vor dem Update werden die verwalteten Programmdateien gesichert. Ablauf: ```text Versionsprüfung | v Repository laden | v Backup | v Graceful Shutdown | v Programmdateien aktualisieren | v Relay starten | v Health Check | +--> Fehler -> automatischer Rollback ``` --- # Repair Im Manager: ```text [2] Installation aus Gitea reparieren ``` Repair lädt den aktuellen Programmstand erneut aus Gitea und stellt fehlende oder beschädigte Programmdateien wieder her. Dabei bleiben erhalten: ```text config.json queue\ failed\ logs\ backup\ Zertifikat Entra App Registration Exchange Application RBAC ``` --- # Failure-Mode QA Im Manager: ```text [11] Failure-Mode QA ``` oder direkt: ```powershell .\Test-SMTPGraphRelay-FailureModes.ps1 ``` Tests: ```text [1] Graph HTTP 429 / Retry testen [2] Graph HTTP 500 / Retry testen [3] Queue-Schreibfehler / SMTP 451 testen [4] Pending-Queue-Backpressure / SMTP 452 testen [5] Disk-Backpressure / SMTP 452 testen [6] Graceful Shutdown während SMTP DATA testen [7] Graceful Shutdown während Queue/Graph testen [8] QA-/Debug-Block aus Config entfernen [0] Beenden ``` Die ursprüngliche `config.json` wird vor jedem Test gesichert und anschließend automatisch wiederhergestellt. --- # Scheduled Task SMTPGraphRelay läuft als Scheduled Task unter: ```text SYSTEM ``` Ausgeführt wird explizit: ```text C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ``` mit: ```text -NoLogo -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\SMTPGraphRelay\SMTPGraphRelay.ps1" -ConfigPath "C:\Program Files\SMTPGraphRelay\config.json" ``` --- # Verzeichnisstruktur ```text C:\Program Files\SMTPGraphRelay\ │ ├── SMTPGraphRelay.ps1 ├── Setup-SMTPGraphRelay.ps1 ├── Test-SMTPGraphRelay.ps1 ├── Test-SMTPGraphRelay-FailureModes.ps1 ├── Renew-SMTPGraphRelayCertificate.ps1 ├── version.json ├── config.json │ ├── queue\ │ ├── incoming\ │ ├── pending\ │ └── processing\ │ ├── failed\ ├── logs\ └── backup\ ``` --- # Systemvoraussetzungen - Windows 10 / Windows 11 oder Windows Server - Windows PowerShell 5.1 - lokale Administratorrechte für Installation und Verwaltung - Zugriff auf das Gitea-Repository - Internetzugang zu Microsoft Entra ID - Internetzugang zu Microsoft Graph - Internetzugang zu Exchange Online - Microsoft-365-/Exchange-Online-Postfach als Relay-Absender - Entra-/Exchange-Administratorkonto für die Ersteinrichtung --- # Repository ```text https://me-gitea.maieredv.cloud/MAIEREDV/SMTPGraphRelay ``` --- # Betriebskonzept SMTPGraphRelay ist für den internen Einsatz vorgesehen. Empfohlene Absicherung: ```text interne Netzwerksegmente + AllowedNetworks + optional SMTP-AUTH + ForceSender + Exchange Application RBAC ``` Dadurch bleibt sowohl der lokale Zugriff als auch der tatsächliche Microsoft-365-Versand klar eingeschränkt.