Add a webhooker resetpw subcommand and a bootstrap banner (closes #208) (#239)
All checks were successful
check / check (push) Successful in 3m25s
All checks were successful
check / check (push) Successful in 3m25s
The admin bootstrap password was printed once, as one line among roughly 45 fx lines, and under docker run -d went to container logs subject to rotation. There was no reset path at all -- no subcommand, no forgot-password flow, no env override -- so recovery meant hand-deleting the users row from webhooker.db, which was documented nowhere. Adds webhooker resetpw [-generate] <username>. The password is read from stdin or generated with the existing crypto/rand helper, never taken from argv where /proc would publish it. It reuses the existing Argon2id hashing rather than reimplementing the parameters, and writes a single UPDATE only after the hash is complete, so no failure can leave an account with no usable password. An unknown username is a hard error and never creates an account. It refuses to run against a DATA_DIR held by a live instance, via the exclusive lock from #201. DATA_DIR and webhooker.db are checked to exist before the lock is acquired, so a mistyped path creates nothing -- neither a directory tree nor a stray lock file. The bootstrap password now appears exactly once, in a distinct banner written straight to a caller-named writer rather than as an fx log line.
This commit was merged in pull request #239.
This commit is contained in:
104
README.md
104
README.md
@@ -286,9 +286,84 @@ On first startup, webhooker automatically generates a cryptographically
|
||||
secure session encryption key and stores it in the database. This key
|
||||
persists across restarts — no manual key management is needed.
|
||||
|
||||
On first startup, webhooker creates an `admin` user
|
||||
with a randomly generated password and logs it to stdout. This password
|
||||
is only displayed once.
|
||||
#### The admin account
|
||||
|
||||
On first startup — a `DATA_DIR` with no accounts in it — webhooker
|
||||
creates an `admin` user with a randomly generated password and prints
|
||||
it to standard output as a ruled banner:
|
||||
|
||||
```
|
||||
========================================================================
|
||||
WEBHOOKER FIRST BOOT: an admin account has been created.
|
||||
|
||||
username: admin
|
||||
password: 3xamPl3-p4ssw0rd
|
||||
|
||||
Save this password now: it is shown only here, and only once.
|
||||
If it is lost, run `webhooker resetpw admin` on a stopped deployment.
|
||||
========================================================================
|
||||
```
|
||||
|
||||
It is a banner rather than a log line because that is the only time it
|
||||
is ever shown: as one `INFO` record it sat among the roughly 45 fx
|
||||
`PROVIDE`/`RUN`/`HOOK` lines a boot writes, and under `docker run -d`
|
||||
it is one line in a log subject to rotation. The database stores only
|
||||
its Argon2id hash. There is no second account and no forgot-password
|
||||
flow, so the banner and the reset command below are the only two ways
|
||||
in.
|
||||
|
||||
#### Recovering a lost admin password
|
||||
|
||||
`webhooker resetpw` sets an existing account's password from the
|
||||
command line:
|
||||
|
||||
```bash
|
||||
# Generate a new password and print it.
|
||||
DATA_DIR=/var/lib/webhooker webhooker resetpw -generate admin
|
||||
|
||||
# Or supply one on standard input (minimum 8 characters).
|
||||
printf '%s' "$NEW_PASSWORD" | \
|
||||
DATA_DIR=/var/lib/webhooker webhooker resetpw admin
|
||||
```
|
||||
|
||||
In a container it is the same binary, which the image sets as `CMD`
|
||||
rather than `ENTRYPOINT`, so the whole command has to be given:
|
||||
|
||||
```bash
|
||||
docker run --rm -v webhooker-data:/var/lib/webhooker \
|
||||
webhooker /app/webhooker resetpw -generate admin
|
||||
```
|
||||
|
||||
Stop the service first — with the volume still attached to a running
|
||||
container, the command refuses.
|
||||
|
||||
The password is never taken as a command-line argument: on Linux argv
|
||||
is readable through `/proc` by every account on the host for as long as
|
||||
the process lives. Standard input is echoed when it is a terminal — the
|
||||
prompt says so — so `-generate` or a pipe is preferable on a shared
|
||||
machine.
|
||||
|
||||
What it will not do:
|
||||
|
||||
- **Run against a live deployment.** It takes the same exclusive
|
||||
`DATA_DIR` lock the server does (see
|
||||
[Single-instance lock](#single-instance-lock)) and refuses while a
|
||||
running instance holds it, naming the directory and exiting non-zero.
|
||||
A running process keeps serving every session that authenticated with
|
||||
the old password, so a reset underneath it would report a change the
|
||||
service does not honour.
|
||||
- **Create anything.** A `DATA_DIR` that does not exist, or that holds
|
||||
no `webhooker.db`, is an error rather than a new empty deployment —
|
||||
a mistyped path must not be built out and then reported as a success.
|
||||
- **Create an account.** A username that does not exist is an error.
|
||||
`resetpw` changes an existing account's password and nothing else.
|
||||
|
||||
`DATA_DIR` selects the deployment exactly as it does for the server. A
|
||||
password that changes on disk takes effect at the next login; sessions
|
||||
that are already authenticated are unaffected either way.
|
||||
|
||||
Changing a password you still know needs none of this — use
|
||||
`POST /user/{username}/password` in the web UI.
|
||||
|
||||
#### What `DEBUG=true` exposes
|
||||
|
||||
@@ -325,11 +400,14 @@ What it does **not** put in the log:
|
||||
What is in the log regardless of `DEBUG`, and is not a debug-logging
|
||||
decision:
|
||||
|
||||
- **The initial `admin` password**, in the clear, once, at `INFO`, on
|
||||
the first boot that creates the account. That line is the only place
|
||||
- **The initial `admin` password**, in the clear, once, on the first
|
||||
boot that creates the account — as the banner described under
|
||||
[The admin account](#the-admin-account), written straight to standard
|
||||
output rather than through the logger. That banner is the only place
|
||||
it is ever shown; the database stores the hash. A first boot's output
|
||||
is not safe to paste anywhere until that account's password has been
|
||||
changed.
|
||||
changed. The same applies to `webhooker resetpw -generate`, which
|
||||
prints the password it generated in the same form.
|
||||
- **An authenticated operator's own configuration**, echoed back
|
||||
untruncated — webhook names, target hostnames. See the logging
|
||||
section under Security for the full list and for the per-line size
|
||||
@@ -842,7 +920,10 @@ A registered user of the webhooker service.
|
||||
Passwords are hashed with Argon2id using secure defaults (64 MB memory,
|
||||
1 iteration, 4 threads, 32-byte key, 16-byte salt). On first startup,
|
||||
an `admin` user is created with a randomly generated 16-character
|
||||
password logged to stdout.
|
||||
password printed once to stdout; `webhooker resetpw` sets it again if
|
||||
it is lost (see [The admin account](#the-admin-account)). Every one of
|
||||
those paths hashes through the same `internal/database` code, so the
|
||||
parameters cannot drift between them.
|
||||
|
||||
#### Webhook
|
||||
|
||||
@@ -1994,8 +2075,12 @@ imports. The entry point is `cmd/webhooker/main.go`.
|
||||
```
|
||||
webhooker/
|
||||
├── cmd/webhooker/
|
||||
│ └── main.go # Entry point: sets globals, locks DATA_DIR, wires fx
|
||||
│ └── main.go # Entry point: subcommand dispatch; no args locks DATA_DIR and wires fx
|
||||
├── internal/
|
||||
│ ├── banner/
|
||||
│ │ └── banner.go # Ruled block for the one credential shown in the clear
|
||||
│ ├── resetpw/
|
||||
│ │ └── resetpw.go # `webhooker resetpw`: set an account's password, stopped deployments only
|
||||
│ ├── config/
|
||||
│ │ └── config.go # Configuration loading from environment variables
|
||||
│ ├── database/
|
||||
@@ -2191,6 +2276,9 @@ check, see [The login endpoint](#the-login-endpoint).
|
||||
header. API keys are stored per-user with usage tracking
|
||||
(`last_used_at`).
|
||||
- **Metrics:** Basic authentication protecting the `/metrics` endpoint.
|
||||
- **Recovery:** `webhooker resetpw <username>` on a stopped deployment
|
||||
is the only way back into an account whose password was lost (see
|
||||
[Recovering a lost admin password](#recovering-a-lost-admin-password)).
|
||||
|
||||
### Security
|
||||
|
||||
|
||||
Reference in New Issue
Block a user