Users, Roles, Privileges, Realms, Anonymous Access, User Tokens, and Least-Privilege Design: Guided Hands-On Workflow and Core Operations
Create disposable local identities and roles, disable anonymous access on a dedicated lab instance, test least-privilege reads and writes, rotate a service credential, and preserve secret-safe evidence.
Learning objectives
- Create disposable local users and roles without using shared administrator credentials for client work.
- Temporarily disable anonymous access on a dedicated lab instance and restore it afterward.
- Prove read/write/delete behavior with positive and negative HTTP tests.
- Rotate a service-account password without printing it or placing it in command history.
- Capture identity and authorization evidence without storing secrets.
Lab boundary. Run this only on a disposable Nexus instance dedicated to the academy. Changing global anonymous access affects every repository on that instance. Capture the pre-lab anonymous configuration and restore it during cleanup. Do not perform this exercise on a shared or employer Nexus server.
1. Scenario and state model
You will create one Raw hosted repository,
academy-ch15-hosted, and three local identities:
| Identity | Role intent | Allowed | Denied |
|---|---|---|---|
| ch15-reader | Consumer | Browse/read lab repository | Add/edit/delete and security administration |
| ch15-publisher | Human publisher | Browse/read/add/edit lab repository | Delete and administration |
| ch15-svc-ci | Automation publisher | Same narrow repository content actions required by CI | Delete, user/role changes, repository administration |
All three authenticate through the local realm. The publisher and service account share a role definition, but remain separate principals so their credentials can be rotated, revoked, and audited independently.
Shell note. Command blocks in this lab use
POSIX/Bash syntax. On Windows PowerShell, use
$env:TEMP or New-TemporaryFile for
temporary files, Get-FileHash -Algorithm SHA256 for
checksums, and curl.exe or
Invoke-WebRequest with credentials supplied from a
protected variable/credential object. Never place passwords
directly in a command line or committed script.
2. Preflight: prove version, edition, and current security state
export NX_URL="http://127.0.0.1:8081"
export LAB="$(mktemp -d)"
mkdir -p "$LAB/evidence" "$LAB/payload"
printf 'chapter-15 identity lab\n' > "$LAB/payload/release.txt"
sha256sum "$LAB/payload/release.txt" > "$LAB/evidence/payload.sha256"
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\n' "$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/anonymous" > "$LAB/evidence/anonymous-before.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/realms/active" > "$LAB/evidence/realms-active.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/users" > "$LAB/evidence/users-before.json"
Record the actual running version. The reference baseline is official self-hosted 3.95.0 with Java 21. If your instance reports another supported version, use its own Swagger UI to confirm endpoint schemas before copying any API mutation.
3. Disable anonymous access only on the disposable instance
Open Settings → Security → Anonymous Access. Record
the current values from anonymous-before.json, then
disable anonymous access and save. Verify through
GET /service/rest/v1/security/anonymous and with an
unauthenticated request after the repository exists.
Why use the UI for this mutation in the lab? The REST endpoint is documented, but the exact request model can evolve. The lab's core lesson is the security state transition, not memorizing one payload shape. Chapter 20 will focus on API provisioning.
Rollback requirement. If the instance was intentionally configured for anonymous access before this lab, the cleanup must restore the exact previous setting. Do not assume the desired post-lab value is “disabled.”
4. Create the disposable repository
Create academy-ch15-hosted as a Raw hosted repository
using the default lab blob store, strict content-type validation,
and an ordinary write policy appropriate for the disposable lab.
Then inspect it:
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/repositories/academy-ch15-hosted" > "$LAB/evidence/repository.json"
The repository configuration is not identity state. It establishes the resource that privileges will reference.
5. Create narrow roles
Use Settings → Security → Roles. Create:
| Role | Privileges |
|---|---|
academy-ch15-reader |
nx-repository-view-raw-academy-ch15-hosted-browse
and ...-read
|
academy-ch15-publisher |
Reader privileges plus ...-add and
...-edit; no delete
|
Do not add nx-admin, nx-all,
repository-admin privileges, user-management privileges, or wildcard
repository privileges. A role name such as “publisher” has no
security effect by itself; the applied privilege list is the policy.
6. Create three local identities
Use Settings → Security → Users. Create
ch15-reader with the reader role,
ch15-publisher with the publisher role, and
ch15-svc-ci with the same narrow publisher role. Set
strong temporary lab passwords interactively. Do not paste them into
lesson notes or evidence files.
The service identity is a separate user because automation needs
independent lifecycle control. If the pipeline is deleted later, you
can disable/remove ch15-svc-ci without affecting a
human publisher.
User Token note. On current self-hosted Community
Edition, do not enable or require User Tokens because that feature
is Pro-only. If your lab is Pro-licensed, an optional extension
later can generate a User Token for ch15-svc-ci; the
authorization matrix must remain identical.
7. Capture client credentials without exposing them
read -r -s -p 'Temporary password for ch15-reader: ' READER_PASS; echo
export READER_AUTH="$(mktemp)"; chmod 600 "$READER_AUTH"
printf 'machine 127.0.0.1 login ch15-reader password %s\n' "$READER_PASS" > "$READER_AUTH"
unset READER_PASS
read -r -s -p 'Temporary password for ch15-publisher: ' PUBLISHER_PASS; echo
export PUBLISHER_AUTH="$(mktemp)"; chmod 600 "$PUBLISHER_AUTH"
printf 'machine 127.0.0.1 login ch15-publisher password %s\n' "$PUBLISHER_PASS" > "$PUBLISHER_AUTH"
unset PUBLISHER_PASS
read -r -s -p 'Temporary password for ch15-svc-ci: ' SVC_PASS; echo
export SVC_AUTH="$(mktemp)"; chmod 600 "$SVC_AUTH"
printf 'machine 127.0.0.1 login ch15-svc-ci password %s\n' "$SVC_PASS" > "$SVC_AUTH"
unset SVC_PASS
export REPO_URL="$NX_URL/repository/academy-ch15-hosted"
Netrc is used only as a disposable local mechanism so passwords do
not appear in curl arguments. In production, use the
organization's approved secret store/client credential mechanism.
8. Prove the initial authorization matrix
# Anonymous read should fail after anonymous access is disabled.
curl -sS -o "$LAB/evidence/anon-read.body" -w 'anonymous read %{http_code}\n' "$REPO_URL/release.txt" | tee "$LAB/evidence/anon-read.status"
# Reader may read a published artifact, but should not upload.
curl -sS --netrc-file "$READER_AUTH" --upload-file "$LAB/payload/release.txt" -o "$LAB/evidence/reader-put.body" -w 'reader PUT %{http_code}\n' "$REPO_URL/reader-must-not-write.txt" | tee "$LAB/evidence/reader-put.status"
# Service account publishes the intended artifact.
curl -sS --netrc-file "$SVC_AUTH" --upload-file "$LAB/payload/release.txt" -o "$LAB/evidence/svc-put.body" -w 'service PUT %{http_code}\n' "$REPO_URL/release.txt" | tee "$LAB/evidence/svc-put.status"
curl -sS --netrc-file "$READER_AUTH" -o "$LAB/evidence/reader-read.txt" -w 'reader GET %{http_code}\n' "$REPO_URL/release.txt" | tee "$LAB/evidence/reader-read.status"
sha256sum "$LAB/evidence/reader-read.txt" > "$LAB/evidence/reader-read.sha256"
Do not hard-code one denial status across every proxy/client combination. Require the policy invariant: anonymous and reader writes do not create content, while the service account can publish and the reader can retrieve the resulting bytes.
9. Prove delete is absent
Discover the asset ID using the administrator's read access, then attempt deletion with the service identity. This proves that “publisher” really excludes removal:
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/assets?repository=academy-ch15-hosted" > "$LAB/evidence/assets-before-delete.json"
export ASSET_ID="$(python - <<'PYI'
import json, os
from pathlib import Path
p=Path(os.environ['LAB'])/'evidence/assets-before-delete.json'
for item in json.loads(p.read_text()).get('items', []):
if item.get('path') == 'release.txt':
print(item['id']); break
PYI
)"
test -n "$ASSET_ID"
curl -sS --netrc-file "$SVC_AUTH" -X DELETE -o "$LAB/evidence/svc-delete.body" -w 'service DELETE %{http_code}\n' "$NX_URL/service/rest/v1/assets/$ASSET_ID" | tee "$LAB/evidence/svc-delete.status"
curl -fsS --netrc-file "$READER_AUTH" "$REPO_URL/release.txt" -o "$LAB/evidence/after-delete-attempt.txt"
If deletion succeeds, stop. The service account has an unintended broader privilege through another role; do not continue until the grant is identified.
10. Rotate the service credential and prove revocation
In Settings → Security → Users, change the password
for ch15-svc-ci. Do not reuse the old secret. Preserve
the existing netrc file temporarily as the “old credential” test,
then create a second private netrc file with the new password.
# Keep the existing private SVC_AUTH only long enough for one negative test.
# Never copy credential material into the evidence directory.
read -r -s -p 'New temporary password for ch15-svc-ci: ' SVC_NEW_PASS; echo
export SVC_NEW_AUTH="$(mktemp)"; chmod 600 "$SVC_NEW_AUTH"
printf 'machine 127.0.0.1 login ch15-svc-ci password %s\n' "$SVC_NEW_PASS" > "$SVC_NEW_AUTH"
unset SVC_NEW_PASS
curl -sS --netrc-file "$SVC_AUTH" -o /dev/null -w 'old credential %{http_code}\n' "$REPO_URL/release.txt" | tee "$LAB/evidence/old-credential.status"
curl -sS --netrc-file "$SVC_NEW_AUTH" -o /dev/null -w 'new credential %{http_code}\n' "$REPO_URL/release.txt" | tee "$LAB/evidence/new-credential.status"
# The old netrc stays outside the evidence directory and is removed during cleanup.
# Evidence contains only the HTTP status, never the old or new credential.
Current Nexus releases invalidate sessions immediately when passwords change. The important lab result is independent credential lifecycle: the old automation secret stops working while the identity's role remains unchanged.
11. Inspect effective configuration without exposing secrets
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/users" > "$LAB/evidence/users-final.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/roles" > "$LAB/evidence/roles-final.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/anonymous" > "$LAB/evidence/anonymous-during-lab.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/realms/active" > "$LAB/evidence/realms-final.json"
If you inspect request/application logs, extract only the timestamp, principal name, request path, and outcome needed to explain the test. Do not copy Authorization headers, netrc contents, passwords, tokens, or unrelated user data into the evidence packet.
12. Challenge: choose the control
A fourth identity needs to download from
academy-ch15-hosted but must not browse the UI and must
never publish. Should you enable anonymous access, assign the
publisher role, or build a narrower role? Explain which exact
privilege action is required and which should be absent. Then prove
your answer with one successful and one denied request.
13. Cleanup and rollback
-
Delete local users
ch15-reader,ch15-publisher, andch15-svc-cithrough supported Security UI/API operations. - Delete the two custom roles after users no longer reference them.
-
Delete
academy-ch15-hostedthrough the repository UI/API. -
Restore the exact anonymous-access configuration captured in
anonymous-before.json. - Delete temporary credential files and the local lab workspace.
rm -f "$READER_AUTH" "$PUBLISHER_AUTH" "$SVC_AUTH" "$SVC_NEW_AUTH" "$NX_AUTH_FILE"
unset READER_AUTH PUBLISHER_AUTH SVC_AUTH SVC_NEW_AUTH NX_AUTH_FILE NX_USER REPO_URL ASSET_ID
rm -rf "$LAB"
14. Knowledge check
Why do publisher and service account use separate users even if their roles are identical?
Independent users give separate credentials, rotation, revocation, ownership, and auditability.
Why is changing global anonymous access allowed only on a disposable instance here?
It is instance-wide security state that can affect every repository and client, so changing it on a shared system can create outages or exposure.
What does the negative DELETE test prove?
That the service identity lacks a broader delete grant through any of its effective roles.
Why preserve the old credential just long enough to test rotation?
To prove revocation behavior; the old secret file is then immediately destroyed so evidence contains only the outcome, not the secret.
Why does this Community lab use a password rather than a Nexus User Token?
Current self-hosted User Token Support is Pro-only; the learning objective is dedicated service identity and credential lifecycle, which Community can demonstrate safely.
15. Summary and next step
You converted the identity chain into observable policy: named principals, reusable roles, least-privilege actions, anonymous-access control, negative tests, and independent credential rotation. Lesson 3 evaluates when local identities are appropriate, when external identity should own lifecycle, and how to prevent role convenience from becoming privilege creep.
Official references and version notes
- Sonatype: Access Control — RBAC object model, default roles, additive grants, and least privilege.
- Sonatype: Users and Roles.
- Sonatype: Privileges — repository, application, selector, and wildcard privilege semantics.
- Sonatype: Realms and Authentication.
- Sonatype: Anonymous Access.
- Sonatype: User Tokens and User Tokens API.
- Sonatype: Security Management API and API Reference.
- Self-Hosted Nexus Repository Feature Matrix — User Token Support and enterprise identity boundaries.
- Sonatype: Download and 2026 self-hosted release notes.
Version-sensitive statements were rechecked on 2026-08-26. Sonatype's current official self-hosted download page exposes Nexus Repository 3.95.0 (archive build 3.95.0-07), while version-index pages may lag. The mandatory chapter path is Community-compatible: local users, roles, privileges, realms, anonymous-access settings, Raw repositories, and temporary passwords. Self-hosted User Token Support is Pro-only and appears only as an optional extension or fixture.
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.