Turn access control on without creating a bootstrap exposure window, and prove exactly when the localhost exception exists and disappears.
Enable Authentication Safely and Understand the localhost Exception During Bootstrap
AtlasMart is moving from a private prototype to a shared service. Before any application account exists, the team must bootstrap the first administrator without briefly creating an unauthenticated network service.
Learning objectives
Define authentication, authorization, SCRAM, authentication database, and the localhost exception before using them.
Bootstrap the first administrator without exposing an unauthenticated service to an untrusted network.
Prove the boundary of the localhost exception with a non-local failure and a genuine local first-user creation.
Observe successful and failed authentication, authenticated identity, and the disappearance of bootstrap privilege after the first user exists.
Explain why a database user is not by itself a tenant-isolation or network-security boundary.
This lesson pins
MongoDB Community Server 8.3.8 with
mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim, mongosh 2.10.0, and
PyMongo 4.17.0 where driver behavior is useful. The
mandatory path is free/local and disposable. Topology: one
disposable standalone named atlasmart-ch22-l1.
Access control is enabled from first startup; the first
administrator is created through the localhost exception from
inside the container. The database is never published on an
untrusted interface; any host mapping is loopback-only on port
27175. Authentication/TLS state is stated next to
each experiment rather than assumed. Default read/write concern
and primary read preference are used unless an example says
otherwise.
FCV is observed and never changed. Atlas,
Enterprise Advanced, KMS, LDAP, Kerberos, OIDC, and commercial
SIEM products are optional discussion paths, not mandatory lab
dependencies. Secrets shown are synthetic local-only example
secrets or generated ephemeral material and must not be reused.
Product runtime labs were not executed in the generation
environment, so authentication outcomes, certificate
fingerprints, rotation timing, network observations, and
audit/log records must be measured locally rather than copied as
invented output.
1. Authentication and authorization solve different questions
Authentication proves which MongoDB principal
is making a request. Authorization evaluates
the roles and privileges attached to that authenticated
principal. MongoDB uses Salted Challenge Response Authentication
Mechanism (SCRAM) as the default password mechanism; this lab
creates a SCRAM-SHA-256 administrator. The
authentication database is where the user
record is stored and is part of the login identity—an
atlasAdmin user in admin is not the
same principal as an identically named user in another database.
| Question | Evidence | Non-guarantee |
|---|---|---|
| Who connected? |
connectionStatus and authentication
success/failure
|
Does not decide what that identity may do. |
| What may it do? | roles, inherited privileges, authorization success/failure | Does not make the network path private. |
| Is this tenant A? | application identity/authorization policy plus data-access tests |
A tenantId field alone is not an
authorization boundary.
|
| Is the channel private/authentic? | TLS certificate and hostname validation | Authentication without TLS can still expose credentials in transit. |
2. The localhost exception is a bootstrap escape hatch, not anonymous mode
When access control is enabled and there are
no users or roles, a self-managed
mongod permits a connection from its localhost
interface to create the first user or role. The exception can
also initiate a replica set. It does not grant a remote Docker
peer the same bootstrap privilege, and it ends once a user or
role exists. Therefore the safe mental model is: a narrowly
scoped local bootstrap capability exists only while the
authorization catalog is empty.
A host connection to 127.0.0.1:27175 is loopback
on the host, but Docker may present that connection to the
container as a bridge/NAT source rather than the server
process's own localhost. The lab deliberately uses
docker exec for the first user so the exception
is not accidentally dependent on Docker networking behavior.
docker network rm atlasmart-ch22-l1-net 2>/dev/null || truedocker network create atlasmart-ch22-l1-netdocker rm -f atlasmart-ch22-l1 2>/dev/null || truedocker run -d --name atlasmart-ch22-l1 --network atlasmart-ch22-l1-net \ -p 127.0.0.1:27175:27017 \ mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim --auth --bind_ip_alluntil docker logs atlasmart-ch22-l1 2>&1 | grep -q "Waiting for connections"; do sleep 1; done# This client is on the Docker network, but not on mongod's localhost.# Expect createUser to fail: the localhost exception must not apply here.docker run --rm --network atlasmart-ch22-l1-net mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim \ mongosh "mongodb://atlasmart-ch22-l1:27017/admin" --quiet --eval ' db.createUser({user:"should-not-work",pwd:"x",roles:[{role:"root",db:"admin"}]}) ' || true
3. Bootstrap the first administrator locally, then watch the exception disappear
The following operation is intentionally executed inside the
database container against 127.0.0.1. The first
user receives root only because this is the
isolated bootstrap administrator. Application identities created
later must be narrower. The lab password is synthetic and
short-lived; production bootstrap should inject a generated
secret without placing it in source control or shell history.
docker exec atlasmart-ch22-l1 mongosh --quiet --host 127.0.0.1 --port 27017 --eval ' const admin=db.getSiblingDB("admin"); admin.createUser({ user:"atlasAdmin", pwd:"local-only-change-me", roles:[{role:"root",db:"admin"}], mechanisms:["SCRAM-SHA-256"] }); printjson(admin.runCommand({connectionStatus:1}));'# After the first user exists, an unauthenticated local connection no longer has# first-user creation privilege. Expect Unauthorized.docker exec atlasmart-ch22-l1 mongosh --quiet --host 127.0.0.1 --eval ' db.getSiblingDB("admin").createUser({user:"second-anonymous",pwd:"x",roles:[]})' || true# Authenticated administrator succeeds.mongosh "mongodb://atlasAdmin:local-only-change-me@127.0.0.1:27175/admin?authSource=admin" --quiet --eval ' printjson(db.runCommand({connectionStatus:1,showPrivileges:true})); db.getSiblingDB("atlasmart").orders.insertOne({orderId:"SEC-2201",status:"created"}); printjson(db.getSiblingDB("atlasmart").orders.findOne({orderId:"SEC-2201"}));'
4. Wrong approaches: public bind, bootstrap racing, and credentials without TLS
A common bootstrap mistake is to start MongoDB on
0.0.0.0 with authorization disabled, create an
administrator later, and assume the exposure window was too
short to matter. Another is to start with
--auth but publish the port broadly and depend on
the localhost exception from a network path whose source
identity is not actually localhost. The repaired pattern is
narrower: access control from first startup, no untrusted
network exposure, one deliberate local first-user creation, then
explicit authenticated tests.
-
Do not run:
docker run -p 27017:27017 ...on an untrusted host while access control is absent. -
Do not infer: an authentication success does
not mean TLS protected the password.
createUserdata is sent in cleartext on an unencrypted connection even though MongoDB does not store the password in cleartext. - Do not reuse: the bootstrap root identity in the application. Create service-specific roles/users and keep administration separate.
- Do not confuse: database authentication with application tenant authorization. The application must still constrain which tenant data each request may access.
5. Verification and cleanup
- The non-local Docker peer could not create the first user.
-
atlasAdminauthenticates with SCRAM-SHA-256 andconnectionStatusreports it. - After the first user exists, unauthenticated localhost first-user creation fails.
- Unauthenticated application reads fail while authenticated reads succeed.
-
The host mapping is exactly
127.0.0.1:27175, never a wildcard/public host address.
docker rm -f atlasmart-ch22-l1 2>/dev/null || truedocker network rm atlasmart-ch22-l1-net 2>/dev/null || truedocker volume rm atlasmart-ch22-l1-db 2>/dev/null || true
6. Production judgment
Enable access control before a database becomes reachable by anything you do not fully trust. Keep the first administrator separate from runtime identities, use TLS before sending real credentials, and treat bootstrap as a one-time ceremony with recorded ownership and recovery procedures. In replica sets and sharded clusters, internal member authentication becomes another boundary; the TLS/keyfile lifecycle is handled explicitly in Lesson 3. Authentication failures, user/role changes, unexpected source addresses, and repeated privilege failures are observability signals—not just troubleshooting noise. The next lesson narrows the broad bootstrap administrator into service-specific roles that can be proven with negative tests.
Check your understanding
- When does the localhost exception apply?
- Why does a host connection to 127.0.0.1 not automatically prove Docker-side localhost?
- Does authentication encrypt the password in transit?
- Why should the application not use the bootstrap root account?
- Is tenantId by itself an authorization boundary?
Review the answers
1. Only while no users or roles exist, and only to a connection that is local to the MongoDB server; it is intended to create the first user/role (and can initiate a replica set).
2. Docker NAT/bridge networking can present a non-loopback source to the container, so the lab uses docker exec and the server's own 127.0.0.1 for bootstrap.
3. No. TLS is the transport protection. SCRAM authenticates the user but an unencrypted createUser/authentication path can expose sensitive data in transit.
4. Root has far more privilege than normal business operations require, increasing blast radius and destroying least-privilege evidence.
5. No. Tenant isolation requires application/database authorization controls and tests that prevent cross-tenant access.
Authoritative references
Version-sensitive behavior in this lesson was checked against current MongoDB documentation at generation time. Re-check these sources before applying the procedures to a later server or managed-service release.
- MongoDB Security
- Security Checklist for Self-Managed Deployments
- Localhost Exception in Self-Managed Deployments
- Role-Based Access Control in Self-Managed Deployments
- SCRAM Authentication
- createUser Command
- Built-In Roles
- Privilege Actions
- User-Defined Roles
- Configure MongoDB Instances for TLS
- Connection String TLS Options
- Self-Managed Internal/Membership Authentication
- Rotate Keys for Self-Managed Replica Sets
- rotateCertificates Command
- IP Binding in Self-Managed Deployments
- Auditing
- Configure Audit Filters
- System Event Audit Messages
- Atlas Database Auditing
- MongoDB 8.3 Release Notes
- mongosh Release Notes
- PyMongo Release Notes