# Secure DMZ Adapter
# Introduction
The secure DMZ adapter serves as a proxy for a DMZ (demilitarized zone), which analyzes requests to the 4ALLPORTAL and blocks or transfers the data to the internal network. It is a Java application including an embedded Jetty server which must be installed in the DMZ.
In each response, the header Server: DMZ Adapter is set.
Possible restrictions are:
- URL
- blocking of calls with an invalid session
- blocking of logins after too many attempts
The following requests can be made:
- HTTP/HTTPS requests
- video streaming
- file upload
- file download
- AMF (Action Message Format) requests
The session of a request can be checked, so that requests with an invalid session will not be forwarded. If the access to a resource is rejected, the HTTP status code 403 (forbidden) is sent.
# Supported DAM Version
This documentation implies an installed DAM version 4.3 or higher. For earlier DAM versions, please use these earlier docs (opens new window).
# Clustering
The DMZ adapter does not cluster. Cookies are transferred though, so that clustering is possible via a downstream service (e.g. Apache).
# Multipart Requests
If you use multipart requests, only the session from a cookie can be checked. If no cookie is set, the analysis in the DMZ will not take place.
Reading out the session from a multipart request does not work, for the request was consumed and can thus no longer be forwarded.
# Requirements
- The DMZ server and the DAM must be approached via the same URL. A different URL would cause problems with various functions, especially when sending e-mails which link to the DAM:
- welcome e-mail
- change of password
- sending an eTicket
- ...
- On the DMZ server a host entry must be set to the DAM URL of the DAM server and access to the IP must be possible.
Example part docker-compose.yml
extra_hosts:
- "dmz4ap.4allportal.com:192.168.55.13"
# Start the DMZ Adapter via docker-compose
# Simple Configuration without SSL
- create a docker-compose file.
Example :
dmz:
container_name: dmz.4allportal.com
image: dmz_develop
restart: unless-stopped
user: "root:root"
environment:
BEARER_TOKEN: e35ed894-9961-4c5e-b04c-afe28abec702
HTTP_PORT: 80
SERVER_NAME: "dmz4ap.4allportal.com"
TARGET: "http://dmz4ap.4allportal.com/"
ports:
- "80:80"
volumes:
- "./docker_dmz/conf:/dmz/conf"
networks:
dmz:
ipv4_address: 192.168.55.12
extra_hosts:
# route the incoming calls to the 4ap or a proxy which handles ssl
- "dmz4ap.4allportal.com:192.168.55.13"
- Start the docker container
docker-compose up
# Configuration with SSL
- Create a docker-compose file.
Example :
dmz:
container_name: dmz.4allportal.com
image: dmz_develop
restart: unless-stopped
user: "root:root"
environment:
BEARER_TOKEN: e35ed894-9961-4c5e-b04c-afe28abec702
HTTP_PORT: 80
HTTPS_PORT: 443
SERVER_NAME: "dmz4ap.4allportal.com"
TARGET: "https://dmz4ap.4allportal.com/"
REDIRECT_HTTP: true
SSL_ENABLED: true
SSL_KEYSTORE_PASSWORD: 123456
SSL_KEY_MANAGER_PASSWORD: 123456
ports:
- "80:80"
- "443:443"
volumes:
- "./docker_certs:/dmz/certs"
- "./docker_dmz/conf:/dmz/conf"
networks:
dmz:
ipv4_address: 192.168.55.12
extra_hosts:
# route the incoming calls to the 4ap or a proxy which handles ssl
- "dmz4ap.4allportal.com:192.168.55.13"
Add certificate for incoming SSL
- Create folder for incoming certificate
docker_certs
in the folder with the docker-compose file and look here how to create certificates
- Create folder for incoming certificate
Add certificate for outgoing SSL
This is necessary when the
TARGET
url starts withhttps
.- Create folder for outgoing certificate
docker_certs/to_java_keystore
in the folder with the docker-compose file - Add
*.pem
file(s) to folderto_java_keystore
. This files will be imported to java keystore on startup. Needs the same public certificates to access theTARGET
url in a browser.
- Create folder for outgoing certificate
Start the docker container
docker-compose up
# Configuration Parameters
# Environment Variables
Environment variable | Default value |
---|---|
BEARER_TOKEN | - |
LOG_LEVEL | INFO |
HTTP_PORT | - |
LISTEN_HTTP_PORT | - |
HTTPS_PORT | - |
LISTEN_HTTPS_PORT | - |
REDIRECT_HTTP | false |
SERVER_NAME | - |
SERVER_ALIAS | - |
SOURCE | / |
TARGET | - |
CHECK_SESSION | true |
REMOVE_SESSION_AFTER_MINUTES | 60 |
MAX_CONNECTIONS | 32768 |
IDLE_TIMEOUT | 90000 |
TIMEOUT | 1200000 |
REQUEST_BUFFER_SIZE | 4096 |
RESPONSE_BUFFER_SIZE | 1200000 |
REMOVE_REQUEST_HEADER | - |
REMOVE_RESPONSE_HEADER | - |
RESTRICT_LOGIN_ENABLED | true |
RESTRICT_LOGIN_AFTER_LOGINS | 10 |
RESTRICT_LOGIN_TRIES | 3 |
RESTRICT_LOGIN_REMOVE_OLDER | 10 |
SSL_ENABLED | false |
SSL_KEYSTORE_PASSWORD | 123456 |
SSL_KEY_MANAGER_PASSWORD | 123456 |
SSL_EXCLUDE_CIPHER_SUITES | "^.*_(CBC)_.*$","TLS_DHE.*", "TLS_EDH.*" |
SSL_EXCLUDE_PROTOCOLS | - |
SSL_CONTEXT_FACTORY | false |
DEBUG | false |
PAUSE |
# Bearer Token
The dmz needs a valid bearer token to access the 4ALLPORTAL. Insert a valid api key from 4ALLPORTAL.
Example:
BEARER_TOKEN: 9a8aefdb-7297-4e47-997d-7b3a995127ff
# Log Level
Default: INFO
. Set LOG_LEVEL to ERROR, WARN, INFO or DEBUG.
The logger will output content to the system log.
Example:
LOG_LEVEL: INFO
# HTTP
To process HTTP requests, you only need to set the port. If http and a redirection to https are not supported, the value can be set to empty.
Example:
HTTP_PORT: 80
# Listen HTTP Port
To listen for http requests on a different port than the request port, set this.
If not set, uses the same as HTTP_PORT
.
Example:
LISTEN_HTTP_PORT: 8080
# HTTPS
SSL/HTTPS is supported both inbound and outbound.
# Inbound
To enable HTTPS, the port must be set:
Example:
HTTPS_PORT: 443
To listen for https requests on a different port than the request port, set this.
If not set, uses the same as HTTPS_PORT
.
Example:
LISTEN_HTTPS_PORT: 4443
And ssl must be activated.
SSL_ENABLED: true
The passwords for the keystore and the key manager have to be set.
Example:
SSL_KEYSTORE_PASSWORD: 123456
SSL_KEY_MANAGER_PASSWORD: 123456
Output information about EnabledProtocols
, SupportedProtocols
, EnabledCipherSuites
and SupportedCipherSuites
in the log.
SSL_CONTEXT_FACTORY: true
The output from the log can be used to exclude ssl protocols and cipher suites which are not safe. Multiple values have to be separated by ,
.
Not all browsers support the same protocols and cipher suites, so restrictions here may mean that access to 4ALLPORTAL may not work for older browser versions in particular.
Example:
# disable protocol
SSL_EXCLUDE_PROTOCOLS: "TLSv1"
# this disables all CBC based chipers which is the default for the DMZ adapter
SSL_EXCLUDE_CIPHER_SUITES: "^.*_(CBC)_.*$,TLS_DHE.*,TLS_EDH.*"
Note: If all CBC based ciphers are disabled, SSL will not work with Safari 9.
If all HTTP requests should be redirected to HTTPS, set the HTTP port and activate redirect:
Example:
HTTP_PORT: 80
REDIRECT_HTTP: true
The necessary certificates including the private key must be imported to the keystore. The certificates must be stored under an alias in the following order:
- server certificate
- intermediate certificate(s)
- private key
Example:
# convert server certificate to pem
openssl x509 -in 4allportal.net.crt -out 1.pem -outform PEM
# convert intermediate certificate to pem
openssl x509 -in DigiCertCA.crt -out 2.pem -outform PEM
# convert private key (copy rear part with Vim to text file 4ap.key)
openssl rsa -text -in 4allportal.net.key -inform DER
# create bundle.pem
cat 1.pem 2.pem > bundle.pem
cat 4ap.key >> bundle.pem
# export certificate for Java
openssl pkcs12 -export -in bundle.pem -inkey 4ap.key -out 4allportal.net.p12
# generate keystore
keytool -importkeystore -srckeystore 4allportal.net.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS
# check if certificates are in keystore
keytool -list -v -alias 1 -storepass 123456 -keystore keystore.jks
keytool -list -rfc -alias 1 -storepass 123456 -keystore keystore.jks
Mount a docker volume and add the file keystore.jks
to folder docker_certs
so that it's reachable in docker container with /dmz/certs/keystore.jks
:
Mount docker volumn
volumes:
- "./docker_certs:/dmz/certs"
# Outbound
If SSL should work outbound, the necessary certificates must be installed in the operating system. This is always necessary if the following URL starts with https:
- target
To insert the required certificates of the certificate chain into cacerts keychain of the used java version.
- create folder
docker_certs/to_java_keystore
- Mount folder
docker_certs
volumes: - "./docker_certs:/dmz/certs"
- add
.pem
files into the folderdocker_certs/to_java_keystore
. The files will be added to the keychain on startup of the DMZ adapter.
# Server Name
Define host for which the proxy configuration works. Only requests with this URL will be processed.
SERVER_NAME: "dmz4ap.4allportal.com"
# Server Alias
(optional)**: Define additional hosts which will be processed.
SERVER_ALIAS: example.4allportal.com
# Target
URL to which the request should be forwarded to (4ALLPORTAL URL).
Example:
TARGET: "https://dmz4ap.4allportal.com/"
# Source
Source url. Default /
. The url of the incoming request.
Example:
SOURCE: "/"
# Init Parameters for ProxyServlet
This parameter allow to configure the proxy servlet
Parameter | Default Value | |
---|---|---|
MAX_CONNECTIONS | 32768 | maximum connections per destination |
IDLE_TIMEOUT | 90000 | the period in ms a connection to proxied server can be idle for before it is closed |
TIMEOUT | 1200000 | the period in ms the client will wait for a response from the proxied server |
REQUEST_BUFFER_SIZE | 4096 | the size of the request buffer |
RESPONSE_BUFFER_SIZE | 1200000 | the size of the response buffer |
# Debug
When set to true
a debugger can access the DMZ adapter on port 8001
)
After that you have to bind the ports.
In case DEBUG
is disabled, there is no need to bind the debug port.
DEBUG: true
# PAUSE
How many seconds wait until start parsing the configuration. Allows to attach the debugger before parsing.
Example: Wait 3 seconds before parse the configuration
PAUSE: 3
# Functions of the DMZ Adapter
# Session Validation
Per default the session validation is active and can be switched off with:
CHECK_SESSION: false
The system tries to read the session ID from the session cookie. If no cookie is set, the session ID is read from the request. For endpoints with checkAuthorization
true, also sessions from bearer authorization will be checked (see here). The DMZ adapter checks whether data for this session is already available. If not, it tries to get the data for the session from the DAM database.
If there is a valid session, the request will be forwarded. Otherwise, the request will be rejected.
Each time a session is accessed, the access time is stored, regardless of whether the session is valid or not. All sessions that have expired and have not been accessed for a certain time will be removed from the container. The time can be configured like this:
REMOVE_SESSION_AFTER_MINUTES: 60
# Logout
When a logout method is executed, the session in the container is set to "expired". Logout methods are:
LoginRemoteService.logoutUser
(AMF)/api/auth/logout
In order to be able to block further calls of expired or invalid sessions, an expired session will not be removed directly.
# Limitation of Login Requests
The login requests can be restricted by the host. For this, the DMZ adapter must have access to the client IP. You can configure the limitation like this:
Here comes the default configuration
RESTRICT_LOGIN_ENABLED: true
RESTRICT_LOGIN_AFTER_LOGINS: 10
RESTRICT_LOGIN_TRIES: 3
RESTRICT_LOGIN_REMOVE_OLDER: 10
description | |
---|---|
RESTRICT_LOGIN_ENABLED | Set true to activate the limitation function. |
RESTRICT_LOGIN_AFTER_LOGINS | Defines after how many logins the limitation is activated. |
RESTRICT_LOGIN_TRIES | Defines how many logins per minute are allowed if the number set in RESTRICT_LOGIN_AFTER_LOGINS is exceeded. |
RESTRICT_LOGIN_REMOVE_OLDER | Removes logins which are older than X minutes. |
Note: If access to the client IP is not possible, RESTRICT_LOGIN_ENABLED must be disabled (set to false).
# Delete Request and Response Headers
Deleting the request header Accept-Encoding makes it possible to e.g. deactivate compression.
Multiple headers have to be split by a ,
without blanks inserted. You can configure it like this:
# remove the 'Accept-Encoding' header to deactivate compression
REMOVE_REQUEST_HEADER: "Accept-Encoding"
REMOVE_RESPONSE_HEADER:
# Customize Configuration
Most of the configuration is possible by environment variables. Only if you need special cache
or endpoint
configuration it's necessary to mount a docker volume and change the /docker_dmz/conf/dmz-adapter.xml
file.
Mount the folder docker_dmz/conf
volumes:
- "./docker_dmz/conf:/dmz/conf"
When the configuration folder is exposed by the container configuration the folder contains always two files
file | description |
---|---|
dmz-adapter-original.xml | The configuration from the container which always will be overwritten on container startup |
dmz-adapter.xml | The used configuration file which only will be copied on container startup when not exist in target folder |
After updating the DMZ adapter, you can simply compare the configuration used with the container configuration.
# Caching
To enable caching, you need to configure like this:
<caches enable="true" time_to_live="60">
<cache>/fonts/*</cache>
<cache>/4allportal-core/*</cache>
<cache>/4allportal-essentials/*</cache>
<cache>/4allportal-dam/*</cache>
</caches>
type | required? | description | |
---|---|---|---|
enable | XML attribute | no (default: true) | Allows you to disable the cache. |
time_to_live | XML attribute | no (default: 60) | Defines how many minutes the data will remain in the cache. |
cache | XML element | yes | Defines which calls are cached. .../* defines that all sublevels are cached, otherwise, only the exact page will be cached. |
# Configuration of Endpoints
All allowed requests must be configured in the configuration file in the section <endpoints>.
URLs which are not explicitly configured will be handled with a higher-level endpoint. This means that for example a call with the URL /service/amf/abc/cde would be answered by the endpoint with the configuration /service/amf.
<endpoint content_transformer="Download">
<url>/service/FileAccessDownload</url>
</endpoint>
You can configure the endpoints like this:
type | required? | description | |
---|---|---|---|
url | XML element | yes | The URL which is allowed or blocked. |
session | XML attribute | no | Attribute from which the session is to be read. For GET and POST requests. |
content_transformer | XML attribute | no | Valid values are: download and upload (see below). |
deny | XML attribute | no | Blocks explicitly this URL. |
destinations | XML element | no | Complex element to configure the handling of AMF requests (see below). |
checkAuthorization | XML attribute | no | Default: false. Set to true to activate session check for 4ALLPORTAL 3.7 endpoints. Session cookies and bearer authorization will be checked then. |
- download: Forwards the request immediately, but sets the header content-length. The file size of a download in the browser will only be displayed if more than one chunk is transferred. Otherwise, the header will be deleted automatically.
- upload: Saves the complete request before it is forwarded. Otherwise, incomplete assets and videos would be created in the DAM.
# Configuration of AMF Requests
Example:
<endpoint>
<url>/service/amf</url>
<destinations>
<destination name="ActionRemoteService" type="3"/>
<destination name="AdministrationRemoteService" type="1">
<operations type="0">
<operation>getBaseConfiguration</operation>
<operation>updateBaseConfiguration</operation>
</operations>
</destination>
</destinations>
</endpoint>
# Destinations
type | required? | description | |
---|---|---|---|
name | XML attribute | yes | Name of the AMF endpoint. |
type | XML attribute | no (default: 0) | Defines the way how the session is included in the request (see below). |
pos | XML attribute | no (default: 0) | Defines in which parameter of the array the session is located. |
operations | XML element | no | Allows to define exceptions for certain operations of an AMF endpoint. If not set, the endpoint setting is used (see below). |
# Destination Types
value | description |
---|---|
0 | no session |
1 | session as string |
2 | session in identVO |
3 | session in object (derived from SimpleRequestObject) |
# Operations
type | required? | description | |
---|---|---|---|
type | XML attribute | no (default: 0) | see: destinations |
pos | XML attribute | no (default: 0) | see: destinations |
access | XML attribute | no (default: true) | False will explicitly block a method. |
operation | XML elementList | yes | List of operations to which this setting applies. |
# Reloading Configuration
This url call allows to reload the configuration. This is only allowed for calls via localhost:
Url
localhost/reload
Reloading works for:
- endpoints
- cache clearing (If files are locked, a log entry will be created.)
Reloading works not for configurations like:
- all parameters which are configured in docker-compose file
- parameters of the connection like: ports, timeouts, server name
- remove_session_after_minutes
- restrict_login
# Checking the Configuration
This method allows to check the configuration. This is only allowed for calls via localhost:
Url
localhost/reload?only_check=true
# Help
# Show Server Ciphers
The output depends on the supported SSL protocols and ciphers. For Linux and Mac OSX this may result in different outputs. Use one of these methods to show server ciphers:
- https://www.ssllabs.com/ssltest/ (opens new window)
nmap -p 443 --script ssl-enum-ciphers HOSTNAME
sslscan HOSTNAME
# Read out Ciphers from HTTPd
#All ciphers
openssl ciphers ${cipherspec} | sed 's/:/\n/g'
#CBC ciphers
openssl ciphers ${cipherspec} | sed 's/:/\n/g' | grep CBC
# Check Certificate Chain
To display the certificate chain, execute:
openssl s_client -connect HOSTNAME:443
openssl s_client -verify_return_error -showcerts -connect HOSTNAME:443
The result must be: Verify return code: 0 (ok)