Identity Gateway 2023.11

Install

This guide shows you how to install and remove IG software. For information about how to install IG for evaluation, refer to the Quick install.

Read the Release notes before you install.

Prepare to install

Before you install, make sure your installation meets the requirements in the release notes.

Create an IG service account

To limit the impact of a security breach, install and run IG from a dedicated service account. This is optional when evaluating IG, but essential in production installations.

A hacker is constrained by the rights granted to the user account where IG runs; therefore, never run IG as root user.

  1. In a terminal window, use a command similar to the following to create a service account:

    $ sudo /usr/sbin/useradd \
    --create-home \
    --comment "Account for running IG" \
    --shell /bin/bash IG
  2. Apply the principle of least privilege to the account, for example:

    • Read/write permissions on the installation directory, /path/to/identity-gateway.

    • Execute permissions on the scripts in the installation bin directory, /path/to/identity-gateway-2023.11.0/bin.

Prepare the network

Configure the network to include hosts for IG, AM, and the sample application. For more information about host files, refer to the Wikipedia entry, Hosts (file).

  1. Add the following entry to your host file:

    /etc/hosts
    127.0.0.1  localhost ig.example.com app.example.com am.example.com
    bash

Set up Identity Cloud

This documentation contains procedures for setting up items in ForgeRock Identity Cloud that you can use with IG. For more information about setting up Identity Cloud, refer to the ForgeRock Identity Cloud docs.

Authenticate an IG agent to Identity Cloud

IG agents are automatically authenticated to Identity Cloud by a non-configurable authentication module. Authentication chains and modules are deprecated in Identity Cloud and replaced by journeys.

You can now authenticate IG agents to Identity Cloud with a journey. The procedure is currently optional, but will be required when authentication chains and modules are removed in a future release of Identity Cloud.

For more information, refer to Identity Cloud’s Journeys.

This section describes how to create a journey to authenticate an IG agent to Identity Cloud. The journey has the following requirements:

  • It must be called Agent

  • Its nodes must pass the agent credentials to the Agent Data Store Decision node.

When you define a journey in Identity Cloud, that same journey is used for all instances of IG, Java agent, and Web agent. Consider this point if you change the journey configuration.

  1. Log in to the Identity Cloud admin UI as an administrator.

  2. Click Journeys > New Journey.

  3. Add a journey with the following information and click Create journey:

    • Name: Agent

    • Identity Object: The user or device to authenticate.

    • (Optional) Description: Authenticate an IG agent to Identity Cloud

    The journey designer is displayed, with the Start entry point connected to the Failure exit point, and a Success node.

  4. Using the Filter nodes bar, find and then drag the following nodes from the Components panel into the designer area:

    • Zero Page Login Collector node to check whether the agent credentials are provided in the incoming authentication request, and use their values in the following nodes.

      This node is required for compatibility with Java agent and Web agent.

    • Page node to collect the agent credentials if they are not provided in the incoming authentication request, and use their values in the following nodes.

    • Agent Data Store Decision node to verify the agent credentials match the registered IG agent profile.

    Many nodes can be configured in the panel on the right side of the page. Unless otherwise stated, do not configure the nodes, and use only the default values.
  5. Drag the following nodes from the Components panel into the Page node:

    • Platform Username node to prompt the user to enter their username.

    • /auth-node-ref/latest/auth-node-platform-password.html[Platform Password] node to prompt the user to enter their password.

  6. Connect the nodes as follows and save the journey:

    A journey that can be used to authenticate an agent to Identity Cloud.

Register an IG agent in Identity Cloud

This procedure registers an agent that acts on behalf of IG.

  1. Log in to the Identity Cloud admin UI as an administrator.

  2. Click verified_user Gateways & Agents > New Gateway/Agent > Identity Gateway > Next, and add an agent profile:

    • ID: agent-name

    • Password: agent-password

      Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
  3. Click Save Profile > Done. The agent profile page is displayed.

  4. To add a redirect URL for CDSSO, go to the agent profile page and add the URL.

  5. To change the introspection scope, click open_in_new Native Consoles > Access Management, and update the agent in the AM admin UI. By default, the agent can introspect OAuth 2.0 tokens issued to any client, in the realm and subrealm where it is created.

  6. To direct login to a custom URL instead of the default AM login page, click open_in_new Native Consoles > Access Management. Select the agent in the AM admin UI and configure Login URL Template for CDSSO.

Set up a demo user in Identity Cloud

This procedure sets up a demo user in the alpha realm.

  1. Log in to the Identity Cloud admin UI as an administrator.

  2. Go to group Identities > Manage > settings_system_daydream Alpha realm - Users, and add a user with the following values:

    • Username: demo

    • First name: demo

    • Last name: user

    • Email Address: demo@example.com

    • Password: Ch4ng3!t

Set up AM

This documentation contains procedures for setting up items in AM that you can use with IG. For more information about setting up AM, refer to the Access Management docs.

Authenticate an IG agent to AM

From AM 7.3

When AM 7.3 is installed with a default configuration, as described in Evaluation, IG is automatically authenticated to AM by an authentication tree. Otherwise, IG is authenticated to AM by an AM authentication module.

Authentication chains and modules were deprecated in AM 7. When they are removed in a future release of AM, it will be necessary to configure an appropriate authentication tree when you are not using the default configuration.

For more information, refer to AM’s Authentication Nodes and Trees.

This section describes how to create an authentication tree to authenticate an IG agent to AM. The tree has the following requirements:

  • It must be called Agent

  • Its nodes must pass the agent credentials to the Agent Data Store Decision node.

