PingGateway 2024.11

Protect PingAM

In its role as a reverse proxy, PingGateway can protect PingAM.

Choose what you allow

The best practice is to allow access only to required AM services:

  • Limit access to specified AM realms.

  • Prevent AM admin UI access.

Learn more about this process in the following articles:

Example route

The following example allows access to the following:

  • Key services in the customers realm.

  • Authentication and logout through XUI and the legacy UI.

  • SAML v2.0 services.

Before you begin

  • Install and run PingGateway as described in the Quick install.

  • Install and configure AM with a customers subrealm of the top-level realm.

Allow-only route

Add the following route to PingGateway:

  • Linux

  • Windows

$HOME/.openig/config/routes/protect-am.json
%appdata%\OpenIG\config\routes\protect-am.json
{
  "name": "protect-am",
  "comment": "Allow authentication and subrealm access only (not the top-level realm or admin UI)",
  "properties": {
    "amBase": "/am",
    "amInstanceUrl": "http://am.example.com:8088/am",
    "subrealm": "customers"
  },
  "baseURI": "&{amInstanceUrl}",
  "condition": "${find(request.uri.path, '^&{amBase}')}",
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "AllowOnlyFilter",
          "config": {
            "rules": [
              {
                "comment": "Allow authentication to the subrealm",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/json/realms/root/realms/&{subrealm}/authenticate"
                    ],
                    "when": "${find(request.queryParams, 'realm') and !(contains(request.queryParams['realm'], '/') or contains(request.queryParams['realm'], '2F') or contains(request.queryParams['authIndexValue'], 'Application') or contains(request.queryParams['module'], 'Application'))}"
                  }
                ]
              },
              {
                "comment": "Allow authentication to the legacy UI",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/UI/Login"
                    ],
                    "when": "${find(request.queryParams, 'realm') and !(contains(request.queryParams['realm'], '/') or contains(request.queryParams['realm'], '2F') or contains(request.queryParams['authIndexValue'], 'Application') or contains(request.queryParams['module'], 'Application'))}"
                  }
                ]
              },
              {
                "comment": "Allow OAuth 2.0 and OID to the subrealm",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/oauth2/realms/root/realms/&{subrealm}/authorize",
                      "&{amBase}/oauth2/realms/root/realms/&{subrealm}/access_token",
                      "&{amBase}/oauth2/realms/root/realms/&{subrealm}/userinfo",
                      "&{amBase}/oauth2/realms/root/realms/&{subrealm}/connect/endSession"
                    ]
                  }
                ]
              },
              {
                "comment": "Allow base requests for sessions and users",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/json/sessions",
                      "&{amBase}/json/users"
                    ]
                  }
                ]
              },
              {
                "comment": "Allow base action requests for sessions",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/json/realms/root/sessions"
                    ]
                  }
                ],
                "when": "${(request.queryParams['_action'] == 'getMaxIdle') or (request.queryParams['_action'] == 'logout') or (request.queryParams['_action'] == 'validate')}"
              },
              {
                "comment": "Allow base action requests for users",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/json/realms/root/users"
                    ]
                  }
                ],
                "when": "${request.queryParams['_action'] == 'idFromSession'}"
              },
              {
                "comment": "Allow subrealm requests for sessions, serverinfo, and users",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/json/realms/root/realms/&{subrealm}/sessions",
                      "&{amBase}/json/realms/root/realms/&{subrealm}/serverinfo/*",
                      "^&{amBase}/json/realms/root/realms/&{subrealm}/users"
                    ]
                  }
                ]
              },
              {
                "comment": "Allow access to the XUI",
                "destination": [
                  {
                    "paths": [
                      "^&{amBase}/XUI"
                    ]
                  }
                ]
              },
              {
                "comment": "Allow access to the legacy UI for logout",
                "destination": [
                  {
                    "paths": [
                      "&{amBase}/UI/Logout"
                    ]
                  }
                ]
              },
              {
                "comment": "Allow SAML v2.0 requests",
                "destination": [
                  {
                    "paths": [
                      "^&{amBase}/ArtifactResolver/",
                      "^&{amBase}/Consumer/",
                      "^&{amBase}/IDPSloPOST/",
                      "^&{amBase}/IDPSloRedirect/",
                      "^&{amBase}/IDPSloSoap/",
                      "^&{amBase}/SSORedirect/",
                      "^&{amBase}/idpsaehandler/",
                      "^&{amBase}/saml2/jsp/"
                    ]
                  }
                ]
              }
            ],
            "failureHandler": {
              "type": "StaticResponseHandler",
              "config": {
                "status": 404,
                "headers": {
                  "Content-Type": [
                    "text/html; charset=UTF-8"
                  ]
                },
                "entity": "<html><p>HTTP 404 Not Found</p></html>"
              }
            }
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}

Notice the key features of the route:

  • An AllowOnlyFilter defines the rules for requests PingGateway allows; PingGateway denies all requests not explicitly allowed.

    Adapt the route for the deployment, for example, by setting the amBase, amInstanceUrl, and subrealm properties.
  • When access to a resource is denied, PingGateway returns HTTP 404 Not Found.

    In deployment, avoid leaking information by returning the same response for missing and denied resources.

When you save the updated route file, PingGateway reloads it.

Validation

  1. Try an allowed request.

    In your browser’s privacy or incognito mode, go to the XUI login page for the subrealm, such as https://ig.example.com:8443/am/XUI/?realm=/customers#login in this example.

    PingGateway displays the login page for the realm.

  2. Try a request that’s not allowed.

    In your browser’s privacy or incognito mode, go to the top-level realm server version information resource, such as https://ig.example.com:8443/am/json/serverinfo/version.

    PingGateway returns 404 and displays a message: HTTP 404 Not Found.

  3. Notice you can still access AM directly, as long as you don’t traverse PingGateway.

    For example, go to the base URL for AM, such as http://am.example.com:8088/am.

    AM displays the login page.

    In deployment, route client traffic to AM through PingGateway, as for other protected applications. Don’t expose AM endpoints directly.