Authentication¶
Eneru's API can require credentials before serving control actions and the dashboard. Authentication is opt-in: until you enable it, the API behaves exactly as it did in v5.3 — read-only, no credentials — and every write surface is hard disabled.
This page covers the auth foundation: the local user/API-key store and the CLI that manages it. The request-side enforcement (login, bearer tokens, the endpoint matrix) is documented in Observability and API.
Model at a glance¶
Think of it like /etc/passwd + /etc/shadow for the API:
- Usernames are public; passwords never are. Passwords are stored only as a one-way salted bcrypt hash. Eneru cannot show you a password back — it can only verify one.
- API keys are random tokens (
eneru_…) stored as a SHA-256 digest. The plaintext is shown once at creation and is unrecoverable afterward. - No default user, no default password. You create the first account yourself. (A shipped default credential is the classic appliance CVE.)
- One global store, a dedicated SQLite database at
/var/lib/eneru/auth.dbby default — separate from the per-UPS statistics databases. Override withapi.auth.db_pathor the CLI--auth-dbflag. - Roles exist but only
adminis enforced in v6.0. Arolecolumn is stored now so v7.0 can addoperator/viewerRBAC without a migration; until then, every authenticated principal is an admin and non-admin roles are rejected.
Transport security¶
The API is plain HTTP, so passwords and bearer tokens are sent unencrypted over
the wire. Keep api.bind on loopback and, if the API must be reachable off-host,
front it with a TLS reverse proxy — see the "Transport security" section in
observability-api.md. The daemon warns at startup on any non-loopback bind.
Requirements¶
The store needs the bcrypt package. The deb package and the container image
include it. For pip installs, request the extra:
On RPM-based distros it is a soft dependency (dnf install python3-bcrypt,
typically from EPEL). If it is missing, auth commands fail with an actionable
hint rather than a stack trace. bcrypt hashes at most the first 72 bytes of a
password; Eneru truncates to that bound deterministically, so longer passphrases
work but only their first 72 bytes are significant.
Managing users¶
The examples below use the pip/developer command
eneru …. On a package (deb/rpm) install, invoke the wrapper instead:sudo python3 /opt/ups-monitor/eneru.py user create alice ….
# Create (interactive prompt by default — asks twice and confirms a match)
eneru user create alice
# Create with a generated password (printed once)
eneru user create alice --generate
# Create non-interactively (automation): password read from stdin
printf '%s' "$PASSWORD" | eneru user create alice --password-stdin
# List, inspect (never prints the hash)
eneru user list
eneru user show alice
# Reset a password (same input options as create)
eneru user passwd alice --generate
# Delete
eneru user delete alice
There is deliberately no --password VALUE flag — a password on the command
line leaks into shell history and the process list (ps). Use the interactive
prompt, --generate, or --password-stdin.
Deleting a user or resetting their password ends their active sessions.
Dashboard sessions are held in memory with a TTL, but the API re-checks on every
request that the session's user still exists and still has the same
password_changed_at value; deleting the account or running eneru user passwd
invalidates its session token immediately, so an open dashboard is signed out on
its next poll. (A transient auth-DB error during that check keeps the existing
session rather than logging out a valid user.)
Managing API keys¶
API keys are for programmatic clients (Grafana, scripts, CI) that send
Authorization: Bearer <key>.
# Create — the key is printed once; store it now
eneru apikey create --label "Grafana read-only"
# List (metadata only, never the key or its hash)
eneru apikey list
# Revoke by id (from the list)
eneru apikey revoke 3
Configuration¶
api:
enabled: true
auth:
enabled: true # opt-in; off => read-only, writes hard-disabled
require_for_reads: false # reads stay open (Prometheus keeps scraping); writes always need a credential
session_ttl: 3600 # dashboard session lifetime, seconds
db_path: "/var/lib/eneru/auth.db"
| Key | Default | Description |
|---|---|---|
api.auth.enabled |
unset (false until an auth DB user exists) |
Turn API authentication on. If omitted, auth auto-activates once the auth DB contains a user; an explicit false keeps v5.3 read-only behavior |
api.auth.require_for_reads |
false |
When off, read endpoints stay open even with auth on; writes always require a credential |
api.auth.session_ttl |
3600 |
Dashboard session token lifetime, in seconds |
api.auth.db_path |
/var/lib/eneru/auth.db |
Location of the user/API-key store; CLI --auth-db overrides |
Auto-enable (create a user, then just sign in — no restart)¶
If the API is on but you never set api.auth.enabled, Eneru enforces auth
automatically as soon as the auth DB has at least one user. This removes a common
footgun: creating a user with eneru user create and then finding the dashboard
rejects the login because auth was silently off.
The decision is dynamic, re-evaluated per request (behind a few-second cache), so it takes effect with no restart and no config edit:
docker exec <container> eneru user create admin --generate
# within a few seconds the dashboard shows Sign-in and login works — no restart
The rule is deliberately conservative:
- An explicit
api.auth.enabled(eithertrueorfalse) always wins — setenabled: falseto keep the API open even with users present. - A fresh install with no users (and no explicit
enabled: true) stays read-only; the check never creates the auth DB as a side effect. - It does not satisfy the
nut_controlfail-closed gate: UPS control still requires an explicitapi.auth.enabled: true.
When the API is enabled but auth is off (no users, or enabled: false), the
daemon logs a one-line notice at startup and the dashboard hides its Sign-in
button — there is nothing to sign into. The dashboard learns the live auth state
from /api/v1/auth/state, which stays open even when require_for_reads gates
the read API; if you create the first user while the dashboard is open, the
Sign-in button appears on the next poll, usually within a few seconds.
Containers¶
In a container the store lives under the persisted /var/lib/eneru volume, so
users and keys survive restarts. Run the management commands inside the
container, for example: