Architektur
Komponentenarchitektur
Abschnitt betitelt „Komponentenarchitektur“Während grommunio als appliance ausgeliefert wird, ist grommunio auch als Softwarepaket erhältlich. Die Paketierung orientiert sich an der modularen Komponentenstruktur der in grommunio verfügbaren Softwaremodule. Der modulare Komponentenaufbau von grommunio ermöglicht eine komponentenbasierte Bereitstellung für alle Arten von Implementierungen und Betriebsszenarien – von Einzelimplementierungen mit hochdichter Komponentenanordnung bis hin zu skalierbaren, verteilten Komponentenanordnungen.
Das Layout der grommunio-Komponente unterstützt eine Vielzahl von Einsatzarten:
- Containers (Docker, LXC)
- Virtuelle Maschinen (KVM, VMware, Hyper-V)
Die von grommunio ausgelieferten Appliances enthalten alle für den Betrieb erforderlichen Komponenten in Form von Paketen, die über Repositorys aktualisiert werden können. Die appliances liefern diese Pakete als Teil der Appliance-Distribution aus, um den Betrieb der Installation ohne die Aktivierung externer Repositories zu ermöglichen (wobei eine Bereitstellung mit aktiver Internetverbindung für Updates während der Bereitstellung dringend empfohlen wird).
Um die Komponentenarchitektur und die Vernetzung dieser Komponenten zu verstehen, werden in den folgenden Kapiteln die einzelnen Komponenten vorgestellt und erläutert, wie sie mit anderen Komponenten im gesamten Komponentenstapel zusammenwirken.
Multi-Server-Architektur
Abschnitt betitelt „Multi-Server-Architektur“Die Skalierung von grommunio über mehrere Instanzen hinweg („Multi-Server“) erfordert ein grundlegendes Verständnis von Scale-out-Lösungen. Für die meisten Benutzer ist der Single-Point-of-Entry-Ansatz (SPOE) vorzuziehen, da sie sich so nicht merken müssen, auf welchem Server sich ihr Profil befindet. In groß angelegten Umgebungen mit Zehntausenden von Benutzern erfordern jedoch selbst die fortschrittlichsten Serversysteme eine Verteilung auf mehrere Knoten, um die Last effizient zu bewältigen.
Die Rolle von Autodiscover, Autoconfig und Load Balancern
Autodiscover bietet in Kombination mit Load Balancern einen einheitlichen Zugangspunkt und stellt sicher, dass sich Benutzer nahtlos mit ihren Postfächern verbinden können, ohne sich Gedanken über die Verteilung im Backend machen zu müssen. Damit dies funktioniert, muss das System wissen, wo die Daten der einzelnen Benutzer gespeichert sind – unabhängig davon, ob sie einen IMAP-Client oder die Weboberfläche nutzen.
Wenn eine Anfrage bei einem Load Balancer eingeht, kann sie an Knoten A weitergeleitet werden, obwohl sich die Daten des Benutzers auf Knoten B befinden. Je nach verwendetem Protokoll wird die Komponente, die die Anfrage bearbeitet, entweder:
Leiten Sie die Anfrage an den entsprechenden Backend-Knoten weiter oder greifen Sie über RPC-Aufrufe (exmdb-Datenverkehr) direkt auf die Daten zu.
Einrichtung eines Multi-Server-Systems
Abschnitt betitelt „Einrichtung eines Multi-Server-Systems“Um einen reibungslosen Betrieb über mehrere Knoten hinweg zu gewährleisten, müssen die folgenden Konfigurationen vorhanden sein:
- Definition von Servern in grommunio Admin:
- Der interne Hostname sollte den tatsächlichen Hostnamen des Servers widerspiegeln.
- Der externe Hostname sollte mit dem Namen übereinstimmen, der in Client-Anfragen übermittelt wird (z. B. für AutoDiscover).
- Benutzerzuordnung:
- Benutzer müssen bestimmten Servern zugeordnet werden. Sind mehrere Server konfiguriert, bestimmt die Auswahlrichtlinie des Systems die automatische Zuordnung.
- Konfiguration der Netzwerkdienste:
- Die Kerndienste müssen entsprechend der Architektur so konfiguriert werden, dass sie auf Netzwerkanfragen warten.
Gemeinsamer Speicher
Abschnitt betitelt „Gemeinsamer Speicher“Um gromox für den Betrieb mit mehreren Servern zu konfigurieren, führen Sie die folgenden Schritte für eine Konfiguration mit drei Knoten (mit aktiviertem IPv4-Datenverkehr) durch:
Konfigurationsdateien
# exmdb_hosts_allow replaces the former exmdb_acl.txt (Gromox 2.8+).# There is no exmdb host map any more — routing comes from the database (below).exmdb_hosts_allow=:: ::ffff:10.10.10.20 ::ffff:10.10.10.30 ::ffff:10.10.10.40 ::1listen_ip=::
# /etc/gromox/midb.cfgmidb_hosts_allow=:: ::ffff:10.10.10.20 ::ffff:10.10.10.30 ::ffff:10.10.10.40 ::1midb_listen_ip=::
# /etc/gromox/midb_list.txt/var/lib/gromox/user/mail1.gro.at ::ffff:10.10.10.20 5555/var/lib/gromox/user/mail2.gro.at ::ffff:10.10.10.30 5555/var/lib/gromox/user/mail3.gro.at ::ffff:10.10.10.40 5555/var/lib/gromox/domain/ ::ffff:10.10.10.20 5555
# /etc/gromox/event.cfgevent_hosts_allow=:: ::ffff:10.10.10.20 ::ffff:10.10.10.30 ::ffff:10.10.10.40 ::1event_listen_ip=::
# /etc/gromox/timer.cfgtimer_hosts_allow=:: ::ffff:10.10.10.20 ::ffff:10.10.10.30 ::ffff:10.10.10.40 ::1timer_listen_ip=::Steuerung „Central Admin API“
Da der Einsatz mehrerer Server auch für den Erstellungsprozess des Postfachspeichers eine gewisse Logik erfordert, wäre bei Verwendung der Topologie von Share-nothing-Clustern dennoch erforderlich, dass der gromox-mbop-Postfacherstellungsprozess auf den Speicher der Knoten zugreifen kann.
Im Allgemeinen wird in Umgebungen mit mehreren Servern empfohlen, die im obigen Abschnitt beschriebene Technik der Voranstellung des Hostnamens zu verwenden. Auf diese Weise wird das Postfach entsprechend seinem Verzeichnis an das von ihm definierte Postfachverzeichnis „gebunden“. Dies bedeutet lediglich, dass dieses Postfach (in erster Linie) dem Hostnamen zugeordnet ist; der tatsächliche Hostname für die „Verarbeitung“ wird jedoch durch die Beziehung „Benutzer<>Postfach“ definiert.
Um die Postfächer mit einem Pfadnamen einschließlich des Hostnamens anzulegen, ist diese Einstellung für grommunio-admin und API erforderlich, damit die Postfächer korrekt erstellt werden:
options: serverExplicitMount: trueIm Idealfall würde dieser Konfigurationsblock in den Konfigurationsbaum von grommunio Admin und API aufgenommen werden, beispielsweise als neue YAML-Datei unter /etc/grommunio-admin-api/conf.d/multiserver.yaml.
Speicherstruktur
Bei dieser Konfiguration werden die Benutzerverzeichnisse auf mehrere Knoten verteilt, wobei eine einheitliche logische Struktur beibehalten wird. Traditionell verwenden Cluster mit gemeinsam genutztem Speicher ein Cluster-Dateisystem, das eine effiziente Replikation gewährleistet, indem verschiedene Benutzerverzeichnisse als separate Inode-Einträge gespeichert werden. Dies reduziert die Belastung des Dateisystems und verbessert die Leistung.
Share-Nothing-Cluster
Abschnitt betitelt „Share-Nothing-Cluster“Mit grommunio 2025.01.1 benötigen Komponenten keinen direkten E/A-Zugriff mehr auf den Mailbox-Speicher. Stattdessen werden Anfragen wie folgt bearbeitet:
- Wenn eine Anfrage nicht zum lokalen Knoten gehört, geht das System wie folgt vor:
- Es ruft die Daten per RPC von dem Knoten ab, auf dem sich das Postfach befindet, oder
- es leitet die Anfrage im Proxy-Modus weiter und gibt die Antwort zurück.
Die wichtigsten Vorteile von Share-Nothing-Clustern
- Kein gemeinsamer Speicher erforderlich: Jeder Knoten arbeitet unabhängig, sodass kein gemeinsames Dateisystem benötigt wird.
- Verbesserte Hochverfügbarkeit: Die Knoten können über verschiedene Standorte verteilt werden, ohne dass eine Abhängigkeit von zentralem Speicher besteht.
- Kompatibilität mit modernen HA-Lösungen:
Diese Architektur unterstützt von Haus aus Cloud-native Umgebungen wie Kubernetes, andere containerisierte Umgebungen sowie verschiedene Replikationsverfahren für Failover-Szenarien.
Aus konfiguratorischer Sicht unterscheiden sich Share-Nothing-Cluster nicht von Setups mit gemeinsam genutztem Speicher, abgesehen davon, dass die Knoten keinen Zugriff auf die Postfächer der anderen Knoten benötigen. In Produktionsumgebungen können zusätzliche Replikationsverfahren zur Gewährleistung einer hohen Verfügbarkeit von Vorteil sein.
Failover
Abschnitt betitelt „Failover“Ein Cluster kann auch strenge Anforderungen an die Hochverfügbarkeit stellen (z. B. fünf Neuner, d. h. >99,999 %). Dieses Verfügbarkeitsniveau setzt voraus, dass bestimmte Cluster-Suite-Software in der Lage ist, bei diesen Failover-Wechseln der zweiten Ebene ein Failover durchzuführen. Für die Umsetzung solcher Szenarien, die durch das Enterprise-Abonnement abgedeckt sind, sieht der Arbeitsablauf in etwa wie folgt aus:
- Ausfall einer Anwendung, eines Containers oder einer VM erkennen
- Standby-Anwendung, -Container oder -VM aktivieren
- Anfragen auf Anwendungsebene auf das neue Standby-System umleiten
Für ein Failover des gesamten Cluster-Stacks von grommunio muss im Kern eine einfache MySQL-Abfrage ausgeführt sowie ein Neuladungssignal (SIGHUP) an die Hauptdienste (gromox-*) gesendet werden. Zum Beispiel:
UPDATE users SET homeserver=8 WHERE homeserver=4;und ein anschließendes Neuladesignal, um etwaige Anwendungs-Caches neu zu laden.
All dies lässt sich mithilfe bewährter Cluster-Dienste wie Pacemaker oder ähnlicher Produkte gut steuern.
Die „Rückkehr“ zum Normalbetrieb kann beispielsweise dadurch erfolgen, dass alle Benutzer neu auf alle verfügbaren Knoten in Ihrer Installation verteilt werden:
-- prepare environmentSET @rownum = 0;SELECT COUNT(*) INTO @server_count FROM servers;
-- reassign homeservers evenlyUPDATE usersJOIN ( SELECT maildir, (@rownum := @rownum + 1) AS rownum FROM users WHERE homeserver != 0 ORDER BY maildir) AS u_orderedON users.maildir = u_ordered.maildirSET users.homeserver = (u_ordered.rownum - 1) % @server_count + 1WHERE users.homeserver != 0;und anschließend erfolgt erneut ein Neuladen der Kerndienste (gromox-*).
Protokoll / Komponentenablauf
Abschnitt betitelt „Protokoll / Komponentenablauf“grommunio ist eine umfassende Kommunikations- und Kollaborationslösung, die Protokolle für eine Vielzahl von Computerstandards im Kommunikationsbereich abdeckt und bereitstellt. Die wichtigsten von grommunio bereitgestellten Protokolle sind:
Protokolle auf Leitungsebene:
- SMTP
- IMAP
- POP3
Protokolle auf Anwendungsebene (auf Basis von HTTP):
- RPC/HTTP (OutlookAnywhere)
- MAPI/HTTP
- EWS (Exchange Web Services)
- EAS (Exchange ActiveSync)
- CalDAV
- CardDAV
Angesichts dieser Vielzahl verfügbarer Protokolle muss grommunio über einen effizienten Komponentenfluss verfügen. Da für denselben Datensatz parallel auf verschiedene Protokolle zugegriffen werden kann, übernimmt grommunio die Parallelisierung und die Protokollverfolgung. Um Betrieb, Sicherheit und Funktionalität zu gewährleisten, nutzt der grommunio eine Reihe verschiedener Komponenten sowie eine pluginbasierte Struktur für größere Komponenten. Auf diese Weise lassen sich die Komponenten im Hinblick auf zukünftige Funktionserweiterungen ausbauen und ermöglichen Patches und Updates nahezu in Echtzeit. Komplexere Konfigurationen profitieren von der Komponenten-/Plugin-Architektur, da die Skalierbarkeit der Komponenten verschiedene Formen der Containerisierung und Orchestrierung ermöglicht.
Die folgende Abbildung zeigt den kombinierten Protokoll- und Komponentenablauf für Komponenten auf Basis der grommunio-Groupware:
Proxy-Funktionen
Abschnitt betitelt „Proxy-Funktionen“Standardmäßig werden die auf HTTP basierenden Dienste des grommunio über den nginx bereitgestellt. Dieser empfohlene Betriebsmodus bietet einen zusätzlichen Schutz für die Komponenten des gromox, da der nginx eingehende HTTP-Anfragen validiert, bevor diese vom gromox verarbeitet werden. Die interne Proxy-Konfiguration des nginx ist nicht für die horizontale Skalierung von Anfragen ausgelegt (und auch nicht erforderlich); stattdessen unterstützt der grommunio vor ihm platzierte Lastverteiler. Diese Lastverteiler fungieren effektiv als Reverse-Proxys mit integrierter Lastverteilungslogik. In solchen Fällen ist es ratsam, einen separaten Proxy vor allen von grommunio bereitgestellten Diensten einzusetzen.
Wenn gromox Anfragen für einen anderen Knoten verarbeiten muss als den, auf dem es läuft, kommt der interne Logikcode von exmdb zum Einsatz und leitet den Datenverkehr an den entsprechenden Knoten weiter.
Grommunio unterstützt verschiedene Load Balancer, die Zehntausende von Verbindungen pro Knoten verarbeiten können. Da jede Installation möglicherweise individuelle Konfigurationsanforderungen hat, sollen die folgenden Abschnitte eine Grundlage bieten und Anregungen für benutzerdefinierte Konfigurationen geben. Bitte beachten Sie, dass es verschiedene zusätzliche Optionen gibt, die hier nicht direkt behandelt werden und auch von anderen Load Balancern wie NGINX Plus, KEMP und/oder anderen angeboten werden.
HAPROXY
Abschnitt betitelt „HAPROXY“global chroot /var/lib/haproxy daemon log /dev/log local0
group haproxy user haproxy
maxconn 80000 stats timeout 30s ulimit-n 165000
ca-base /etc/ssl/certs crt-base /etc/ssl/private ssl-default-bind-ciphers AES128-GCM-SHA256:AES128-SHA:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA:AES256-SHA256:DES-CBC3-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets tune.ssl.default-dh-param 2048
defaults log global mode http option httplog option dontlognull
retries 3 timeout connect 5s timeout queue 30s timeout client 300s timeout server 300s
frontend fe_http
bind :80 http-response set-header Strict-Transport-Security max-age=31536000 http-response set-header X-Content-Type-Options nosniff http-response set-header X-Forwarded-Proto https http-response set-header X-Frame-Options SAMEORIGIN
acl whitelist-ip src -f /etc/haproxy/ha_whitelist_main.txt http-request silent-drop if HTTP_1.0 acl blacklist-ip src -f /etc/haproxy/ha_blacklist_main.txt http-request deny if blacklist-ip
mode http maxconn 80000
bind *:443 ssl crt /etc/haproxy/proxy.pem alpn h2,http/1.1 no option httpclose option forwardfor redirect scheme https code 301 if !{ ssl_fc }
# bind quic4@:443 ssl crt /etc/haproxy/proxy.pem alpn h3 # http-after-response add-header alt-svc 'h3=":443"; ma=60'
acl fe_haproxy hdr(host) -i mail.grommunio.at acl admin dst_port 8443 acl auth path_beg /auth acl autodiscover path_beg -i /autodiscover acl chat path_beg /chat acl colibri path_beg /colibri-ws acl dav path_beg /dav acl default path_beg / acl eas path_beg /Microsoft-Server-ActiveSync acl ews path_beg /EWS acl files path_beg /files acl hdr_connection_upgrade hdr(Connection) -i upgrade acl hdr_upgrade_websocket hdr(Upgrade) -i websocket acl mapi path_beg /mapi acl meet path_beg /meet acl oab path_beg /OAB acl office path_beg /office acl rpc path_beg /rpc/rpcproxy.dll acl web path_beg /web
use_backend be_adminnodes if admin fe_haproxy use_backend be_authnodes if auth fe_haproxy use_backend be_chatnodes if chat fe_haproxy use_backend be_filesnodes if files fe_haproxy use_backend be_gromoxnodes if autodiscover use_backend be_gromoxnodes if ews fe_haproxy use_backend be_gromoxnodes if mapi fe_haproxy use_backend be_gromoxnodes if rpc fe_haproxy use_backend be_meetnodes if colibri fe_haproxy use_backend be_meetnodes if hdr_connection_upgrade hdr_upgrade_websocket meet fe_haproxy use_backend be_meetnodes if meet fe_haproxy use_backend be_officenodes if office fe_haproxy use_backend be_webnodes if dav fe_haproxy use_backend be_webnodes if default fe_haproxy use_backend be_webnodes if eas fe_haproxy use_backend be_webnodes if web fe_haproxy
frontend fe_imaps mode tcp option tcplog bind :993 name imaps acl blocklist-imap src -f /etc/haproxy/ha_blacklist_imap.txt tcp-request connection reject if blocklist-imap default_backend be_imaps
frontend fe_pop3s mode tcp option tcplog bind :995 name pop3s acl blocklist-pop3s src -f /etc/haproxy/ha_blacklist_pop3.txt tcp-request connection reject if blocklist-pop3s default_backend be_pop3s
frontend fe_smtp mode tcp option tcplog bind :25 name smtp acl blocklist-smtp src -f /etc/haproxy/ha_blacklist_smtp.txt tcp-request connection reject if blocklist-smtp default_backend be_smtp
frontend fe_submission mode tcp option tcplog bind :587 name submission acl blocklist-submission src -f /etc/haproxy/ha_blacklist_submission.txt tcp-request connection reject if blocklist-submission default_backend be_submission
frontend fe_admin mode http option httplog option forwardfor bind *:8443 ssl crt /etc/haproxy/proxy.pem alpn h2,http/1.1 acl whitelist-admin src -f /etc/haproxy/ha_whitelist_admin.txt http-request deny if !whitelist-admin default_backend be_adminnodes
backend be_gromoxnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option redispatch server gromox01 mail01.grommunio.at:443 check ssl verify none server gromox02 mail02.grommunio.at:443 check ssl verify none server gromox03 mail03.grommunio.at:443 check ssl verify none
backend be_chatnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server chat01 chat01.grommunio.at:443 check ssl verify none server chat02 chat02.grommunio.at:443 check ssl verify none
backend be_webnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server web01 web01.grommunio.at:443 check ssl verify none server web02 web02.grommunio.at:443 check ssl verify none
backend be_meetnodes stick-table type ip size 10240k expire 60m stick on src balance url_param room hash-type consistent option forwardfor option http-server-close option redispatch server meet01 meet01.grommunio.at:443 check ssl verify none server meet02 meet02.grommunio.at:443 check ssl verify none
backend be_filesnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server files01 files01.grommunio.at:443 check ssl verify none server files02 files02.grommunio.at:443 check ssl verify none
backend be_officenodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server office01 office01.grommunio.at:443 check ssl verify none
backend be_authnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server auth01 auth01.grommunio.at:443 check ssl verify none
backend be_adminnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server admin01 admin01.grommunio.at:8443 check ssl verify none
backend be_imaps stick-table type ip size 10240k expire 60m mode tcp balance source stick on src server imap01 classic01.grommunio.at:993 check server imap02 classic02.grommunio.at:993 check
backend be_pop3s stick-table type ip size 10240k expire 60m mode tcp balance source stick on src server pop01 classic01.grommunio.at:995 check server pop02 classic02.grommunio.at:995 check
backend be_smtp mode tcp balance source server smtp01 classic01.grommunio.at:25 send-proxy server smtp02 classic02.grommunio.at:25 send-proxy
backend be_submission mode tcp balance source server submission01 classic01.grommunio.at:587 send-proxy server submission02 classic02.grommunio.at:587 send-proxyPostfix (PROXY protocol)
Abschnitt betitelt „Postfix (PROXY protocol)“Die Backends be_smtp und be_submission leiten den Datenverkehr mit send-proxy an Postfix weiter,
sodass der Load Balancer einen PROXY-Protocol-Header voranstellt und das Backend die
tatsächliche Client-Adresse anstelle der Proxy-Adresse sieht. Postfix muss angewiesen werden, diesen Header
bei den über den Proxy bereitgestellten Diensten zu erwarten – andernfalls interpretiert es den Header als SMTP-Befehle
und lehnt die Sitzung ab. In master.cf:
# Port 25, fronted by postscreensmtp inet n - n - 1 postscreen -o postscreen_upstream_proxy_protocol=haproxysmtpd pass - - n - - smtpd
# Port 587 (submission)submission inet n - n - - smtpd -o smtpd_upstream_proxy_protocol=haproxy -o syslog_name=postfix/submissionWenn Port 25 von einem einfachen smtpd (ohne Postscreen) bedient wird, setze stattdessen
-o smtpd_upstream_proxy_protocol=haproxy auf diesen Dienst.
Bitte beachten Sie, dass diese Konfiguration andere relevante Einstellungen von nginx in einer groß angelegten Scale-out-Installation nicht abdeckt. Bitte konsultieren Sie das nginx-Handbuch hinsichtlich bestimmter Konfigurationsanweisungen zur Skalierbarkeit, beispielsweise (unter anderem) worker_processes.
Der optimale Wert hängt von vielen Faktoren ab, darunter die Anzahl der verfügbaren CPU-Kerne, das Auslastungsmuster und weitere Aspekte. Im Zweifelsfall ist die Einstellung der Anzahl der verfügbaren CPU-Kerne ein guter Ausgangspunkt.
upstream be_smtp { server classic01.example.com:25; server classic02.example.com:25;}
upstream be_submission { server classic01.example.com:587; server classic02.example.com:587;}
upstream be_imaps { server classic01.example.com:993; server classic02.example.com:993;}
upstream be_pop3s { server classic01.example.com:995; server classic02.example.com:995;}
upstream be_gromoxnodes { server mail01.grommunio.at:443; server mail02.grommunio.at:443; server mail03.grommunio.at:443;}
upstream be_adminnodes { server admin01.grommunio.at:8443;}
upstream be_archivenodes { server archive01.grommunio.at:443;}
upstream be_chatnodes { server chat01.grommunio.at:443; server chat02.grommunio.at:443;}
upstream be_webnodes { server web01.grommunio.at:443; server web02.grommunio.at:443;}
upstream be_filesnodes { server files01.grommunio.at:443; server files02.grommunio.at:443;}
upstream be_officenodes { server office01.grommunio.at:443;}
upstream be_meetnodes { server meet01.grommunio.at:443; server meet02.grommunio.at:443;}
upstream be_authnodes { server auth01.grommunio.at:443;}
stream { server { listen 25; proxy_pass be_smtp; } server { listen 587; proxy_pass be_submission; } server { listen 993; proxy_pass be_imaps; } server { listen 995; proxy_pass be_pop3s; }}
server { listen 80; listen [::]:80;
server_name _;
error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log;
return 301 https://$server_name$request_uri;}
server { listen 443 ssl http2; listen [::]:443 ssl http2; # listen 443 quic reuseport; # listen [::]:443 quic reuseport;
server_name _;
ssl_certificate /etc/nginx/proxy.pem; ssl_certificate_key /etc/nginx/proxy.key; include ssl_params;
error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log;
charset utf-8;
proxy_buffers 4 256k; proxy_buffer_size 128k; proxy_busy_buffers_size 256k; proxy_http_version 1.1; proxy_pass_header Authorization; proxy_pass_header Date; proxy_pass_header Server; proxy_pass_request_headers on; proxy_read_timeout 3h; proxy_read_timeout 60s;
more_set_input_headers 'Authorization: $http_authorization'; more_set_headers -s 401 'WWW-Authenticate: Basic realm="mail.grommunio.at"'; proxy_set_header Accept-Encoding ""; proxy_set_header Connection "Keep-Alive"; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 0;
location ~* /admin { proxy_pass https://be_adminnodes; } location ~* /auth { proxy_pass https://be_authnodes; } location ~* /antispam { proxy_pass https://be_adminnodes/antispam; } location ~* /archive { proxy_pass https://be_gromoxnodes/archive; } location ~* /autodiscover { proxy_pass https://be_gromoxnodes/Autodiscover; } location ~* /colibri-ws { proxy_pass https://be_meetnodes/meet; } location ~* /chat { proxy_pass https://be_chatnodes/chat; } location ~* /EWS { proxy_pass https://be_gromoxnodes/EWS; } location ~* /files { proxy_pass https://be_filesnodes/files; } location ~* /mapi { proxy_pass https://be_gromoxnodes/mapi; } location ~* /meet { proxy_pass https://be_meetnodes/meet; } location ~* /office { proxy_pass https://be_officenodes/office; } location ~* /Microsoft-Server-ActiveSync { proxy_pass https://be_webnodes/Microsoft-Server-ActiveSync; } location ~* /oab { proxy_pass https://be_gromoxnodes/OAB; } location ~* /Rpc { proxy_pass https://be_gromoxnodes/Rpc; } location ~* /web { proxy_pass https://be_webnodes/web; }
location / { proxy_pass https://be_gromoxnodes/; }}Knotenorientierte Lastverteilung
Abschnitt betitelt „Knotenorientierte Lastverteilung“Das obige Beispiel berücksichtigt keine stark verteilten Installationen. Mit dem folgenden Beispiel und einigen Automatisierungen kann haproxy so konfiguriert werden, dass es den geeigneten Backend-Knoten direkt auswählt, wodurch die Notwendigkeit entfällt, dass Backend-Knoten die Anfragen durch RPC-Weiterleitungen und/oder knotenübergreifenden Datenverkehr verteilen müssen. Damit dies funktioniert, muss eine spezielle mailbox.map-Datei erstellt werden. Dies kann auf verschiedene Weise erfolgen, unter anderem durch Anbindung an die grommunio-admin-API.
Auf Low-Level-Ebene – durch direkten Zugriff auf die grommunio-Datenbank – lässt sich dies beispielsweise mit folgender Abfrage bewerkstelligen:
mysql -N -e "SELECT u.username, s.hostnameFROM users uJOIN servers s ON s.id = u.homeserverUNION ALLSELECT a.aliasname, s.hostnameFROM aliases aJOIN users u ON u.username = a.mainnameJOIN servers s ON s.id = u.homeserver;" > /etc/haproxy/mailbox.mapBitte beachten Sie, dass hierfür die entsprechende Zuordnung von Postfächern zu Servern konfiguriert sein muss. In diesem Beispiel muss die HAProxy-Installation ordnungsgemäß aufgelöst werden können (wenn „extname“-Hostnamen in der HAProxy-Konfiguration nicht so eingestellt sind, dass sie öffentlich erreichbar sind).
Diese Konfiguration basiert auf zwei Annahmen:
- Alle Backend-Knoten bedienen auch Webanfragen (grommunio-(web|sync) usw.).
- Die Gromox-Knoten sind gemäß der Dokumentation für Mehrserver-Konfigurationen entsprechend konfiguriert.
global chroot /var/lib/haproxy daemon log /dev/log local0 group haproxy user haproxy maxconn 80000 stats timeout 30s ulimit-n 165000 ca-base /etc/ssl/certs crt-base /etc/ssl/private ssl-default-bind-ciphers AES128-GCM-SHA256:AES128-SHA:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA:AES256-SHA256:DES-CBC3-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets tune.ssl.default-dh-param 2048
defaults log global mode http option httplog option dontlognull retries 3 timeout connect 5s timeout queue 30s timeout client 300s timeout server 300s
frontend fe_http bind :80 http-response set-header Strict-Transport-Security max-age=31536000 http-response set-header X-Content-Type-Options nosniff http-response set-header X-Forwarded-Proto https http-response set-header X-Frame-Options SAMEORIGIN
acl whitelist-ip src -f /etc/haproxy/ha_whitelist_main.txt http-request silent-drop if HTTP_1.0 acl blacklist-ip src -f /etc/haproxy/ha_blacklist_main.txt http-request deny if blacklist-ip
mode http maxconn 80000
bind *:443 ssl crt /etc/haproxy/proxy.pem alpn h2,http/1.1 no option httpclose option forwardfor redirect scheme https code 301 if !{ ssl_fc }
# --- extract mailbox for direct routing --- acl web_login path_beg /web/?login http-request set-var(txn.mbox) req.body_param(username) if web_login http-request set-var(txn.mbox) urlp(MailboxId) if autodiscover or ews or mapi or rpc
acl fe_haproxy hdr(host) -i mail.grommunio.at acl admin dst_port 8443 acl auth path_beg /auth acl autodiscover path_beg -i /autodiscover acl chat path_beg /chat acl colibri path_beg /colibri-ws acl dav path_beg /dav acl default path_beg / acl eas path_beg /Microsoft-Server-ActiveSync acl ews path_beg /EWS acl files path_beg /files acl hdr_connection_upgrade hdr(Connection) -i upgrade acl hdr_upgrade_websocket hdr(Upgrade) -i websocket acl mapi path_beg /mapi acl meet path_beg /meet acl oab path_beg /OAB acl office path_beg /office acl rpc path_beg /rpc/rpcproxy.dll acl web path_beg /web
use_backend be_adminnodes if admin fe_haproxy use_backend be_authnodes if auth fe_haproxy use_backend be_chatnodes if chat fe_haproxy use_backend be_filesnodes if files fe_haproxy use_backend be_gromoxnodes if autodiscover use_backend be_gromoxnodes if ews fe_haproxy use_backend be_gromoxnodes if mapi fe_haproxy use_backend be_gromoxnodes if rpc fe_haproxy use_backend be_meetnodes if colibri fe_haproxy use_backend be_meetnodes if hdr_connection_upgrade hdr_upgrade_websocket meet fe_haproxy use_backend be_meetnodes if meet fe_haproxy use_backend be_officenodes if office fe_haproxy use_backend be_webnodes if dav fe_haproxy use_backend be_webnodes if default fe_haproxy use_backend be_webnodes if eas fe_haproxy use_backend be_webnodes if web fe_haproxy
frontend fe_imaps mode tcp option tcplog bind :993 name imaps acl blocklist-imap src -f /etc/haproxy/ha_blacklist_imap.txt tcp-request connection reject if blocklist-imap default_backend be_imaps
frontend fe_pop3s mode tcp option tcplog bind :995 name pop3s acl blocklist-pop3s src -f /etc/haproxy/ha_blacklist_pop3.txt tcp-request connection reject if blocklist-pop3s default_backend be_pop3s
frontend fe_smtp mode tcp option tcplog bind :25 name smtp acl blocklist-smtp src -f /etc/haproxy/ha_blacklist_smtp.txt tcp-request connection reject if blocklist-smtp default_backend be_smtp
frontend fe_submission mode tcp option tcplog bind :587 name submission acl blocklist-submission src -f /etc/haproxy/ha_blacklist_submission.txt tcp-request connection reject if blocklist-submission default_backend be_submission
frontend fe_admin mode http option httplog option forwardfor bind *:8443 ssl crt /etc/haproxy/proxy.pem alpn h2,http/1.1 acl whitelist-admin src -f /etc/haproxy/ha_whitelist_admin.txt http-request deny if !whitelist-admin default_backend be_adminnodes
backend be_gromoxnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option redispatch server gromox01 mail01.grommunio.at:443 check ssl verify none server gromox02 mail02.grommunio.at:443 check ssl verify none server gromox03 mail03.grommunio.at:443 check ssl verify none # pick correct gromox node when mailbox is known use-server %[var(txn.mbox),map(/etc/haproxy/mailbox.map)] if { var(txn.mbox) -m found }
backend be_chatnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server chat01 chat01.grommunio.at:443 check ssl verify none server chat02 chat02.grommunio.at:443 check ssl verify none
backend be_webnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server web01 web01.grommunio.at:443 check ssl verify none server web02 web02.grommunio.at:443 check ssl verify none # route login requests to the mailbox's home node (if web servers share names with gromox nodes) use-server %[var(txn.mbox),map(/etc/haproxy/mailbox.map)] if { var(txn.mbox) -m found }
backend be_meetnodes stick-table type ip size 10240k expire 60m stick on src balance url_param room hash-type consistent option forwardfor option http-server-close option redispatch server meet01 meet01.grommunio.at:443 check ssl verify none server meet02 meet02.grommunio.at:443 check ssl verify none
backend be_filesnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server files01 files01.grommunio.at:443 check ssl verify none server files02 files02.grommunio.at:443 check ssl verify none
backend be_officenodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server office01 office01.grommunio.at:443 check ssl verify none
backend be_authnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server auth01 auth01.grommunio.at:443 check ssl verify none
backend be_adminnodes stick-table type ip size 10240k expire 60m stick on src balance roundrobin option forwardfor option http-server-close option redispatch server admin01 admin01.grommunio.at:8443 check ssl verify none
backend be_imaps stick-table type ip size 10240k expire 60m mode tcp balance source stick on src server imap01 classic01.grommunio.at:993 check server imap02 classic02.grommunio.at:993 check
backend be_pop3s stick-table type ip size 10240k expire 60m mode tcp balance source stick on src server pop01 classic01.grommunio.at:995 check server pop02 classic02.grommunio.at:995 check
backend be_smtp mode tcp balance source server smtp01 classic01.grommunio.at:25 send-proxy server smtp02 classic02.grommunio.at:25 send-proxy
backend be_submission mode tcp balance source server submission01 classic01.grommunio.at:587 send-proxy server submission02 classic02.grommunio.at:587 send-proxySo funktioniert es:
- Das Frontend „fe_http“ erfasst die Mailbox aus Anfragen an AutoDiscover/EWS/MAPI oder aus dem POST-Request der grommunio-Web-Anmeldung.
- Das Backend „be_gromoxnodes“ verwendet „use-server“ mit „mailbox.map“, um den richtigen gromox-Knoten auszuwählen, sofern eine Zuordnung vorhanden ist; andernfalls greift es auf Round-Robin zurück.
- Das Backend „be_webnodes“ kann dieselbe Abfrage durchführen, wenn die Webserver dieselben Servernamen wie die gromox-Knoten verwenden (andernfalls wird weiterhin „Round-Robin“ verwendet).
- Dadurch bleiben gromox-Anfragen und grommunio-Web-Sitzungen auf dem Heimserver der Mailbox, wodurch unnötige Weiterleitungen oder Proxy-Hops vermieden werden.
Bei OIDC/Keycloak wird ein anderer Ansatz empfohlen – wir empfehlen hier, bei der Anmeldung ein Cookie zu setzen und dieses in HAProxy auszuwerten, um es mit der Usermap abzugleichen, z. B. durch Erweiterung des nginx des IdP, indem der entsprechende Header gelesen und ein Cookie gesetzt wird, das den Benutzernamen enthält:
proxy_set_header Authorization $http_authorization;
# After successful login, extract username from ID token (needs lua/openresty or auth_request)add_header Set-Cookie "kc_username=$jwt_claim_preferred_username; Path=/; HttpOnly";SMTP ist das Hauptprotokoll für den E-Mail-Transport. Zur Veranschaulichung wird zwischen internem und externem E-Mail-Verkehr unterschieden.
Der gesamte Transport ist so konfiguriert, dass bei der E-Mail-Verarbeitung keine Lücken entstehen. Auf diese Weise schützt grommunio auch vor internen Bedrohungen (beispielsweise Spam oder der Verbreitung von Viren).
Die hier beschriebene Konfiguration definiert den Standardkonfigurationssatz. In vielen Fällen sind jedoch auch komplexere Konfigurationen denkbar, beispielsweise bei einer erweiterten Integration von Sicherheitsmodulen vom Typ appliance. Die folgenden Workflows enthalten die Prozessdefinition, die einen Überblick darüber gibt, an welcher Stelle ein bevorzugter Hook implementiert werden könnte.
Eingehend
Abschnitt betitelt „Eingehend“E-Mails werden wie folgt bearbeitet (gilt für eingehende und ausgehende E-Mails):
- Der integrierte Postfix MTA empfängt Nachrichten und leitet sie über das Milter-Mailfilterprotokoll an grommunio-antispam weiter.
- grommunio-antispam überprüft die Nachricht auf Spam. Falls konfiguriert, leitet grommunio-antispam die Nachricht (optional) an einen Antiviren-Dienst weiter.
- Die Antwort der Antiviren-Prüfung wird von antispam zurückgelesen.
- Die Antwort von „antispam“ wird von Postfix zurückgelesen.
- Postfix wertet den Inhalt des Adresspaares „Envelope-From“ und „Envelope-To“ aus, um zu entscheiden, ob es sich um eingehende oder ausgehende E-Mail handelt.
- Eingehende E-Mails werden an den gromox-Zustellungsprozess weitergeleitet, der die E-Mail in ein MAPI-Objekt umwandelt und in das Postfach des Benutzers ablegt.
- Ausgehende E-Mails werden an einen konfigurierten Relay-Host oder an den nächsten MX-Zielserver zugestellt, der für die Zieladresse zuständig ist.
Arbeitsablauf für RPC/HTTP, MAPI/HTTP und EWS
Abschnitt betitelt „Arbeitsablauf für RPC/HTTP, MAPI/HTTP und EWS“Die wichtigsten Protokolle, die von grommunio für die MAPI-basierte Konnektivität verwendet werden – wie sie beispielsweise bei Microsoft und Outlook zum Einsatz kommen – sind:
- RPC/HTTP (OutlookAnywhere)
- MAPI/HTTP
- EWS (Exchange Web Services)
Alle diese Protokolle basieren auf HTTP, weshalb sie – vor allem aus Gründen der Sicherheit, Skalierbarkeit und Überwachung – über den mitgelieferten nginx-Webserver geleitet werden.
Verbindungen auf Basis von MAPI werden wie folgt verarbeitet:
- In der ersten Phase nutzt der Endpunkt die AutoDiscover (https://learn.microsoft.com/en-us/exchange/architecture/client-access/autodiscover))-Technologie (mit Authentifizierung), um herauszufinden, welche Service-Endpunkt-URL für ihn zuständig ist.
- Wenn der AutoDiscover-Endpunkt beim selben Dienst landet (andernfalls wird er zur URL des anderen Endpunkts umgeleitet), leitet nginx die Verbindung direkt an den gromox-http-Dienst weiter, der die Verbindung verarbeitet.
- Für den Zugriff auf das Postfach des Benutzers stellt das emsmdb-Plugin von gromox-http eine Verbindung zum exmdb-Plugin her, um die Postfachdaten zu übermitteln.
Exchange ActiveSync (EAS)
Abschnitt betitelt „Exchange ActiveSync (EAS)“Das für Mobilgeräte und Tablets hauptsächlich verwendete Protokoll ist Exchange ActiveSync (EAS). EAS ist ein zustandsbasiertes Synchronisationsprotokoll, das Zustandsdaten verwendet, um seinen aktuellen Synchronisationsstatus zu ermitteln. EAS wird oft synonym als „Push-Mail“ bezeichnet, da es permanent mit seinem Dienst verbunden ist und auf Aktualisierungen wartet. Daher wird EAS als Protokoll für mobile Geräte empfohlen, insbesondere in unzuverlässigen Netzwerken wie Mobilfunknetzen. Zwar ist es möglich, bestimmte Clients, darunter Microsoft Mail und Microsoft Outlook, zu verbinden, davon wird jedoch dringend abgeraten. Im Vergleich zu leistungsstärkeren Alternativen wie MAPI/HTTP ist das EAS-Protokoll bei der Übertragung großer Datenmengen oder bei großen bis sehr großen (10 GB+) Postfächern langsamer. Schließlich bietet das EAS-Protokoll nur einen Teil der Funktionen, die bei anderen Protokollen verfügbar sind.
Verbindungen auf Basis von EAS werden wie folgt verarbeitet:
- In der ersten Phase nutzt der Endpunkt die AutoDiscover (https://learn.microsoft.com/en-us/exchange/architecture/client-access/autodiscover))-Technologie (mit Authentifizierung), um herauszufinden, welche Service-Endpunkt-URL für ihn zuständig ist.
- Wenn der AutoDiscover-Endpunkt beim selben Dienst landet (andernfalls wird er zur URL des anderen Endpunkts umgeleitet), leitet nginx die Verbindung an grommunio-sync weiter, das seinem Gerät nativ den Endpunkt /Microsoft-Server-ActiveSync bereitstellt.
- Für den Zugriff auf die Mailbox des Benutzers stellt grommunio-sync eine Verbindung zu gromox-zcore her, das die Schnittstellen PHP-MAPI für den Zugriff bereitstellt
- gromox-http über das exmdb-Plugin zur Bereitstellung der Postfachdaten.
CalDAV / CardDAV (grommunio-dav)
Abschnitt betitelt „CalDAV / CardDAV (grommunio-dav)“grommunio-dav bietet standardbasierten Zugriff auf CalDAV (Kalender, Aufgaben) und
CardDAV (Kontakte) für Clients, die DAV unterstützen – wie beispielsweise Apple
Kalender/Kontakte auf iOS sowie macOS, Mozilla Thunderbird und Evolution. Clients
erreichen es über HTTPS bis nginx, das DAV-Anfragen an grommunio-dav weiterleitet;
grommunio-dav nutzt php-mapi/gromox-zcore, um das Postfach im
Informationsspeicher (exmdb) zu lesen und zu schreiben. Die Client-Einrichtung wird von AutoConfig unterstützt, und die
.well-known-Weiterleitungen werden von gromox-http bereitgestellt.