Separate identity proof from permission decisions, then design AtlasMart human/service roles that keep application data access distinct from cluster administration, backup, and monitoring.
Authentication, Authorization, Roles, Service Identities, and Least Privilege
AtlasMart begins security engineering by separating identity proof from permission and by giving each workload only the data-plane or control-plane authority it actually needs.
Separate authentication, authorization, and accounting/audit responsibilities before assigning permissions.
Distinguish human identities from workload/service identities and avoid shared database superusers.
Design roles for application read/write, monitoring, backup, and cluster administration using least privilege.
Use bounded credential lifetimes and observable denied/allowed actions without assuming short-lived credentials are available in every product.
1. A service account should not be a cluster administrator
AtlasMart's catalog API needs to read products. Its order service needs to mutate orders. A backup job needs snapshot/backup access. An operator occasionally needs to change cluster membership. These are different security subjects with different duties. Authentication establishes who or what is connecting; authorization decides what that authenticated identity may do. Combining them into one shared superuser credential destroys the boundary that makes compromise containable.
A human identity represents a person and should usually flow through an organization identity provider with accountable elevation. A service identity represents a workload and should be bound to one service or automation purpose. Both require revocation, rotation, and audit, but their lifecycle and interaction patterns differ.
2. Role design starts from duties, not product defaults
Least privilege means granting the minimum operations and resource scope needed for the current duty. It is not merely “read-only vs read-write.” Distributed databases expose data-plane operations, topology/configuration control, backup/restore, metrics, repair, user/role management, and sometimes cloud-project administration. A role that can read catalog rows should not automatically be able to add nodes, alter replication, read backups, or create new privileged users.
| Identity | Needed permissions | Should not have |
|---|---|---|
| catalog-api | Catalog read on its namespace | Cluster admin, backup, role management |
| orders-api | Order read/write inside owned boundary | Unrelated tenant/catalog admin |
| monitor-service | Metrics/health metadata | Business-data mutation |
| backup-service | Backup create/read and required key access | Normal application writes |
| on-call operator | Time-bounded approved admin scope | Permanent shared superuser |
3. Observable request path
A useful security trace records identity, credential type,
requested resource/action, authorization decision, policy/role
version, and correlation ID—without logging reusable secrets.
The decision “authenticated but denied
cluster:write” is valuable evidence: it proves
identity validation succeeded but the role boundary stopped
privilege escalation. Conversely, a successful request only
proves the evaluated policy allowed it; it does not prove the
caller's host is uncompromised or the action was
business-appropriate.
Application permissions and cluster-management permissions are separate attack surfaces. Treating database access as one broad privilege domain turns an application vulnerability into a topology, backup, and identity-management incident.
4. Deliberately wrong approach: a shared superuser in every service
Teams sometimes start development with one powerful credential and then carry it into production. If AtlasMart's catalog service is compromised, the attacker can then mutate orders, alter roles, or reconfigure the cluster even though the catalog workload never needed those operations. Shared credentials also make attribution weak: audit records tell you which credential acted, not which workload/person actually controlled it.
The repair is role separation plus individual workload identities, explicit resource scopes, credential rotation/revocation, and audited emergency elevation. Where the platform supports short-lived credentials, prefer them; otherwise simulate the same lifecycle with frequent rotation and narrow secrets rather than claiming every product has identical token semantics.
5. AtlasMart lab: authenticate, authorize, expire, deny
Python 3.13+ standard library only; verified with Python 3.13.5. This is a policy simulator, not a real identity provider or database ACL engine. No database server, directory service, cloud IAM, password, token, or privileged OS access is required.
from dataclasses import dataclass
PERMISSIONS = {
"catalog_reader": {"catalog:read"},
"order_writer": {"orders:read", "orders:write"},
"backup_service": {"backup:create", "backup:read"},
"monitor_service": {"metrics:read"},
"cluster_admin": {"cluster:read", "cluster:write", "roles:write"},
}
@dataclass
class Identity:
name: str
kind: str
roles: tuple
expires_at: int
identities = {
"catalog-api": Identity("catalog-api", "service", ("catalog_reader",), 120),
"orders-api": Identity("orders-api", "service", ("order_writer",), 120),
"backup-job": Identity("backup-job", "service", ("backup_service",), 105),
"oncall-alice": Identity("oncall-alice", "human", ("cluster_admin",), 103),
}
def authenticate(name, now):
ident = identities.get(name)
if ident is None:
return None, "unknown identity"
if now >= ident.expires_at:
return None, "credential expired"
return ident, "authenticated"
def authorize(ident, permission):
granted=set()
for role in ident.roles:
granted |= PERMISSIONS[role]
return permission in granted
def attempt(name, permission, now):
ident, reason = authenticate(name, now)
if not ident:
return (name, permission, "DENY", reason)
return (name, permission, "ALLOW" if authorize(ident, permission) else "DENY",
"role policy")
print("SEPARATE AUTHENTICATION FROM AUTHORIZATION")
for req in [
("catalog-api", "catalog:read", 100),
("catalog-api", "cluster:write", 100),
("backup-job", "backup:create", 100),
("backup-job", "orders:write", 100),
("oncall-alice", "cluster:write", 100),
("oncall-alice", "cluster:write", 104),
]:
print(attempt(*req))
print("\nBROKEN SHARED SUPERUSER")
shared_permissions={"catalog:read","orders:read","orders:write","backup:create","backup:read","metrics:read","cluster:read","cluster:write","roles:write"}
print("compromised catalog process can cluster:write:", "cluster:write" in shared_permissions)
print("\nREPAIRED LEAST PRIVILEGE")
print("catalog-api cluster:write:", attempt("catalog-api","cluster:write",100))
print("orders-api orders:write:", attempt("orders-api","orders:write",100))
print("monitor-service modeled role permissions:", sorted(PERMISSIONS["monitor_service"]))
print("short-lived on-call credential expires at t=103; use a fresh audited grant for later emergencies")
The catalog identity can read catalog data but is denied
cluster:write; the backup identity cannot mutate
orders; and the on-call credential stops authenticating after
its simulated expiry. The broken shared-superuser model
demonstrates why credential compromise must not imply
control-plane compromise.
6. Production judgment
Measure denied-operation rate, authentication failures, stale/unused privileged roles, credential age, break-glass usage, role-change events, and unexpected data-plane identities calling admin endpoints. Keep service identities unique enough to attribute actions. Protect the identity store itself, and ensure backup/monitoring roles do not become hidden read-all pathways to regulated data.
PostgreSQL 18 explicitly separates connection authentication from role-based privileges. Its current documentation also supports certificate-based client authentication over TLS. These are implementation anchors, not a requirement to use PostgreSQL in the chapter.
The next lesson asks what happens after identity is correct but sensitive bytes cross networks, memory, storage, snapshots, and backups.
Check your understanding
- What is the difference between authentication and authorization?
- Why are service identities preferable to one shared application login?
- Why should monitoring and backup use distinct roles?
- What does a denied admin request from an authenticated application identity prove?
- Why is permanent superuser access a poor break-glass mechanism?
Review the answers
1. Authentication establishes the identity of a caller; authorization evaluates whether that authenticated identity may perform an action on a resource.
2. They constrain privilege and improve attribution, rotation, revocation, and blast-radius control per workload.
3. They need different operations and data scopes; combining them creates unnecessary access paths and weakens incident containment.
4. The identity was recognized but policy prevented that action; it does not prove the caller itself is trustworthy.
5. It creates standing privilege. Emergency access should be time-bounded, approved, scoped, audited, and reviewed.
References
Foundational claims use standards, specifications, primary research, or current official documentation where practical. Product references are optional implementation anchors; the mandatory labs are vendor-neutral.
- PostgreSQL 18 client authentication — Current official separation of connection authentication from database role privileges.
- PostgreSQL 18 certificate authentication — Current certificate-authentication behavior for TLS connections.
- NIST SP 800-53 Rev. 5 — Security and privacy controls including access control, audit, identification/authentication, and system protection.
- OWASP Authentication Cheat Sheet — Practical authentication design and credential-handling guidance.