Der Simplifier Server benötigt einen typischen Reverse-Proxy als Standard-Setup.
Der Reverse-Proxy sollte die folgenden Dienste für eine sichere Einrichtung bereitstellen
- SSL Offloading
- Viren- / Malware-Scanning
- Web Application Firewall
Weitergeleitete Ports:
- 443 (HTTPS)
Port 443
- HTTPS (gültiges Zertifikat)
- moderne, sichere TLS-Konfiguration (inkl. HTTP Strict Transport Security)
- HTTP2, wenn möglich
- Header:
- „Upgrade“: Vom Client weiterleiten (für WebSockets)
- „X-Real-IP“: IP-Adresse des Clients
- „X-Forwarded-Forwarded-For“: Remote-Adresse des Clients oder X-Forwarded-For-Header des übergeordneten Proxy-Servers.
- „X-Forwarded-Proto“: Ursprüngliches Protokoll der Anfrage („http“ oder „https“)
- CORS-Header (siehe unten)
- (Temporäre) Weiterleitung von „/“ entweder zur AdminUI („/UserInterface/“) oder zur gewünschten App („/appDirect/$appName“)
- Proxy-Verbindungs-/Lese-/Sende-Timeout auf einen hohen Wert, z. B. 10 min
- Maximale Body-Größe (POST, PUT) auf einen angemessenen Wert setzen, z. B. 20 MB (muss nicht zu groß sein, da Pakete > 20 MB als einzelne Chunks übertragen werden)
- Proxy-Weiterleitung zum Simplifier AppServer-Port 8080 (wenn auf einem anderen Server, muss er über die Firewall erreichbar sein)
CORS-Header
Cross-Origin Resource Sharing (CORS) ist ein Mechanismus, der zusätzliche HTTP-Header verwendet, um einem User Agent den Zugriff auf ausgewählte Ressourcen von einem Server zu ermöglichen, der sich auf einer anderen Origin (Domain) befindet als die aktuell verwendete Website. Ein User Agent stellt eine Cross-Origin-HTTP-Anfrage, wenn er eine Ressource von einer anderen Domain, einem anderen Protokoll oder Port anfordert als dem, von dem das aktuelle Dokument stammt.
Der CORS-Mechanismus unterstützt sichere Cross-Domain-Abfragen und Datenübertragungen zwischen Browsern und Webservern. Moderne Browser verwenden CORS in einem API-Container wie XMLHttpRequest oder Fetch, um die Risiken von Cross-Origin-HTTP-Anfragen zu minimieren.
Für die Anfragemethoden „POST“, „GET“, „PUT“, „DELETE“, „PATCH“:
| Header-Name | Header-Wert |
| Access-Control-Allow-Origin | * |
| Access-Control-Allow-Credentials | true |
| Access-Control-Allow-Methods | GET, POST, PUT, DELETE, OPTIONS, PATCH |
| Access-Control-Allow-Headers | DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, SimplifierToken, SimplifierApp, SimplifierModule, SimplifierModuleInterface, SimplifierClientBusinessObject, SimplifierClientBusinessObjectFunction, sap-cancel-on-close, sap-contextid-accept, MaxDataServiceVersion, DataServiceVersion, Content-Length, SimplifierApiKey, OData-MaxVersion, OData-Version, MIME-Version, X-CSRF-Token |
| Access-Control-Expose-Headers | remainingTokenLifetime,OData-Version |
Zusätzlich für die Anfragemethode „OPTIONS“:
| Header-Name | Header-Wert |
| Access-Control-Max-Age | 1728000 |
| Antwortname | Antwortcode |
| Leerer Content | 204, Kein Proxy-Aufruf erforderlich |
Die folgenden Pfade sollten für das Routing zurück zum Simplifier konfiguriert werden
| Ort / Pfad | Beschreibung |
| „^/genToken/$“ | Der Simplifier Authentifizierungsdienst basierend auf Tokens |
| „^/assets/(.*)$“ | Die statischen Assets wie Bilder, PDF-Dateien usw. für eine Anwendung |
| „^/client/(.*)$“ | Die Client-REST-API für den Zugriff auf Business-Objekte, Konnektoren oder Plugins |
| „^/library-managed/(.*)$“ | Drittanbieter-Javascript-Bibliotheken, die für die HTML5-Anwendungen benötigt werden |
| „^/library-static/(.*)$“ | Drittanbieter-Javascript-Bibliotheken, die für die HTML5-Anwendungen benötigt werden |
| „^/appDirect/(.*)$“ | Hosting-Pfad für die erstellten HTML5-Anwendungen |
| „^/UserInterface/(.*)$“ | Admin-Backend-Oberflächenanwendung (sollte nur in einer sicheren Umgebung, im internen Netzwerk, zugänglich sein) |
| „^/authentication/(.*)$“ | Externer Authentifizierungsanbieter für z. B. oAuth |
| „^/passwordExpired/(.*)$“ | Passwort-Reset-Seite für die Admin-Oberfläche |
| „^/develop/(.*)$“ | Plugin-Schnittstelle |
| „^/system-library/(.*)$“ | Systembibliotheken wie OpenUI5 |
| „^/appwizard/(.*)$“ | REST API für App-Wizards |
| „^/Hybrid/(.*)$“ | Launchpad- und Workflow-Integrationsrouten |
| „^/HybridUserinterface/(.*)$“ | Launchpad- und Workflow-Integrationsrouten |
Um alle obligatorischen Anforderungen zu erfüllen, kannst du das folgende Beispiel einer Nginx Reverse Proxy-Konfiguration verwenden:
# Aus Sicherheitsgründen als Benutzer mit geringeren Rechten ausführen.
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
pid /var/run/nginx.pid;
http {
# Wenn wir X-Forwarded-Proto erhalten, leiten wir es weiter; andernfalls leiten wir das
# Schema weiter, das für die Verbindung zu diesem Server verwendet wurde
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
# Wenn wir Upgrade erhalten, setze Connection auf „upgrade“; andernfalls lösche jeglichen
# Connection-Header, der an diesen Server übergeben worden sein könnte
map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
# Weiterleitung zu HTTPS, unter Verwendung von 307 anstelle von 301, um POST-Daten zu erhalten
server {
server_name localhost;
listen 80;
return 307 https://$host$request_uri;
}
server {
listen [::]:443 ssl;
listen 443 ssl http2;
server_name localhost;
# Schutz vor dem BEAST-Angriff durch vollständigen Verzicht auf SSLv3. Wenn du ältere Browser (IE6) unterstützen musst, musst du möglicherweise
# SSLv3 zur Liste der unten stehenden Protokolle hinzufügen.
ssl_protocols TLSv1.2;
# Cipher-Suites so eingestellt, dass sie den besten Schutz vor Beast bieten und gleichzeitig Forward Secrecy gewährleisten, wie von Mozilla definiert - https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;
# TLS/SSL optimieren durch Caching von Session-Parametern für 10 Minuten. Dies reduziert die Anzahl der aufwendigen TLS/SSL-Handshakes.
# Der Handshake ist die CPU-intensivste Operation und wird standardmäßig bei jeder neuen/parallelen Verbindung neu verhandelt.
# Durch Aktivierung eines Caches (vom Typ „shared between all Nginx workers“) weisen wir den Client an, den bereits ausgehandelten Zustand wiederzuverwenden.
# Weitere Optimierung kann durch Erhöhung des keepalive_timeout erreicht werden, dies sollte jedoch nur geschehen, wenn du hauptsächlich HTTPS bereitstellst.
ssl_session_cache shared:SSL:10m; # ein 1MB-Cache kann etwa 4000 Sessions speichern, sodass wir 40000 Sessions speichern können
ssl_session_timeout 24h;
# Verwende ein höheres Keepalive-Timeout, um die Notwendigkeit wiederholter Handshakes zu reduzieren
keepalive_timeout 300; # erhöht von standardmäßigen 75 Sek.
# Zertifikat für ein Jahr speichern und automatisch zu HTTPS verbinden
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
ssl_certificate /etc/nginx/ssl.crt;
ssl_certificate_key /etc/nginx/ssl.key;
location / {
proxy_pass http://simplifier:8080; # TODO: Port ersetzen, wenn die App auf einem anderen Port als 80 lauscht
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
client_max_body_size 20m;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SimplifierToken,SimplifierApp,remainingTokenLifetime';
add_header 'Access-Control-Expose-Headers' 'remainingTokenLifetime,OData-Version';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SimplifierToken,SimplifierApp,remainingTokenLifetime';
add_header 'Access-Control-Expose-Headers' 'remainingTokenLifetime,OData-Version';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SimplifierToken,SimplifierApp,remainingTokenLifetime';
add_header 'Access-Control-Expose-Headers' 'remainingTokenLifetime,OData-Version';
}
if ($request_method = 'PUT') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SimplifierToken,SimplifierApp,remainingTokenLifetime';
add_header 'Access-Control-Expose-Headers' 'remainingTokenLifetime,OData-Version';
}
if ($request_method = 'DELETE') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SimplifierToken,SimplifierApp,remainingTokenLifetime';
add_header 'Access-Control-Expose-Headers' 'remainingTokenLifetime,OData-Version';
}
if ($request_method = 'PATCH') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,SimplifierToken,SimplifierApp,remainingTokenLifetime';
add_header 'Access-Control-Expose-Headers' 'remainingTokenLifetime,OData-Version';
}
location = / {
return 302 https://$host/UserInterface/;
}
}
}
}
In diesem Beispiel bezieht sich das
proxy_pass http://simplifier:8080;