When you define a tree in AM, that same tree is used for all instances of IG, Java agent, and Web agent. Consider this point if you change the tree configuration.

  1. On the Realms page of the AM admin UI, choose the realm in which to create the authentication tree.

  2. On the Realm Overview page, click Authentication > Trees > Create tree.

  3. Create a tree named Agent.

    The authentication tree designer is displayed, with the Start entry point connected to the Failure exit point, and a Success node.

    The authentication tree designer provides the following features on the toolbar:

    Button Usage
    Trees auto layout

    Lay out and align nodes according to the order they are connected.

    Trees full screen

    Toggle the designer window between normal and full-screen layout.

    Trees delete node

    Remove the selected node. Note that the Start entry point cannot be deleted.

  4. Using the Filter bar, find and then drag the following nodes from the Components panel into the designer area:

    • Zero Page Login Collector node to check whether the agent credentials are provided in the incoming authentication request, and use their values in the following nodes.

      This node is required for compatibility with Java agent and Web agent.

    • Page node to collect the agent credentials if they are not provided in the incoming authentication request, and use their values in the following nodes.

    • Agent Data Store Decision node to verify the agent credentials match the registered IG agent profile.

    Many nodes can be configured in the panel on the right side of the page. Unless otherwise stated, do not configure the nodes and use only the default values.
  5. Drag the following nodes from the Components panel into the Page node:

    • Username Collector node to prompt the user to enter their username.

    • Password Collector node to prompt the user to enter their password.

  6. Connect the nodes as follows and save the tree:

    A tree that can be used to authenticate an agent to AM.

Register an IG agent in AM 7 and later

In AM 7 and later versions, follow these steps to register an agent that acts on behalf of IG.

  1. In the AM admin UI, select the top-level realm, and then select Applications > Agents > Identity Gateway.

  2. Add an agent with the following configuration, leaving other options blank or with the default value:

    • Agent ID : ig_agent

    • Password : password

Register an IG agent in AM 6.5 and earlier

In AM 6.5 and earlier versions, follow these steps to register an agent that acts on behalf of IG.

  1. In the AM admin UI, select the top-level realm, and then select Applications > Agents > Java (or J2EE ).

  2. Add an agent with the following configuration, leaving other options blank or with the default value:

    • Agent ID : ig_agent

    • Agent URL : http://ig.example.com:8080/agentapp

    • Server URL : http://am.example.com:8088/openam

    • Password : password

  3. On the Global tab, deselect Agent Configuration Change Notification.

    This option stops IG from being notified about agent configuration changes in AM. IG doesn’t need these notifications.

  4. (For CDSSO) On the SSO tab, select the following values:

    • Cross Domain SSO : Deselect this option

    • CDSSO Redirect URI : /home/cdsso/redirect

  5. (For CDSSO and policy enforcement) On the SSO tab, select the following values:

    • Cross Domain SSO : Deselect this option

    • CDSSO Redirect URI : /home/pep-cdsso/redirect

Set up a demo user in AM

AM is provided with a demo user in the top-level realm, with the following credentials:

  • ID/username: demo

  • Last name: user

  • Password: Ch4ng31t

  • Email address: demo@example.com

  • Employee number: 123

For information about how to manage identities in AM, refer to AM’s Identity stores.

