Skip to content

Authentication

NORA supports Basic Auth (htpasswd with bcrypt) and revocable API tokens with role-based access control (RBAC).

Terminal window
# Create file with first user
htpasswd -cbB users.htpasswd admin yourpassword
# Add more users
htpasswd -bB users.htpasswd ci-user ci-secret
htpasswd -bB users.htpasswd developer dev-pass

Note: The -B flag enables bcrypt hashing, which is required by NORA. Apache htpasswd is part of the apache2-utils (Debian/Ubuntu) or httpd-tools (RHEL/CentOS) package.

Terminal window
docker run -d -p 4000:4000 \
-v nora-data:/data \
-v ./users.htpasswd:/data/users.htpasswd:ro \
-e NORA_AUTH_ENABLED=true \
ghcr.io/getnora-io/nora:latest

Docker Compose:

services:
nora:
image: ghcr.io/getnora-io/nora:latest
ports:
- 4000:4000
volumes:
- nora-data:/data
- ./users.htpasswd:/data/users.htpasswd:ro
environment:
NORA_AUTH_ENABLED: true
restart: unless-stopped
volumes:
nora-data:
Terminal window
# Should return 401
curl -s http://localhost:4000/v2/_catalog
# {error:Authentication required}
# Should return 200
curl -s -u admin:yourpassword http://localhost:4000/v2/_catalog

API tokens provide programmatic access without exposing htpasswd credentials. Each token has a role, TTL, and optional description.

Terminal window
curl -s -X POST http://localhost:4000/api/tokens \
-H Content-Type: application/json \
-d '{
username: admin,
password: yourpassword,
role: write,
ttl_days: 90,
description: CI/CD pipeline
}'

Response:

{
token: nra_a1b2c3d4e5f6...,
expires_in_days: 90
}

Save the token immediately — it is only shown once.

Terminal window
# Login using token
docker login localhost:4000 -u token -p nra_a1b2c3d4e5f6...
# Push images as usual
docker push localhost:4000/myapp:latest
Terminal window
curl -H Authorization: Bearer nra_a1b2c3d4e5f6... \
http://localhost:4000/v2/_catalog
Terminal window
curl -s -X POST http://localhost:4000/api/tokens/list \
-H Content-Type: application/json \
-d '{username: admin, password: yourpassword}'
Terminal window
curl -s -X POST http://localhost:4000/api/tokens/revoke \
-H Content-Type: application/json \
-d '{
username: admin,
password: yourpassword,
hash_prefix: a1b2c3d4e5f6g7h8
}'

The hash_prefix is the first 16 characters shown in the token list response.


Tokens support three roles:

RolePull / ReadPush / WriteDelete / Admin
readYesNoNo
writeYesYesNo
adminYesYesYes

Default role for new tokens: read.

Terminal window
# Read-only token for monitoring/CI pull
curl -s -X POST http://localhost:4000/api/tokens \
-H Content-Type: application/json \
-d '{username:admin,password:pass,role:read,ttl_days:365,description:Monitoring}'
# Write token for CI/CD push
curl -s -X POST http://localhost:4000/api/tokens \
-H Content-Type: application/json \
-d '{username:admin,password:pass,role:write,ttl_days:90,description:GitLab CI}'
# Admin token for garbage collection and management
curl -s -X POST http://localhost:4000/api/tokens \
-H Content-Type: application/json \
-d '{username:admin,password:pass,role:admin,ttl_days:30,description:Admin ops}'

The following endpoints do not require authentication, even when auth is enabled:

EndpointReason
/healthHealth checks (load balancers, K8s probes)
/readyReadiness checks
/metricsPrometheus scraping
/v2/Docker Registry v2 version check
/ui/*Web UI
/api-docs/*Swagger documentation
/api/tokensToken creation (requires Basic Auth in body)
/api/tokens/listToken listing (requires Basic Auth in body)
/api/tokens/revokeToken revocation (requires Basic Auth in body)

VariableDefaultDescription
NORA_AUTH_ENABLEDfalseEnable authentication
NORA_AUTH_HTPASSWD_FILEusers.htpasswdPath to htpasswd file
NORA_AUTH_TOKEN_STORAGEdata/tokensDirectory for token files

config.toml:

[auth]
enabled = true
htpasswd_file = users.htpasswd
token_storage = data/tokens