fix: unify PIN hashing across CLI, FSM, and web (closes #2) #7

Merged
alee merged 16 commits from fix/issue-2-pin-unification into main 2026-04-05 16:59:51 +00:00
Showing only changes of commit eb281ad058 - Show all commits

View File

@@ -113,8 +113,9 @@ omitted sections behave sensibly.
`.vge` files.
- `hls_dir` (default `/var/vigilar/hls`): HLS segment output.
- `log_level` (default `"INFO"`): one of DEBUG, INFO, WARNING, ERROR.
- `arm_pin_hash` (default `""`): commented out in the sample; set via
`vigilar config set-pin`.
- `arm_pin_hash` (default `""`): **deprecated.** Still parsed but
ignored at runtime. Use `[security] pin_hash` instead; run
`vigilar config set-pin` to generate the canonical hash.
### `[mqtt]`
@@ -291,11 +292,10 @@ enabled = false`, `[visitors] enabled = false`, `[highlights] enabled
- `[location] latitude`, `longitude` (default `0.0`): used for sunrise
and sunset lookups.
- `[security] pin_hash` and `recovery_passphrase_hash`: populated by
`vigilar config set-pin` (the same hash is also stored under
`[system] arm_pin_hash` on the `system` model; both fields exist
because the web UI uses `[security]` while the CLI helper prints a
`[system]` line — pick one location and stick with it).
- `[security] pin_hash` (canonical arm/disarm PIN store) and
`recovery_passphrase_hash`: both populated by
`vigilar config set-pin`. The `[system] arm_pin_hash` field is
deprecated; see the `[system]` section above.
## CLI reference
@@ -344,9 +344,9 @@ sudo -u vigilar /opt/vigilar/venv/bin/vigilar config show \
```
Dumps the parsed config as JSON with `web.password_hash`,
`system.arm_pin_hash`, and `alerts.webhook.secret` redacted. Useful
for confirming which defaults Pydantic applied for keys you did not
set.
`security.pin_hash`, `security.recovery_passphrase_hash`, and
`alerts.webhook.secret` redacted. Useful for confirming which
defaults Pydantic applied for keys you did not set.
### `vigilar config set-password`
@@ -365,10 +365,12 @@ prints a `password_hash = "salt_hex:key_hex"` line to paste into
sudo -u vigilar /opt/vigilar/venv/bin/vigilar config set-pin
```
Prompts for an arm/disarm PIN, generates a random 32-byte HMAC key,
computes `HMAC-SHA256(key, pin)`, and prints an `arm_pin_hash =
"secret_hex:mac_hex"` line to paste into `[system]`. Again, no file
write.
Prompts for an arm/disarm PIN, derives a salted PBKDF2-SHA256 hash
(600,000 iterations) via `vigilar.alerts.pin.hash_pin`, and prints a
`pin_hash = "pbkdf2_sha256$salt$dk"` line to paste into `[security]`.
Again, no file write. The same hash format is verified identically by
the web arm/disarm endpoint and by `ArmStateFSM` in the event
processor — there is one canonical PIN store.
## Secrets and security
@@ -388,9 +390,13 @@ write.
volume on integrity-verified storage (dm-integrity, ZFS with
checksums) or mirror to write-once media.
- The web UI password is a scrypt hash set by `vigilar config
set-password` and stored at `[web] password_hash`. The arm PIN is
an HMAC stored at `[system] arm_pin_hash` (and/or `[security]
pin_hash`).
set-password` and stored at `[web] password_hash`. The arm/disarm
PIN is a PBKDF2-SHA256 hash (600k iterations, salted) set by
`vigilar config set-pin` and stored at `[security] pin_hash`.
A legacy `[system] arm_pin_hash` field is still parsed but ignored
at runtime; if it's set and `[security] pin_hash` is empty, the
service logs a deprecation warning at startup and arm/disarm will
behave as if no PIN were configured until you re-run `set-pin`.
- TLS: `gen_cert.sh` uses `mkcert` if present, otherwise an `openssl`
ECDSA P-256 self-signed certificate valid for 3650 days with SANs
for `vigilar.local`, `localhost`, `127.0.0.1`, and the detected LAN
@@ -602,10 +608,6 @@ Do not expose port `49735` directly on the WAN; require the tunnel.
`[health]` for real disk policy.
- **No schema migrations.** There is no Alembic (or equivalent) in
the tree. Rollbacks rely on your backup discipline.
- **Duplicate PIN fields.** `vigilar config set-pin` writes to
`[system] arm_pin_hash`, while the web arm/disarm flow reads from
`[security] pin_hash`. Both models exist. If you set one and the
other side does not behave as expected, mirror the value manually.
## Troubleshooting