In routes that use AmService, IG retrieves AM’s SSO cookie name from the ssoTokenHeader property or from AM’s /serverinfo/* endpoint.

In other circumstances where you need to find the SSO cookie name, access http://am-base-url/serverinfo/*. For example, access the AM endpoint with curl:

$ curl http://am.example.com:8088/openam/json/serverinfo/*
bash

Download IG

The .zip file unpacks into a /path/to/identity-gateway directory with the following content:

  • bin: Start and stop executables

  • classes: Initially empty; used to install patches from ForgeRock support

  • docker/Dockerfile: Dockerfile and README to build an IG Docker image

  • legal-notices: Licenses and copyrights

  • lib: IG and third-party libraries

  1. Create a local installation directory for IG. The examples in this section use /path/to.

    The installation directory should be a new, empty directory. Installing IG into an existing installation directory can cause errors.
  2. Download IG-2023.11.0.zip from the ForgeRock BackStage download site, and copy the .zip file to the installation directory:

    $ cp IG-2023.11.0.zip /path/to/IG-2023.11.0.zip
    bash
  3. Unzip the file:

    $ unzip IG-2023.11.0.zip
    bash

    The directory /path/to/identity-gateway is created.

Start and stop IG

Start IG with default settings

Use the following step to start the instance of IG, specifying the configuration directory where IG looks for configuration files.

  1. Start IG:

    $ /path/to/identity-gateway-2023.11.0/bin/start.sh
    
    ...
    ... started in 1234ms on ports : [8080 8443]
    bash

    By default, IG configuration files are located under $HOME/.openig (on Windows %appdata%\OpenIG). For information about how to use a different location, refer to Configuration location.

  2. Check that IG is running in one of the following ways:

    • Ping IG at http://ig.example.com:8080/openig/ping, and make sure an HTTP 200 is returned.

    • Access the IG welcome page at http://ig.example.com:8080.

    • When IG is running in development mode, display the product version and build information at http://ig.example.com:8080/openig/api/info.

Start IG with custom settings

By default, IG runs on HTTP, on port 8080, from the instance directory $HOME/.openig.

To start IG with custom settings, add the configuration file admin.json with the following properties, and restart IG:

  • vertx: Finely tune Vert.x instances.

  • connectors: Customize server port, TLS, and Vert.x-specific configurations. Each connectors object represents the configuration of an individual port.

  • prefix: Set the instance directory, and therefore, the base of the route for administration requests.

The following example starts IG on non-default ports, and configures Vert.x-specific options for the connection on port 9091:

{
  "connectors": [{
    "port": 9090
  },
  {
    "port": 9091,
    "vertx": {
      "maxWebSocketFrameSize": 128000,
      "maxWebSocketMessageSize": 256000,
      "compressionLevel": 4
    }
  }]
}
json

For more information, refer to AdminHttpApplication (admin.json).

Allow startup when there is an existing PID file

By default, if there is an existing PID file during startup the startup fails. Use one of the following ways to allow startup when there is an existing PID file. IG then removes the existing PID file and creates a new one during startup.

  1. Add the following configuration to admin.json and restart IG:

    {
      "pidFileMode": "override"
    }
    json
  2. Define an environment variable for the configuration token ig.pid.file.mode, and then start IG in the same terminal:

    $ IG_PID_FILE_MODE=override /path/to/identity-gateway/bin/start.sh
    bash
  3. Define a system property for the configuration token ig.pid.file.mode when you start IG:

    $HOME/.openig/env.sh
    export "IG_OPTS=-Dig.pid.file.mode=override"
    bash

Stop IG

Use the stop.sh script to stop an instance of IG, specifying the instance directory as an argument. If the instance directory is not specified, IG uses the default instance directory:

$ /path/to/identity-gateway-2023.11.0/bin/stop.sh $HOME/.openig
bash

Set up environment variables and system properties

Configure environment variables and system properties as follows:

  • By adding environment variables on the command line when you start IG.

  • By adding environment variables in $HOME/.openig/bin/env.sh, where $HOME/.openig is the instance directory. After changing env.sh, restart IG to load the new configuration.

Start IG with a customized router scan interval

By default, IG scans every 10 seconds for changes to the route configuration files. Any changes to the files are automatically loaded into the configuration without restarting IG. For more information about the router scan interval, refer to Router.

The following example overwrites the default value of the Router scan interval to two seconds when you start up IG:

$ IG_ROUTER_SCAN_INTERVAL='2 seconds' /path/to/identity-gateway-2023.11.0/bin/start.sh
bash

Define environment variables for startup, runtime, and stop

IG provides the following environment variables for Java runtime options:

IG_OPTS

(Optional) Java runtime options for IG and its startup process, such as JVM memory sizing options.

Include all options that are not shared with the stop script.

The following example specifies environment variables in the env.sh file to customize JVM options and keys:

# Specify JVM options
JVM_OPTS="-Xms256m -Xmx2048m"

# Specify the DH key size for stronger ephemeral DH keys, and to protect against weak keys
JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048"

# Wrap them up into the IG_OPTS environment variable
export IG_OPTS="${IG_OPTS} ${JVM_OPTS} ${JSSE_OPTS}"
bash
JAVA_OPTS

(Optional) Java runtime options for IG include all options that are shared by the start and stop script.

Encrypt and share JWT sessions

JwtSession objects store session information in JWT cookies on the user agent. The following sections describe how to set authenticated encryption for JwtSession, using symmetric keys.

Authenticated encryption encrypts data and then signs it with HMAC, in a single step. For more information, refer to Authenticated Encryption. For information about JwtSession, refer to JwtSession.

Encrypt JWT sessions

This section describes how to set up a keystore with a symmetric key for authenticated encryption of a JWT session.

  1. Set up a keystore to contain the encryption key, where the keystore and the key have the password password:

    1. Locate a directory for secrets, and go to it:

      $ cd /path/to/secrets
      bash
    2. Generate the key:

      $ keytool \
        -genseckey \
        -alias symmetric-key \
        -keystore jwtsessionkeystore.pkcs12 \
        -storepass password \
        -storetype pkcs12 \
        -keyalg HmacSHA512 \
        -keysize 512
      bash
      Because keytool converts all characters in its key aliases to lowercase, use only lowercase in alias definitions of a keystore.
  2. Add the following route to IG:

    $HOME/.openig/config/routes/jwt-session-encrypt.json
    {
      "name": "jwt-session-encrypt",
      "heap":  [{
        "name": "KeyStoreSecretStore-1",
        "type": "KeyStoreSecretStore",
        "config": {
          "file": "/path/to/secrets/jwtsessionkeystore.pkcs12",
          "storeType": "PKCS12",
          "storePasswordSecretId": "keystore.secret.id",
          "secretsProvider": ["SystemAndEnvSecretStore-1"],
          "mappings": [{
            "secretId": "jwtsession.symmetric.secret.id",
            "aliases": ["symmetric-key"]
          }]
        }
      },
        {
          "name": "SystemAndEnvSecretStore-1",
          "type": "SystemAndEnvSecretStore"
        }
      ],
      "session": {
        "type": "JwtSession",
        "config": {
          "authenticatedEncryptionSecretId": "jwtsession.symmetric.secret.id",
          "encryptionMethod": "A256CBC-HS512",
          "secretsProvider": ["KeyStoreSecretStore-1"],
          "cookie": {
            "name": "IG",
            "domain": ".example.com"
          }
        }
      },
      "handler": {
        "type": "StaticResponseHandler",
        "config": {
          "status": 200,
          "headers": {
            "Content-Type": [ "text/plain; charset=UTF-8" ]
          },
          "entity": "Hello world!"
        }
      },
      "condition": "${request.uri.path == '/jwt-session-encrypt'}"
    }
    json

    Notice the following features of the route:

    • The route matches requests to /jwt-session-encrypt.

    • The KeyStoreSecretStore uses the SystemAndEnvSecretStore in the heap to manage the store password.

    • The JwtSession uses the KeyStoreSecretStore in the heap to manage the session encryption secret.

  3. In the terminal where you will run the IG instance, create an environment variable for the value of the keystore password:

    $ export KEYSTORE_SECRET_ID='cGFzc3dvcmQ='
    bash

    The password is retrieved by the SystemAndEnvSecretStore, and must be base64-encoded.

Share JWT sessions between multiple instances of IG

When a session is shared between multiple instances of IG, the instances are able to share the session information for load balancing and failover.

This section gives an example of how to set up a deployment with three instances of IG that share a JwtSession.

Three instances of IG share a JwtSession.
  1. Set up a keystore to contain the encryption key, where the keystore and the key have the password password:

    1. Locate a directory for secrets, and go to it:

      $ cd /path/to/secrets
      bash
    2. Generate the key:

      $ keytool \
        -genseckey \
        -alias symmetric-key \
        -keystore jwtsessionkeystore.pkcs12 \
        -storepass password \
        -storetype pkcs12 \
        -keyalg HmacSHA512 \
        -keysize 512
      bash
      Because keytool converts all characters in its key aliases to lowercase, use only lowercase in alias definitions of a keystore.
  2. Prepare the IG installation:

    1. Create an installation directory for IG in /path/to.

    2. Download and unzip IG-2023.11.0.zip in /path/to, as described in the Install. The directory /path/to/identity-gateway is created.

  3. Set up the first instance of IG, which acts as the load balancer:

    1. Create a configuration directory for the instance and go to it:

      $ mkdir -p /path/to/config-instance1/config/routes
      bash
    2. Add the following route:

      /path/to/config-instance1/config/routes/instance1-loadbalancer.json
      {
        "name": "instance1-loadbalancer",
        "heap": [{
          "name": "KeyStoreSecretStore-1",
          "type": "KeyStoreSecretStore",
          "config": {
            "file": "/path/to/secrets/jwtsessionkeystore.pkcs12",
            "storeType": "PKCS12",
            "storePasswordSecretId": "keystore.secret.id",
            "secretsProvider": ["SystemAndEnvSecretStore-1"],
            "mappings": [{
              "secretId": "jwtsession.symmetric.secret.id",
              "aliases": ["symmetric-key"]
            }]
          }
        },
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          }
        ],
        "session": {
          "type": "JwtSession",
          "config": {
            "authenticatedEncryptionSecretId": "jwtsession.symmetric.secret.id",
            "encryptionMethod": "A256CBC-HS512",
            "secretsProvider": ["KeyStoreSecretStore-1"],
            "cookie": {
              "name": "IG",
              "domain": ".example.com"
            }
          }
        },
        "handler": {
          "type": "DispatchHandler",
          "config": {
            "bindings": [{
              "condition": "${find(request.uri.path, '/webapp/browsing') and (contains(request.uri.query, 'one') or empty(request.uri.query))}",
              "baseURI": "http://ig.example.com:8002",
              "handler": "ReverseProxyHandler"
            }, {
              "condition": "${find(request.uri.path, '/webapp/browsing') and contains(request.uri.query, 'two')}",
              "baseURI": "http://ig.example.com:8003",
              "handler": "ReverseProxyHandler"
            }, {
              "condition": "${find(request.uri.path, '/log-in-and-generate-session')}",
              "handler": {
                "type": "Chain",
                "config": {
                  "filters": [{
                    "type": "AssignmentFilter",
                    "config": {
                      "onRequest": [{
                        "target": "${session.authUsername}",
                        "value": "Sam Carter"
                      }]
                    }
                  }],
                  "handler": {
                    "type": "StaticResponseHandler",
                    "config": {
                      "status": 200,
                      "headers": {
                        "Content-Type": [ "text/html; charset=UTF-8" ]
                      },
                      "entity": "<html><body>Sam Carter logged IN. (JWT session generated)</body></html>"
                    }
                  }
                }
              }
            }]
          }
        },
        "capture": "all"
      }
      json

      Notice the following features of the route:

      • The route has no condition, so it matches all requests.

      • When the request matches /log-in-and-generate-session, the DispatchHandler creates a JWT session, whose authUsername attribute contains the name Sam Carter.

      • When the request matches /webapp/browsing, the DispatchHandler dispatches the request to instance 2 or instance 3, depending on the rest of the request path.

    3. Add the following configuration:

      /path/to/config-instance1/config/admin.json
      {
        "connectors": [{
        "port": 8001
        }]
      }
      bash
    4. In the terminal where you will run the IG instance, create an environment variable for the value of the keystore password:

      $ export KEYSTORE_SECRET_ID='cGFzc3dvcmQ='
      bash

      The password is retrieved by the SystemAndEnvSecretStore, and must be base64-encoded.

    5. Start IG:

      $ /path/to/identity-gateway-2023.11.0/bin/start.sh /path/to/config-instance1/
      
      ...
      ... started in 1234ms on ports : [8001]
      bash
  4. Set up and start the second instance of IG:

    1. Create a configuration directory for the instance:

      $ mkdir -p /path/to/config-instance2/config/routes
      bash
    2. Add the following route:

      /path/to/config-instance2/config/routes/instance2-retrieve-session-username.json
      {
        "name": "instance2-retrieve-session-username",
        "heap":  [{
          "name": "KeyStoreSecretStore-1",
          "type": "KeyStoreSecretStore",
          "config": {
            "file": "/path/to/secrets/jwtsessionkeystore.pkcs12",
            "storeType": "PKCS12",
            "storePasswordSecretId": "keystore.secret.id",
            "secretsProvider": ["SystemAndEnvSecretStore-1"],
            "mappings": [{
              "secretId": "jwtsession.symmetric.secret.id",
              "aliases": ["symmetric-key"]
            }]
          }
        },
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          }
        ],
        "session": {
          "type": "JwtSession",
          "config": {
            "authenticatedEncryptionSecretId": "jwtsession.symmetric.secret.id",
            "encryptionMethod": "A256CBC-HS512",
            "secretsProvider": ["KeyStoreSecretStore-1"],
            "cookie": {
              "name": "IG",
              "domain": ".example.com"
            }
          }
        },
        "handler": {
          "type": "StaticResponseHandler",
          "config": {
            "status": 200,
            "headers": {
              "Content-Type": [ "text/html; charset=UTF-8" ]
            },
            "entity": [
              "<html>",
              "  <body>",
              "    ${session.authUsername!= null?'Hello, '.concat(session.authUsername).concat(' !'):'Session.authUsername is not defined'}! (instance2)",
              "  </body>",
              "</html>"
            ]
          }
        },
        "condition": "${find(request.uri.path, '/webapp/browsing')}",
        "capture": "all"
      }
      json

      Notice the following features of the route compared to the route for instance 1:

      • The route matches the condition /webapp/browsing. When a request matches /webapp/browsing, the DispatchHandler dispatches it to instance 2.

      • The StaticResponseHandler displays information from the session context.

    3. Add the following configuration:

      /path/to/config-instance2/config/admin.json
      {
        "connectors": [{
        "port": 8002
        }]
      }
      bash
    4. In the terminal where you will run the IG instance, create an environment variable for the value of the keystore password:

      $ export KEYSTORE_SECRET_ID='cGFzc3dvcmQ='
      bash

      The password is retrieved by the SystemAndEnvSecretStore, and must be base64-encoded.

    5. Start IG:

      $ /path/to/identity-gateway-2023.11.0/bin/start.sh /path/to/config-instance2/
      
      ...
      ... started in 1234ms on ports : [8002]
      bash
  5. Set up and start the third instance of IG:

    1. Create a configuration directory:

      $ mkdir -p /path/to/config-instance3/config/routes
      bash
    2. Add the following route:

      /path/to/config-instance3/config/routes/instance3-retrieve-session-username.json
      {
        "name": "instance3-retrieve-session-username",
        "heap":  [{
          "name": "KeyStoreSecretStore-1",
          "type": "KeyStoreSecretStore",
          "config": {
            "file": "/path/to/secrets/jwtsessionkeystore.pkcs12",
            "storeType": "PKCS12",
            "storePasswordSecretId": "keystore.secret.id",
            "secretsProvider": ["SystemAndEnvSecretStore-1"],
            "mappings": [{
              "secretId": "jwtsession.symmetric.secret.id",
              "aliases": ["symmetric-key"]
            }]
          }
        },
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          }
        ],
        "session": {
          "type": "JwtSession",
          "config": {
            "authenticatedEncryptionSecretId": "jwtsession.symmetric.secret.id",
            "encryptionMethod": "A256CBC-HS512",
            "secretsProvider": ["KeyStoreSecretStore-1"],
            "cookie": {
              "name": "IG",
              "domain": ".example.com"
            }
          }
        },
        "handler": {
          "type": "StaticResponseHandler",
          "config": {
            "status": 200,
            "headers": {
              "Content-Type": [ "text/html; charset=UTF-8" ]
              },
              "entity": [
                "<html>",
                "  <body>",
                "    ${session.authUsername!= null?'Hello, '.concat(session.authUsername).concat(' !'):'Session.authUsername is not defined'}! (instance3)",
                "  </body>",
                "</html>"
              ]
            }
          },
        "condition": "${find(request.uri.path, '/webapp/browsing')}",
        "capture": "all"
      }
      json

      Notice that the route is the same as that for instance 2, apart from the text in the entity of the StaticResponseHandler.

    3. Add the following configuration:

      /path/to/config-instance3/config/admin.json
      {
        "connectors": [{
        "port": 8003
        }]
      }
      bash
    4. In the terminal where you will run the IG instance, create an environment variable for the value of the keystore password:

      $ export KEYSTORE_SECRET_ID='cGFzc3dvcmQ='
      bash

      The password is retrieved by the SystemAndEnvSecretStore, and must be base64-encoded.

    5. Start IG:

      $ /path/to/identity-gateway-2023.11.0/bin/start.sh /path/to/config-instance3/
      
      ...
      ... started in 1234ms on ports : [8003]
      bash
  6. Test the setup:

    1. Access instance 1, to generate a session:

      $ curl -v http://ig.example.com:8001/log-in-and-generate-session
      
      GET /log-in-and-generate-session HTTP/1.1
      ...
      
      HTTP/1.1 200 OK
      Content-Length: 84
      Set-Cookie: IG=eyJ...HyI; Path=/; Domain=.example.com; HttpOnly
      ...
      Sam Carter logged IN. (JWT session generated)
      bash
    2. Using the JWT cookie returned in the previous step, access instance 2:

      $ curl -v http://ig.example.com:8001/webapp/browsing\?one --header "cookie:IG=eyJ...HyI"
      
      GET /webapp/browsing?one HTTP/1.1
      ...
      cookie: IG=eyJ...HyI
      ...
      HTTP/1.1 200 OK
      ...
      Hello, Sam Carter !! (instance2)
      bash

      Note that instance 2 can access the session info.

    3. Using the JWT cookie again, access instance 3:

      $ curl -v http://ig.example.com:8001/webapp/browsing\?two --header "cookie:IG=eyJ...HyI"
      
      GET /webapp/browsing?two HTTP/1.1
      ...
      cookie: IG=eyJ...HyI
      ...
      HTTP/1.1 200 OK
      ...
      Hello, Sam Carter !! (instance3)
      bash

      Note that instance 3 can access the session info.

Prepare for load balancing and failover

For high scale or highly available deployments, consider using a pool of IG servers with nearly identical configurations. Load balance requests across the pool to handle more load. Route around any servers that become unavailable.

Manage state information

Before spreading requests across multiple servers, decide how to manage state information. IG manages state information in the following ways:

Stores state information in a context

By using filters that can store information in the context. Most filters depend on information in the request, response, or context, some of which is first stored by IG. For a summary of filters that can populate a context, refer to Summary of contexts.

By using a handler such as the ScriptableHandler that can store state information in the context. Most handlers depend on information in the context, some of which is first stored by IG.

Retrieves state information to local memory

By using filters and handlers that depend on the configuration of the local file system, such as the following filters:

When a server becomes unavailable, state information held in local memory is lost. To prevent data loss when a server becomes unavailable, set up failover. Server failover should be transparent to client applications.

Prepare stateless sessions

For example configurations, refer to Encrypt and share JWT sessions.

JwtSession

Manage stateless sessions though JwtSession. Session content is stored on a JWT cookie on the user agent.

So that any server can read or update a JWT cookie from any other server in the same cookie domain, encrypt JWT sessions and share keys and secret across all IG configurations.

Encrypt JWT sessions. The maximum size of the JWT session cookie is 4 KBytes, as defined by the browser. If the cookie exceeds this size, IG automatically splits it into multiple cookies.

Session stickiness

Session stickiness helps to ensure that a client request goes to the server holding the original session data.

If data attached to a context must be stored on the server-side, configure session stickiness so that the load balancer sends all requests from the same client session to the same server.

For an example configuration, refer to Share JWT sessions between multiple instances of IG.

SAML in deployments with multiple instances of IG

IG uses AM federation libraries to implement SAML. When IG acts as a SAML service provider, some internal state information is maintained in the fedlet instead of the session cookie. In deployments that use multiple instances of IG as a SAML service provider, set up sticky sessions so that requests go to the server that started the SAML interaction.

For information, refer to Session state considerations in AM’s SAML v2.0 guide.

Secure connections

IG is often deployed to replay credentials or other security information. In a real world deployment, this information must be communicated over a secure connection using HTTPS, meaning HTTP over encrypted Transport Layer Security (TLS). Never send real credentials, bearer tokens, or other security information unprotected over HTTP.

This guide describes how to install self-signed certificates for trying out the software or for deployments where you manage all clients that access IG. For information about how to use well-known CA-signed certificates, refer to the documentation for the Java Virtual Machine (JVM).

After installing certificates for client-server trust, consider which cipher suites to use. IG inherits the list of cipher suites from the underlying Java environment.

IG uses the JSSE to secure connections. You can set security and system properties to configure the JSSE. For a list of properties to customize the JSSE in Oracle Java, refer to the Customization section of the JSSE Reference guide.

Configure IG for HTTPS (client-side)

When IG sends requests over HTTP to a proxied application, or requests services from a third-party application, IG is acting as a client of the application, and the application is acting as a server. IG is client-side.

When IG sends requests securely over HTTPS, IG must be able to trust the server. By default, IG uses the Java environment truststore to trust server certificates. The Java environment truststore includes public key signing certificates from many well-known Certificate Authorities (CAs).

When servers present certificates signed by trusted CAs, then IG can send requests over HTTPS to those servers, without any configuration to set up the HTTPS client connection. When server certificates are self-signed or signed by a CA whose certificate is not automatically trusted, the following objects can be required to configure the connection:

  • KeyStoreSecretStore, to manage a secret store for cryptographic keys and certificates, based on a standard Java keystore.

  • SecretsTrustManager, to manage trust material that verifies the credentials presented by a peer.

  • (Optional) SecretsKeyManager, to manage keys that authenticate a TLS connection to a peer.

  • ClientHandler and ReverseProxyHandler reference to ClientTlsOptions, for connecting to TLS-protected endpoints.

The following procedure describes how to set up IG for HTTPS (client-side), when server certificates are self-signed or signed by untrusted CAs.

Set up IG for HTTPS (client-side) for untrusted servers
  1. Locate or set up the following directories:

    • Directory containing the sample application .jar: sampleapp_install_dir

    • Directory to store the sample application certificate and IG keystore: /path/to/secrets

  2. Extract the public certificate from the sample application:

    $ cd /path/to/secrets
    bash
    $ jar --verbose --extract \
    --file sampleapp_install_dir/IG-sample-application-2023.11.0.jar tls/sampleapp-cert.pem
    
    inflated: tls/sampleapp-cert.pem
    bash

    The file /path/to/secrets/tls/sampleapp-cert.pem is created.

  3. From the same directory, import the certificate into the IG keystore, and answer yes to trust the certificate:

    $ keytool -importcert \
    -alias ig-sampleapp \
    -file tls/sampleapp-cert.pem \
    -keystore reverseproxy-truststore.p12 \
    -storetype pkcs12 \
    -storepass password
    
    ...
    Trust this certificate? [no]: yes
    
    Certificate was added to keystore
    bash
    Because keytool converts all characters in its key aliases to lowercase, use only lowercase in alias definitions of a keystore.
  4. List the keys in the IG keystore to make sure that a key with the alias ig-sampleapp is present:

    $ keytool -list \
    -v \
    -keystore /path/to/secrets/reverseproxy-truststore.p12 \
    -storetype pkcs12 \
    -storepass password
    
    Keystore type: PKCS12
    Keystore provider: SUN
    Your keystore contains 1 entry
    Alias name: ig-sampleapp
    ...
    bash
  5. In the terminal where you run IG, create an environment variable for the value of the keystore password:

    $ export KEYSTORE_SECRET_ID='cGFzc3dvcmQ='
    bash

    The password is retrieved by the SystemAndEnvSecretStore, and must be base64-encoded.

  6. Add the following route to IG to serve the sample application .css and other static resources:

    $HOME/.openig/config/routes/00-static-resources.json
    {
      "name" : "00-static-resources",
      "baseURI" : "http://app.example.com:8081",
      "condition": "${find(request.uri.path,'^/css') or matchesWithRegex(request.uri.path, '^/.*\\\\.ico$') or matchesWithRegex(request.uri.path, '^/.*\\\\.gif$')}",
      "handler": "ReverseProxyHandler"
    }
    json
  7. Add the following route to IG:

    $HOME/.openig/config/routes/client-side-https.json
    {
      "name": "client-side-https",
      "condition": "${find(request.uri.path, '/home/client-side-https')}",
      "baseURI": "https://app.example.com:8444",
      "heap": [
        {
          "name": "Base64EncodedSecretStore-1",
          "type": "Base64EncodedSecretStore",
          "config": {
            "secrets": {
              "keystore.secret.id": "cGFzc3dvcmQ="
            }
          }
        },
        {
          "name": "KeyStoreSecretStore-1",
          "type": "KeyStoreSecretStore",
          "config": {
            "file": "/path/to/secrets/reverseproxy-truststore.p12",
            "storeType": "PKCS12",
            "storePasswordSecretId": "keystore.secret.id",
            "secretsProvider": "Base64EncodedSecretStore-1",
            "mappings": [
              {
                "secretId": "trust.manager.secret.id",
                "aliases": [ "ig-sampleapp" ]
              }
            ]
          }
        },
        {
          "name": "SecretsTrustManager-1",
          "type": "SecretsTrustManager",
          "config": {
            "verificationSecretId": "trust.manager.secret.id",
            "secretsProvider":"KeyStoreSecretStore-1"
          }
        },
        {
          "name": "ReverseProxyHandler-1",
          "type": "ReverseProxyHandler",
          "config": {
            "tls": {
              "type": "ClientTlsOptions",
              "config": {
                "trustManager": "SecretsTrustManager-1"
              }
            },
            "hostnameVerifier": "ALLOW_ALL"
          },
          "capture": "all"
        }
      ],
      "handler": "ReverseProxyHandler-1"
    }
    json

    Notice the following features of the route:

    • The route matches requests to /home/client-side-https.

    • The baseURI changes the request URI to point to the HTTPS port for the sample application.

    • The Base64EncodedSecretStore provides the keystore password.

    • The SecretsTrustManager uses a KeyStoreSecretStore to manage the trust material.

    • The KeyStoreSecretStore points to the sample application certificate. The password to access the keystore is provided by the SystemAndEnvSecretStore.

    • The ReverseProxyHandler uses the SecretsTrustManager for the connection to TLS-protected endpoints. All hostnames are allowed.

  8. Test the setup:

    1. Start the sample application

      $ java -jar sampleapp_install_dir/IG-sample-application-2023.11.0.jar
      bash
    2. Go to http://ig.example.com:8080/home/client-side-https.

      The request is proxied transparently to the sample application, on the TLS port 8444.

    3. Check the route log for a line like this:

      GET https://app.example.com:8444/home/client-side-https

Configure IG for HTTPS (server-side)

When IG is server-side, applications send requests to IG or request services from IG. IG is acting as a server of the application, and the application is acting as a client.

To run IG as a server over HTTPS, you must configure connections to TLS-protected endpoints, based on ServerTlsOptions.

For more information, refer to About keys and certificates.

Serve the same certificate for TLS connections to all server names

This example uses PEM files and a PKCS#12 keystore for self-signed certificates, but you can adapt it to use official (non self-signed) keys and certificates.

Before you start, install IG, as described in the Install.

  1. Locate a directory for the secrets, for example, /path/to/secrets.

  2. Create self-signed keys in one of the following ways. If you have your own keys, use them and skip this step.

    Use your own keys

    If you have your own keys, use them and skip this step.

    Set up a self-signed certificate in a (PKCS#12) keystore
    1. Create the keystore, replacing /path/to/secrets with your path:

      $ keytool \
      -genkey \
      -alias https-connector-key \
      -keyalg RSA \
      -keystore /path/to/secrets/IG-keystore \
      -storepass password \
      -keypass password \
      -dname "CN=ig.example.com,O=Example Corp,C=FR"
      bash
      Because keytool converts all characters in its key aliases to lowercase, use only lowercase in alias definitions of a keystore.
    2. In the secrets directory, add a file called keystore.pass, containing the keystore password password:

      $ cd /path/to/secrets/
      $ echo -n 'password' > keystore.pass
      bash

      Make sure the password file contains only the password, with no trailing spaces or carriage returns.

    Set up self-signed certificate stored in PEM file
    1. Locate a directory for secrets, and go to it:

      $ cd /path/to/secrets
      bash
    2. Create the following secret key and certificate pair as PEM files:

      $ openssl req \
      -newkey rsa:2048 \
      -new \
      -nodes \
      -x509 \
      -days 3650 \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout ig.example.com-key.pem \
      -out ig.example.com-certificate.pem
      bash

      Two PEM files are created, one for the secret key, and another for the associated certificate.

    3. Map the key and certificate to the same secret ID in IG:

      $ cat ig.example.com-key.pem ig.example.com-certificate.pem > key.manager.secret.id.pem
      bash
  3. Set up TLS on IG in one of the following ways:

    Keys stored in a (PKCS#12) keystore

    Add the following file to IG, replacing /path/to/secrets with your path:

    $HOME/.openig/config/admin.json
    {
      "connectors": [
        {
          "port": 8080
        },
        {
          "port": 8443,
          "tls": "ServerTlsOptions-1"
        }
      ],
      "heap": [
        {
          "name": "ServerTlsOptions-1",
          "type": "ServerTlsOptions",
          "config": {
            "keyManager": {
              "type": "SecretsKeyManager",
              "config": {
                "signingSecretId": "key.manager.secret.id",
                "secretsProvider": "ServerIdentityStore"
              }
            }
          }
        },
        {
          "type": "FileSystemSecretStore",
          "name": "SecretsPasswords",
          "config": {
            "directory": "/path/to/secrets",
            "format": "PLAIN"
          }
        },
        {
          "name": "ServerIdentityStore",
          "type": "KeyStoreSecretStore",
          "config": {
            "file": "/path/to/secrets/IG-keystore",
            "storePasswordSecretId": "keystore.pass",
            "secretsProvider": "SecretsPasswords",
            "mappings": [
              {
                "secretId": "key.manager.secret.id",
                "aliases": ["https-connector-key"]
              }
            ]
          }
        }
      ]
    }
    json

    Notice the following features of the file:

    • IG starts on port 8080, and on 8443 over TLS.

    • IG’s private keys for TLS are managed by the SecretsKeyManager, whose ServerIdentityStore references a KeyStoreSecretStore.

    • The KeyStoreSecretStore maps the keystore alias to the secret ID for retrieving the server keys (private key + certificate).

    • The password of the KeyStoreSecretStore is provided by the FileSystemSecretStore.

    Keys stored in PEM file

    Add the following file to IG, replacing /path/to/secrets with your path:

    $HOME/.openig/config/admin.json
    {
      "connectors": [
        {
          "port": 8080
        },
        {
          "port": 8443,
          "tls": "ServerTlsOptions-1"
        }
      ],
      "heap": [
        {
          "name": "ServerTlsOptions-1",
          "type": "ServerTlsOptions",
          "config": {
            "keyManager": {
              "type": "SecretsKeyManager",
              "config": {
                "signingSecretId": "key.manager.secret.id",
                "secretsProvider": "ServerIdentityStore"
              }
            }
          }
        },
        {
          "name": "ServerIdentityStore",
          "type": "FileSystemSecretStore",
          "config": {
            "format": "PLAIN",
            "directory": "/path/to/secrets",
            "suffix": ".pem",
            "mappings": [{
              "secretId": "key.manager.secret.id",
              "format": {
                "type": "PemPropertyFormat"
              }
            }]
          }
        }
      ]
    }
    json

    Notice how this file differs to that for the keystore-based approach:

    • The ServerIdentityStore is a FileSystemSecretStore.

    • The FileSystemSecretStore reads the keys that are stored as file in the PEM standard format.

  4. Start IG:

    $ /path/to/identity-gateway-2023.11.0/bin/start.sh
    
    ...
    ... started in 1234ms on ports : [8080 8443]
    bash

    By default, IG configuration files are located under $HOME/.openig (on Windows %appdata%\OpenIG). For information about how to use a different location, refer to Configuration location.

Serve different certificates for TLS connections to different server names

This example uses PEM files for self-signed certificates, but you can adapt it to use official (non self-signed) keys and certificates.

Before you start, install IG, as described in the Install.

  1. Locate a directory for secrets, for example, /path/to/secrets, and go to it.

    $ cd /path/to/secrets
    bash
  2. Create the following secret key and certificate pair as PEM files:

    1. For ig.example.com:

      1. Create a key and certificate:

        $ openssl req \
        -newkey rsa:2048 \
        -new \
        -nodes \
        -x509 \
        -days 3650 \
        -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
        -keyout ig.example.com-key.pem \
        -out ig.example.com-certificate.pem
        bash

        Two PEM files are created, one for the secret key, and another for the associated certificate.

      2. Map the key and certificate to the same secret ID in IG:

        $ cat ig.example.com-key.pem ig.example.com-certificate.pem > key.manager.secret.id.pem
        bash
    2. For servers grouped by a wildcard:

      1. Create a key and certificate:

        $ openssl req \
        -newkey rsa:2048 \
        -new \
        -nodes \
        -x509 \
        -days 3650 \
        -subj "/CN=*.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
        -keyout wildcard.example.com-key.pem \
        -out wildcard.example.com-certificate.pem
        bash
      2. Map the key and certificate to the same secret ID in IG:

        $ cat wildcard.example.com-key.pem wildcard.example.com-certificate.pem > wildcard.secret.id.pem
        bash
    3. For other, unmapped servers

      1. Create a key and certificate:

        $ openssl req \
        -newkey rsa:2048 \
        -new \
        -nodes \
        -x509 \
        -days 3650 \
        -subj "/CN=un.mapped.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
        -keyout default.example.com-key.pem \
        -out default.example.com-certificate.pem
        bash
      2. Map the key and certificate to the same secret ID in IG:

        $ cat default.example.com-key.pem default.example.com-certificate.pem > default.secret.id.pem
        bash
  3. Add the following file to IG, replacing /path/to/secrets with your path, and then restart IG:

    $HOME/.openig/config/admin.json
    {
      "connectors": [
        {
          "port": 8080
        },
        {
          "port": 8443,
          "tls": "ServerTlsOptions-1"
        }
      ],
      "heap": [
        {
          "name": "ServerTlsOptions-1",
          "type": "ServerTlsOptions",
          "config": {
            "sni": {
              "serverNames": {
                "ig.example.com": "key.manager.secret.id",
                "*.example.com": "wildcard.secret.id"
              },
              "defaultSecretId" : "default.secret.id",
              "secretsProvider": "ServerIdentityStore"
            }
          }
        },
        {
          "name": "ServerIdentityStore",
          "type": "FileSystemSecretStore",
          "config": {
            "format": "PLAIN",
            "directory": "path/to/secrets",
            "suffix": ".pem",
            "mappings": [
              {
                "secretId": "key.manager.secret.id",
                "format": {
                  "type": "PemPropertyFormat"
                }
              },
              {
                "secretId": "wildcard.secret.id",
                "format": {
                  "type": "PemPropertyFormat"
                }
              },
              {
                "secretId": "default.secret.id",
                "format": {
                  "type": "PemPropertyFormat"
                }
              }
            ]
          }
        }
      ]
    }
    json

    Notice the following features of the file:

    • The ServerTlsOptions object maps two servers to secret IDs, and includes a default secret ID

    • The secret IDs correspond to the secret IDs in the FileSystemSecretStore, and the PEM files generated in an earlier step.

  4. Run the following commands to request TLS connections to different servers, using different certificates:

    1. Connect to ig.example.com, and note that the certificate subject corresponds to the certificate created for ig.example.com:

      $ openssl s_client -connect localhost:8443 -servername ig.example.com
      
      ...
      Server certificate
      -----BEGIN CERTIFICATE-----
      MII...dZC
      -----END CERTIFICATE-----
      subject=/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr
      issuer=/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr
      bash
    2. Connect to other.example.com, and note that the certificate subject corresponds to the certificate created with the wildcard, *.example.com:

      $ openssl s_client -connect localhost:8443 -servername other.example.com
      
      ...
      Server certificate
      -----BEGIN CERTIFICATE-----
      MII...fY=
      -----END CERTIFICATE-----
      subject=/CN=*.example.com/OU=example/O=com/L=fr/ST=fr/C=fr
      issuer=/CN=*.example.com/OU=example/O=com/L=fr/ST=fr/C=fr
      bash
    3. Connect to unmapped.site.com, and note that the certificate subject corresponds to the certificate created for the default secret ID:

      $ openssl s_client -connect localhost:8443 -servername unmapped.site.com
      
      ...
      Server certificate
      -----BEGIN CERTIFICATE-----
      MII..rON
      -----END CERTIFICATE-----
      subject=/CN=un.mapped.com/OU=example/O=com/L=fr/ST=fr/C=fr
      issuer=/CN=un.mapped.com/OU=example/O=com/L=fr/ST=fr/C=fr
      bash