# 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

  1. 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
    • ...
  2. 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

  1. 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"
  1. Start the docker container
docker-compose up

# Configuration with SSL

  1. 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"
  1. Add certificate for incoming SSL

  2. Add certificate for outgoing SSL

    This is necessary when the TARGET url starts with https.

    • Create folder for outgoing certificate docker_certs/to_java_keystore in the folder with the docker-compose file
    • Add *.pem file(s) to folder to_java_keystore. This files will be imported to java keystore on startup. Needs the same public certificates to access the TARGET url in a browser.
  3. 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 folder docker_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:

# 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)

Request missing documentation