The Simplifier Server needs a typcial Reverse Proxy as standard setup.
The Reverse Proxy should provide the following services for a secure setup
- SSL Offloading
- Virus / Malware Scanning
- Web Application Firewall
Forwarded Ports:
- 443 (HTTPS)
Port 443
- HTTPS (valid certificate)
- modern, secure TLS configuration (incl. HTTP Strict Transport Security)
- HTTP2 if possible
- Header:
- “Upgrade”: Pass from client (for WebSockets)
- “X-Real-IP”: IP address of the client
- “X-Forwarded-Forwarded-For”: Remote address of the client, or X-Forwarded-For Header of the parent proxy server.
- “X-Forwarded-Proto”: Original protocol of the request (“http” or “https”)
- CORS Headers (see below)
- (Temporary) Redirect from “/” to either AdminUI (“/UserInterface/”) or App of choice (“/appDirect/$appName”)
- Proxy connection/read/send Timeout to high value, e. g. 10 min
- Maximum body size (post, put) to appropriate value, e. g. 20 MB (doesn’t have to be too big, because packets > 20 MB are transferred as single chunks)
- Proxy forwarding to Simplifier AppServer port 8080 (if on another server, must be reachable via firewall)
CORS headers
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to allow a user agent to access selected resources from a server located on a different origin (domain) than the currently used Web site. A user agent makes a cross-origin HTTP request when requesting a resource from another domain, protocol, or port than the one from which the current document originates.
The CORS mechanism supports secure cross-domain queries and data transfers between browsers and web servers. Modern browsers use CORS in an API container such as XMLHttpRequest or Fetch to minimize the risks of cross-origin HTTP requests.
For request methods’ POST’,’ GET’,’ PUT’,’ DELETE’,’PATCH’:
Header name | Header value |
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 |
Additionally for request method ‘OPTIONS’:
Header name | Header value |
Access-Control-Max-Age | 1728000 |
Response name | Response code |
Empty Content | 204, No proxy call required |
The following paths should be configured for routing it back to Simplifier
Location / Path | Description |
“^/genToken/$” | The Simplifier Authentification Service based on Tokens |
“^/assets/(.*)$” | The static assets like images, pdf files, etc for an Application |
“^/client/(.*)$” | The Client REST API to access business objects, connector or plugins |
“^/library-managed/(.*)$” | Third-Party Javascript Libraries that need for the HTML5 Applications |
“^/library-static/(.*)$” | Third-Party Javascript Libraries that need for the HTML5 Applications |
“^/appDirect/(.*)$” | Hosting Path for the created HTML5 Applications |
“^/UserInterface/(.*)$” | Admin Backend Interface Application (should only accessible in a secure environment, internal network) |
“^/authentication/(.*)$” | External Authentication Provider for e.g. oAuth |
“^/passwordExpired/(.*)$” | Password Reset Page for Admin Interface |
“^/develop/(.*)$” | Plugin Interface |
“^/system-library/(.*)$” | System Libraries like OpenUI5 |
“^/appwizard/(.*)$” | REST API for App Wizards |
“^/Hybrid/(.*)$” | Launchpad and Workflow Integration Routes |
“^/HybridUserinterface/(.*)$” | Launchpad and Workflow Integration Routes |
To fulfill all mandatory requirements, you can use the following example of a nginx reverse proxy configuration:
# Run as a less privileged user for security reasons. user nginx; worker_processes auto; events { worker_connections 1024; } pid /var/run/nginx.pid; http { # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the # scheme used to connect to this server map $http_x_forwarded_proto $proxy_x_forwarded_proto { default $http_x_forwarded_proto; '' $scheme; } # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any # Connection header that may have been passed to this server map $http_upgrade $proxy_connection { default upgrade; '' close; } #Redirect to https, using 307 instead of 301 to preserve post data server { server_name localhost; listen 80; return 307 https://$host$request_uri; } server { listen [::]:443 ssl; listen 443 ssl http2; server_name localhost; # Protect against the BEAST attack by not using SSLv3 at all. If you need to support older browsers (IE6) you may need to add # SSLv3 to the list of protocols below. ssl_protocols TLSv1.2; # Ciphers set to best allow protection from Beast, while providing forwarding secrecy, as defined by Mozilla - 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; # Optimize TLS/SSL by caching session parameters for 10 minutes. This cuts down on the number of expensive TLS/SSL handshakes. # The handshake is the most CPU-intensive operation, and by default it is re-negotiated on every new/parallel connection. # By enabling a cache (of type "shared between all Nginx workers"), we tell the client to re-use the already negotiated state. # Further optimization can be achieved by raising keepalive_timeout, but that shouldn't be done unless you serve primarily HTTPS. ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions ssl_session_timeout 24h; # Use a higher keepalive timeout to reduce the need for repeated handshakes keepalive_timeout 300; # up from 75 secs default # remember the certificate for a year and automatically connect to HTTPS 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: replace port if app listens on port other than 80 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 this Example the
proxy_pass http://simplifier:8080;