Cybersecurity Lab
PW4 — OpenAM Identity Federation & SSO
Built a two-host lab (Ubuntu 24.04 + Kali) running OpenAM 15.2.2 on Tomcat 9 against an OpenLDAP backend: realm configuration, LDAP data store, policy agents, and verified single sign-on across two applications sharing one iPlanetDirectoryPro session token.
Objective
Deploy an access-management server, federate it with an external directory on a separate host, and demonstrate single sign-on and risk-adaptive authorization with evidence taken from outside the console that configured it.
Tools Used
Steps Performed
- Provisioned openam.corp.local on a static address, installed OpenJDK 11 and Apache Tomcat 9.0.107 with a split CATALINA_HOME/CATALINA_BASE layout.
- Deployed the OpenAM 15.2.2 WAR from the Open Identity Platform community fork to the /openam context.
- Ran the custom configuration wizard, setting the server URL, cookie domain and configuration directory explicitly.
- Added a second mdb database (dc=corp,dc=local) to the existing OpenLDAP instance on the Kali host.
- Created a Generic LDAPv3 data store, Corp-LDAP, in the /corp realm and confirmed three users resolved live.
- Deployed two JSP applications guarded by SSO token validation and demonstrated one session across both.
- Attached time-of-day and IPv4 source-address conditions to the policies and evaluated the decision change via REST.
Key Findings
- Single sign-on was proven positively rather than inferred: both applications printed an identical iPlanetDirectoryPro token, captured seventeen seconds apart, after one authentication.
- The profile page rendered First Name, Last Name and Email read from the remote inetOrgPerson entry, proving authentication and profile retrieval both flow through the external identity store.
- OpenAM disregarded a client-supplied address in the evaluation request and used the real TCP source address — correct behaviour, since a self-declared source would be trivially bypassable.
- A policy carrying a time condition returned a concrete epoch ttl while the unconditioned policy returned the maximum long, showing continuous evaluation rather than a one-off check at login.
Contents
Lessons Learned
- A configuration screen showing the intended values is not evidence that the intended behaviour occurs — twice the console displayed a plausible configuration while the stored object was wrong.
- Both faults were found only by observing the system from outside the interface that created it: directory-side query logging, and the REST representation of the stored policy.
- Binding as the end user directly against the directory before involving OpenAM isolates later failures to the access-management layer.
- Recurring benign log messages should be identified and documented so they are not mistaken for faults.
Future Improvements
- Terminate the console and login flows over TLS rather than plain HTTP.
- Replace the hand-written JSP guard with a supported policy agent.
- Add SAML 2.0 federation between two OpenAM instances to extend beyond same-domain SSO.
References
- Open Identity Platform — OpenAM 15.2.2 documentation
- Apache Tomcat 9 documentation
- OpenLDAP Software 2.6 Administrator's Guide
Lab Environment
| Component | Detail |
|---|---|
| Hypervisor | Parallels Desktop on Apple Silicon (ARM64) |
| OpenAM host | openam.corp.local / 10.211.55.18 / Ubuntu 24.04.3 ARM64 |
| Directory host | ldap.corp.local / 10.211.55.15 / Kali Linux 2025.2 ARM64 |
| Java | OpenJDK 11.0.31 (aarch64) |
| Servlet container | Apache Tomcat 9.0.107 |
| Access management | OpenAM 15.2.2 Community Edition (Build 55403318b2) |
| Directory server | OpenLDAP slapd 2.6.13 |
| Realm | /corp |
| Identity suffix | dc=corp,dc=local |
1. Environment Setup (Server and Dependencies)
A dedicated virtual machine was provisioned to host the access-management tier, with the fully qualified hostname openam.corp.local as required by the brief. The FQDN is mandatory rather than cosmetic: the OpenAM configurator refuses to run against a bare hostname or a raw IP address, because the server URL and the SSO cookie domain are both derived from it.
The host was given a static address of 10.211.55.18 through NetworkManager, replacing the DHCP lease assigned by the Parallels shared network. Because OpenAM writes its server URL into the configuration store at install time, a changing address would invalidate the deployment.
1.1 Java and directory layout — OpenJDK 11 was installed from the distribution repositories in line with the brief. Memory was raised to 8 GB, as the 2 GB minimum stated in the brief is insufficient once the OpenAM heap and the embedded directory server run alongside a graphical desktop session.
The tomcat9 package has been removed from the Ubuntu 24.04 repositories; only tomcat10 remains, which implements the Jakarta EE jakarta.servlet namespace and is therefore incompatible with the javax.servlet codebase that OpenAM is built against. Apache Tomcat 9.0.107 was instead deployed from the Apache archive using a split installation, so that the runtime directory layout still matches the paths named in the brief:
CATALINA_HOME = /opt/tomcat9 (binaries, shared libraries)
CATALINA_BASE = /var/lib/tomcat9 (conf, webapps, work, temp, logs)A setenv.sh script supplies the JVM options, including -Duser.home=/var/lib/tomcat9, which is what causes OpenAM to place its configuration and log directories beneath the required path rather than under the service account home.
1.2 Deploying the OpenAM WAR — ForgeRock closed the OpenAM source after release 13.5. The maintained community fork published by the Open Identity Platform project was used instead; release 15.2.2 was selected because the 15.x line targets Java 11, which the brief specifies. The archive was copied to the webapps directory as openam.war so that the deployed context path is /openam, which every URL in the brief depends upon.
2. Configure OpenAM (Initial Setup Wizard)
The Custom Configuration path was chosen over Default Configuration. The default path accepts only administrator passwords and derives everything else automatically, which would have prevented the server URL and the configuration directory from being set explicitly — both of which the brief requires.
The configuration directory was deliberately set to /var/lib/tomcat9/openam rather than the wizard default. This is the setting that places the OpenAM instance data and logs at the location the brief instructs the reader to inspect in Section 6.
The embedded directory server was used for the configuration store, as instructed. It is important to distinguish this from the identity store: the root suffix dc=openam,dc=openidentityplatform,dc=org holds OpenAM's own service configuration and is unrelated to dc=corp,dc=local, which is added separately in Section 3 as the source of user identities.
2.1 Verifying the installation — The brief states that OpenAM logs are located at /var/lib/tomcat9/openam/logs. In release 15.2.2 that path does not exist. The instance directories sit one level deeper and are split by purpose: debug/ holds service debug output (Configuration, Entitlement, Session) and log/ holds the audit trail as CSV files. This discrepancy is documented in Section 6.
3. Connect to LDAP (User Identity Store)
Rather than colocating the directory with OpenAM, the identity store was placed on a separate host. The existing OpenLDAP server on the Kali Linux VM was extended with a second database so that both directory trees coexist on one slapd instance. This produces a genuine cross-host LDAP bind over the network name ldap.corp.local, which is closer to a real deployment than a loopback connection and yields more meaningful evidence.
3.1 Creating the dc=corp,dc=local database — A second mdb database was added to the running slapd instance through the cn=config backend, with its own suffix, root DN, data directory and access control. The pre-existing dc=lab,dc=local tree was left untouched.
| Parameter | Value |
|---|---|
| Suffix | dc=corp,dc=local |
| Root DN | cn=admin,dc=corp,dc=local |
| Data directory | /var/lib/ldap/corp |
| Indexes | objectClass eq; cn,sn,uid,mail eq |
| Organization | o: CyberCorp |
3.2 Populating users and groups — Organizational units, the three users named in the brief, and three departmental groups were loaded by LDIF. Passwords were stored as salted SHA hashes generated with slappasswd. Groups use the groupOfNames object class with the member attribute.
Binding as alice directly against the directory before involving OpenAM was a deliberate ordering choice: it establishes that any subsequent authentication failure lies in the OpenAM configuration rather than in the directory itself. ldapwhoami returned dn:uid=alice,ou=People,dc=corp,dc=local.
3.3 Adding the Corp-LDAP data store — A Generic LDAPv3 data store named Corp-LDAP was created in the /corp realm. The generic type was selected rather than the OpenDJ type, which expects ForgeRock-specific schema not present in OpenLDAP. The option to load schema on save was deliberately left disabled to avoid writing OpenAM schema into a directory that also serves an unrelated database.
3.4 Authenticating alice through OpenAM — The profile page is the significant evidence here. The attribute values were not entered in OpenAM; they were read from the inetOrgPerson entry in OpenLDAP, which demonstrates that authentication and profile retrieval are both flowing through the external identity store.
4. Configure Single Sign-On (SSO)
Two applications, app1 (an HR portal) and app2 (a Finance portal), were deployed as JSP applications under /var/lib/tomcat9/webapps/. Each page prints its own host and path, the authenticated username, and the value of the iPlanetDirectoryPro SSO token cookie. Printing the token is what allows single sign-on to be demonstrated positively rather than merely inferred from the absence of a second login prompt.
Enforcement is performed by a guard include on each page which extracts the SSO cookie and validates it against the OpenAM session REST endpoint. If the token is missing or invalid, the request is redirected to the /corp realm login page with a goto parameter pointing back at the originally requested resource.
4.1 Demonstrating the SSO flow — An unauthenticated request to /app1/ was intercepted and redirected to the realm login page with the goto parameter preserved. After authentication, App1 rendered with the SSO token cookie iPlanetDirectoryPro = AQIC5wM2LY4SfcxdmHPFLauQxVJNXLFOk0alMIIC…
App2 was then opened in a second tab with no further credential prompt, presenting an identical token. The two captures were taken seventeen seconds apart and present the same token value from two independent applications. A single authentication event produced one session that both applications recognise, which is precisely the behaviour single sign-on is intended to deliver.
4.2 Authorization policies — A URL policy set was created in the /corp realm with the identifier iPlanetAMWebAgentService, and two policies were defined within it:
| Policy | Definition |
|---|---|
| App1-Access | http://openam.corp.local:8080/app1/* — GET, POST allowed |
| App2-Access | http://openam.corp.local:8080/app2/* — GET, POST allowed |
| Subject condition | Authenticated Users |
Rather than relying on the console view alone, the policies were exercised through the policy evaluation endpoint, using an administrator token to evaluate the decision for alice as subject. This confirms the policy engine actually reaches an Allow decision rather than simply that a policy object exists.
5. Risk-Adaptive Authentication
Two environmental risk signals named in the brief were implemented as policy conditions and then exercised through the evaluation endpoint, so that the change in the access decision is visible rather than merely configured.
5.1 Time-of-day condition — A SimpleTime condition was attached to App1-Access restricting access to business hours, with the enforcement time zone set to America/Vancouver. App2-Access was intentionally left without a time condition to serve as a control. At 17:38 PDT against a window of 08:00–17:00, App1 returned an empty action set while App2 returned GET and POST true — same user, same token, same instant.
The ttl values in that response are worth noting. App2, which carries no time condition, returns the maximum long value, indicating a decision with no expiry. App1 returns a concrete epoch timestamp, because the policy engine is telling the enforcement point that the decision is only valid until the time window closes. This is continuous evaluation rather than a one-off check at login.
5.2 Source address condition — An IPv4 condition was attached to App2-Access permitting only the address 10.211.55.18, the OpenAM host. The Kali host at 10.211.55.15 therefore falls outside the trusted range. The evaluation was run from both machines.
An initial attempt supplied the client address in the environment element of the evaluation request. OpenAM disregarded it and evaluated the real TCP source address instead. This is correct security behaviour: a condition that trusted a self-declared source address would be trivially bypassable. The demonstration was therefore repeated using two genuinely different hosts.
Taken together, the two evaluations show the same user presenting the same credentials against the same policy at essentially the same moment, with the access decision determined solely by the network location of the request. This satisfies the "unknown IP results in deny" requirement, and does so against real network separation rather than a spoofable request header.
6. Troubleshooting and Verification
Four issues were encountered and resolved during the build. Each is recorded below with the diagnostic method used, since the method is often more transferable than the fix.
6.1 tomcat9 unavailable on Ubuntu 24.04 — apt offered only tomcat10, which implements the Jakarta EE jakarta.servlet namespace, while OpenAM is built against javax.servlet and deploys to an empty context under Tomcat 10. Apache Tomcat 9.0.107 was installed from the Apache archive with a split CATALINA_HOME/CATALINA_BASE layout. OpenAM 15.2.2 then deployed successfully in 5.1 seconds.
6.2 Log directory path differs from the brief — catalina.out reported "Debug file can't be written : Failed to create debug directory", and the path given in the brief did not exist. The error was transient, logged before the configurator created the instance directories. OpenAM 15.2.2 nests them one level deeper, splitting debug/ from log/. No corrective action was required; the path in the brief reflects an earlier OpenAM release.
6.3 Data store connected but returned no subjects — The Corp-LDAP data store saved without error, but the Subjects tab returned no users. olcLogLevel on the directory server was temporarily set to "stats filter" and the incoming query captured. OpenAM was issuing SRCH base="ou=people,dc=openam,dc=openidentityplatform,dc=org" with result code 32 (noSuchObject) — the LDAP Organization DN had reverted to the configuration store root suffix rather than the identity suffix. The search filter itself was correct. Resetting the Organization DN to dc=corp,dc=local corrected the search base and all three users resolved.
6.4 Policies evaluated to an empty action set — Policy evaluation returned no actions for either application despite both policies appearing correctly configured in the console. Querying the stored definitions through the REST API showed the console resource builder had saved the pattern as https*://*openam.corp.local:*8080/*app1 — with no trailing /* , so no child resource could match — and the subject condition had been stored as type NONE, meaning the policy applied to no subject at all. Both policies were replaced by REST PUT with the exact resource pattern and subject type AuthenticatedUsers.
6.5 Non-blocking messages assessed — Two recurring log messages were investigated and determined to be benign, and are recorded to distinguish them from genuine faults:
- "Persistent search is not supported by the directory, persistent search will be disabled" — OpenAM attempts to register an OpenDJ persistent-search control that OpenLDAP does not implement, logs the failure, and falls back to polling.
- "slap_global_control: unrecognized control: 1.3.6.1.4.1.36733.2.1.5.1" — the directory-side record of the same probe. The OID belongs to the ForgeRock enterprise arc.
6.6 Verification summary — Directory connectivity was verified from the OpenAM host using the command named in the brief, adapted to the suffix in use:
ldapsearch -x -H ldap://ldap.corp.local -b "dc=corp,dc=local"- Tomcat service state and JVM configuration — systemctl status and the catalina.out startup banner.
- OpenAM deployment — "Deployment of web application archive ... has finished" and HTTP 302 on the /openam context.
- Directory bind as an end user — ldapwhoami returning dn:uid=alice,ou=People,dc=corp,dc=local.
- Identity store integration — user attributes rendered in the OpenAM profile page from the remote LDAP entry.
- Single sign-on — an identical iPlanetDirectoryPro token presented by two independent applications after one authentication.
- Policy enforcement — REST evaluation returning Allow, and returning Deny once time and source-address conditions were applied.
Conclusion
OpenAM 15.2.2 was deployed behind Apache Tomcat 9 on a dedicated host, configured through the custom wizard with an embedded configuration store, and integrated with an external OpenLDAP directory on a separate machine as the identity store for the /corp realm. Single sign-on was demonstrated across two web applications by showing the same session token presented by both after a single authentication, and adaptive access control was demonstrated by attaching time-of-day and source-address conditions to the authorization policies and observing the access decision change accordingly.
The exercise reinforced a point that recurs throughout identity management: a configuration screen showing the intended values is not evidence that the intended behaviour occurs. In two separate cases here — the reverted organization DN and the mangled policy resource pattern — the console displayed a plausible configuration while the underlying object was wrong. In both cases the fault was only identified by observing the system from outside the interface that created it, through directory-server query logging and through the REST representation of the stored objects.
Appendix A — Lab credentials and identifiers. Passwords are recorded separately and are not reproduced in this report.
| Account / Object | Value |
|---|---|
| OpenAM administrator | amAdmin |
| Policy agent account | UrlAccessAgent |
| Directory root DN | cn=admin,dc=corp,dc=local |
| Test users | uid=alice / uid=bob / uid=charlie, ou=People,dc=corp,dc=local |
| Groups | HR_Group, Finance_Group, IT_Group (ou=Groups) |
| Realm | /corp |
| Policy set ID | iPlanetAMWebAgentService |
| Policies | App1-Access, App2-Access |
| SSO cookie name | iPlanetDirectoryPro |
| Config store suffix | dc=openam,dc=openidentityplatform,dc=org |
| Identity store suffix | dc=corp,dc=local |