Checkpoint Lab — LDAP, SAML, OIDC, External Identity, Credential Rotation, and Authentication Troubleshooting
Model one external identity integration end to end, prove mapping, rotate a fake bind credential, diagnose three failures, preserve local recovery access, and assemble an operator evidence packet.
Checkpoint objectives
- Model one LDAP integration end to end using synthetic records.
- Prove external-group-to-Nexus-role intent and positive/negative mapping outcomes.
- Rotate a fake bind credential without exposing either value.
- Diagnose three independent authentication failures from evidence.
- Preserve and verify a local recovery path and produce a secret-free evidence packet.
Checkpoint safety boundary. This lab does not change LDAP/SAML/OIDC settings on the running Nexus instance. It reads current version/license/realm state and models external identity locally. That makes the mandatory path safe on Community Edition and prevents accidental lockout of a shared Nexus instance. SAML and OIDC remain Pro-only self-hosted features.
1. Scenario
Your organization wants centralized identity for Nexus. Community Edition is the mandatory baseline, so LDAP is the deployable external source today. SAML/OIDC may be evaluated after a Pro upgrade. You receive a synthetic directory export, three failure logs, and a target Nexus role. Your job is to prove the mapping model, rotate the directory bind credential, diagnose the failures, and show that emergency local administration is still available.
2. Exact assumptions
- Self-hosted Nexus Repository 3.95.0 reference line; record actual running version.
- Java 21 for current H2/PostgreSQL Nexus lines.
- Community Edition mandatory path: LDAP supported; SAML/OIDC modeled only because they are Pro-only self-hosted features.
- Disposable local Nexus may use H2 for learning; production database guidance remains external PostgreSQL.
- No blob/database mutation; no production IdP; no real secrets; no public exposure.
-
POSIX/Bash examples; Windows users can reproduce fixture/hash
logic with PowerShell and
Get-FileHash.
3. Setup and preflight
export NX_URL="http://127.0.0.1:8081"
export LAB="$(mktemp -d)"
mkdir -p "$LAB/fixtures" "$LAB/evidence"
chmod 700 "$LAB"
export NX_USER="admin"
read -r -s -p 'Disposable Nexus admin password: ' NX_PASS; echo
export NX_AUTH_FILE="$(mktemp)"; chmod 600 "$NX_AUTH_FILE"
printf 'machine 127.0.0.1 login %s password %s
' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/status" > "$LAB/evidence/status.txt"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/system/license" > "$LAB/evidence/license.json" || true
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/realms/active" > "$LAB/evidence/realms-before.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/roles" > "$LAB/evidence/roles-before.json"
Before continuing, verify that at least one known local authentication/recovery path is active and that you can still authenticate with the disposable administrator. Do not change realm order in this checkpoint.
4. Predictions before action
Write at least these predictions into
$LAB/evidence/predictions.txt:
-
A user in external group
nexus-ch16-readersshould map to roleacademy-ch16cp-reader; a user infinance-onlyshould not. - Rotating the fake LDAP bind secret should invalidate the old secret while leaving the mapping and repository authorization policy unchanged.
- A successful bind with zero user-search matches is an LDAP search configuration failure, not a Nexus repository privilege failure.
- The current active realm list will be unchanged because this checkpoint models rather than enables external auth.
cat > "$LAB/evidence/predictions.txt" <<'TXT'
1. nexus-ch16-readers -> academy-ch16cp-reader; finance-only -> no role.
2. Old bind credential fails after rotation; new credential succeeds; role mapping is unchanged.
3. bind=success + user_search=0 means search/base/filter failure, not repository authorization.
4. Nexus active realm order remains unchanged throughout this modeled checkpoint.
TXT
5. Create the synthetic LDAP directory and mapping
cat > "$LAB/fixtures/directory.json" <<'JSON'
{
"service_account": "cn=nexus-bind,ou=svc,dc=example,dc=invalid",
"users": [
{"uid":"ada","dn":"uid=ada,ou=people,dc=example,dc=invalid","groups":["nexus-ch16-readers"]},
{"uid":"grace","dn":"uid=grace,ou=people,dc=example,dc=invalid","groups":["finance-only"]}
]
}
JSON
cat > "$LAB/fixtures/nexus-mapping.json" <<'JSON'
{
"external_group": "nexus-ch16-readers",
"nexus_role": "academy-ch16cp-reader",
"intended_privileges": [
"nx-repository-view-raw-example-browse",
"nx-repository-view-raw-example-read"
]
}
JSON
The role ID is intentionally synthetic; the checkpoint proves the identity-to-role contract without changing a real repository. In production, create the actual narrow role through supported Nexus UI/API and map the exact external group to it.
6. Prove positive and negative role mapping
python - <<'PY' | tee "$LAB/evidence/mapping-results.txt"
import json, os
from pathlib import Path
root=Path(os.environ['LAB'])/'fixtures'
directory=json.loads((root/'directory.json').read_text())
mapping=json.loads((root/'nexus-mapping.json').read_text())
for user in directory['users']:
allowed=mapping['external_group'] in user['groups']
role=mapping['nexus_role'] if allowed else '<none>'
print(f"user={user['uid']} groups={user['groups']} mapped_role={role}")
PY
Expected: Ada maps to academy-ch16cp-reader; Grace does
not. If both map, your mapping condition is overbroad. If neither
maps, the external group string or source attribute is wrong.
7. Rotate the fake LDAP bind credential
export OLD_BIND_SECRET="$(python - <<'PY'
import secrets
print(secrets.token_urlsafe(24))
PY
)"
export NEW_BIND_SECRET="$(python - <<'PY'
import secrets
print(secrets.token_urlsafe(24))
PY
)"
python - <<'PY' > "$LAB/evidence/rotation.txt"
import hashlib, hmac, os
old=os.environ['OLD_BIND_SECRET'].encode(); new=os.environ['NEW_BIND_SECRET'].encode()
accepted=new
for label, candidate in [('old-after-cutover',old),('new-after-cutover',new)]:
ok=hmac.compare_digest(hashlib.sha256(candidate).digest(), hashlib.sha256(accepted).digest())
print(f'{label}: authenticated={ok}')
print('old_fingerprint='+hashlib.sha256(old).hexdigest())
print('new_fingerprint='+hashlib.sha256(new).hexdigest())
PY
unset OLD_BIND_SECRET NEW_BIND_SECRET
The evidence file contains only one-way fingerprints and boolean results. In a real LDAP rotation, validate Nexus bind and search after updating the configured credential. Do not copy the secret into incident notes.
8. Diagnose three independent failures
cat > "$LAB/fixtures/failure-cases.json" <<'JSON'
[
{"id":"ldap-search","protocol":"LDAP","connect":"ok","bind":"ok","user_search":0,"base":"ou=wrong,dc=example,dc=invalid"},
{"id":"saml-flow","protocol":"SAML","idp_login":"ok","audience":"https://old.example.invalid","expected_audience":"https://nexus.example.invalid/service/rest/v1/security/saml/metadata","cookie_present":false},
{"id":"oidc-claims","protocol":"OIDC","provider_login":"ok","issuer":"https://id.example.invalid/realms/academy","groups_claim_name":"roles","token_has_groups_claim":true,"mapped_groups":[]}
]
JSON
python - <<'PY' | tee "$LAB/evidence/diagnosis.txt"
import json, os
from pathlib import Path
cases=json.loads((Path(os.environ['LAB'])/'fixtures/failure-cases.json').read_text())
for c in cases:
if c['id']=='ldap-search':
diagnosis='user search base/filter: bind succeeded but zero users matched'
elif c['id']=='saml-flow':
diagnosis='SAML audience/ACS/browser correlation: wrong audience and missing SAML2_AUTH_REQUEST cookie'
else:
diagnosis='OIDC claim mapping: configured groups claim does not match the token field used for groups'
print(c['id']+': '+diagnosis)
PY
Each diagnosis stops before repository authorization because identity has not yet been established correctly. That is the key operational discipline.
9. Add a rate-limit interpretation drill
HTTP/1.1 429 Too Many Requests
Retry-After: 30
principal: ch16cp-service
mechanism: Basic
Prediction: this is not proof of DNS/TLS failure. The server
responded and is throttling repeated authentication failures. Stop
retries, wait for Retry-After, correct the credential,
and make one controlled request. Do not disable rate limiting to
hide a bad secret.
10. Prove the secure local recovery path
Re-read active realms and perform one authenticated status request with the disposable local administrator. This is a recovery-path proof, not a reason to use that identity routinely.
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/realms/active" > "$LAB/evidence/realms-after.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/status" > "$LAB/evidence/local-recovery-status.txt"
cmp -s "$LAB/evidence/realms-before.json" "$LAB/evidence/realms-after.json" && echo 'realm-state: unchanged' | tee "$LAB/evidence/realm-compare.txt" || echo 'realm-state: DIFFERENT - investigate before cleanup' | tee "$LAB/evidence/realm-compare.txt"
If the realm files differ unexpectedly, stop and investigate. The mandatory checkpoint did not require a realm mutation.
11. Required evidence packet
- actual Nexus version/edition and Java/runtime assumptions;
- active realms before/after;
- role inventory snapshot;
- synthetic directory and mapping files;
- positive/negative mapping results;
- bind-rotation booleans + fingerprints only;
- three failure diagnoses;
- 429 interpretation;
- local recovery status;
- no passwords, client secrets, bearer tokens, private keys, or Authorization headers.
12. Verification checklist
- Ada maps; Grace does not.
- Old fake bind credential is rejected after cutover; new fake credential is accepted.
- LDAP search failure is diagnosed after successful bind.
- SAML case is diagnosed at audience/cookie/ACS layer.
- OIDC case is diagnosed at claim mapping.
- 429 is treated as rate limiting, not network outage.
- Active realm state is unchanged.
- Local recovery identity still authenticates.
- SAML/OIDC remain clearly labeled Pro-only in self-hosted Nexus.
13. Cleanup and rollback
rm -f "$NX_AUTH_FILE"
unset NX_AUTH_FILE NX_USER
rm -rf "$LAB"
No Nexus external-identity settings were changed, so no server rollback is needed. If you chose an optional live LDAP exercise, remove only the disposable LDAP configuration/provider and restore the exact recorded active realm list. Never delete Nexus database/blob data to clean up identity configuration.
14. Knowledge check
Why does this checkpoint model LDAP instead of requiring a real directory?
The prompt requires a free-compatible path and explicitly allows a synthetic identity fixture; modeling teaches the protocol and mapping without risking enterprise identity infrastructure.
What is proven by comparing realms-before and realms-after?
That the checkpoint preserved the running Nexus authentication-source configuration while testing the external-identity model.
Which failure is indicated by bind=ok and user_search=0?
LDAP search base/filter/schema mapping, not network transport or repository privilege.
Why are secret fingerprints acceptable evidence while plaintext secrets are not?
A fingerprint can identify which rotation phase/value was tested without exposing the reusable credential itself.
What does Chapter 16 add to the production operating model?
An external-identity runbook covering protocol/edition choice, realm precedence, group-to-role mapping, browser-vs-client credentials, secret/certificate rotation, failure-layer diagnosis, rate limiting, and local recovery.
15. Production operating model and bridge to Chapter 17
Chapter 16 adds identity federation as an explicit dependency with ownership and recovery boundaries. You can now document who owns the LDAP/IdP, which exact external group maps to which Nexus role, how humans and package clients authenticate differently, how to rotate secrets/certificates, and how to prove a failure before changing policy.
Chapter 17 moves outward to TLS, reverse proxies, context paths, HTTP settings, network boundaries, and secure exposure—the transport layer that external identity and repository clients both depend on.
Official references and version notes
- Sonatype: Authentication and Realms.
- Sonatype: LDAP — bind/search, user/group mapping, LDAP cache, LDAPS trust.
- Sonatype: SAML — Pro-only browser SSO, metadata, ACS, signing, external role mapping.
- Sonatype: OpenID Connect — self-hosted Pro from 3.86, claim mapping, OAuth2 realm, truststore support.
- Sonatype: Security Management API — users, roles, privileges, realms and Pro OIDC configuration endpoints.
- Sonatype: Authentication Attempt Rate Limiting.
- Self-Hosted Nexus Repository Feature Matrix — LDAP Community/Pro; SAML and User Tokens Pro.
- Sonatype: Download and Java Runtime Compatibility Matrix.
Version-sensitive statements were rechecked on 2026-08-26. The current official direct-download page lists Nexus Repository 3.95.0. Nexus 3.87+ on H2/PostgreSQL requires Java 21. The mandatory learning path is Community-compatible and models external identity with local fixtures; SAML and OIDC are optional Pro-only integrations for self-hosted Nexus.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.