All chapters
Security
This page says what protects a Huntress installation, what does not, and what you should do about it. It is written to be checked against.
Accounts and sign-in#
- Passwords need at least 12 characters. Five failed attempts lock the account for five minutes, and a locked account stays locked for passkeys too.
- Two-factor sign-in with an authenticator app (time-based codes) and eight one-time recovery codes. Set it up under My account → Security.
- Passkeys (WebAuthn / FIDO2) let a person sign in without a password. Register, name and delete them under My account → Security.
- Sessions. The cookie is HTTP-only, SameSite Lax, always Secure in production and slides over 8 hours.
- Registration is closed unless an invitation key is set, compared in constant time and rate limited.
- Bot trap. The sign-in and registration forms ask no question. An invisible check (a decoy field, a script-answered challenge and a signed, expiring token) runs before the password or key is looked at, so a robot cannot test credentials or lock an account out.
- Password reset is an e-mailed link and needs mail and
AppBaseUrl. Without them it logs an error and sends nothing. - Sign-in history (time, address and browser of each sign-in) is on My account. Deleting your own account needs a code sent by e-mail.
Roles and isolation#
See Concepts for what each role can do. Roles are read from the database on every check, so a change applies at once, and every action re-checks it on the server, not only in the page.
- Owners and Admins add members by name, e-mail and role. There is no invitation e-mail: Huntress generates a password that the administrator hands over, and the member should change it. A member shows as Verified once they have signed in.
- Organisations are isolated. Every query filters on the organisation, ids sent by a page are checked against it, and tokens and keys carry it. The ingest paths enforce it too: a token maps to one organisation, and rate limits are per organisation.
- Sensitive device outputs (locks, door strikes, gates, valves, sirens) need an Owner or Admin.
Secrets#
| Secret | Used for | Stored | Notes |
|---|---|---|---|
| Sensor, person and panel tokens | A device reporting | In the database | In the URL, so they appear in a device's configuration. Replace the sensor on suspicion. Shortened to eight characters in logs. |
API keys (hnt_…) | The provisioning API | Only a SHA-256 hash | Shown once. Revocable. Suspended when their creator stops being an Owner or Admin. |
| Edge secrets | An edge connecting | A hash | Compared in constant time; one edge per camera. |
| Alarm-panel AES keys | Encrypted DC-09 | In the database, unencrypted | Shown only on the panel's settings page; never logged. |
| RTSP URLs with credentials | Reading a camera | In the database, unencrypted | Sent to the edge that serves the camera. Use view-only camera accounts. |
| HTTP output header values | Calling a device | In the database, unencrypted | Shown only on the output's settings page; never logged. |
| The Zigbee network key | The Zigbee network | A file in zigbee/ | Protected by file permissions only. |
| Cookie and bot-trap signing keys | Sessions | Files in keys/ | Protected by file permissions only. |
Network exposure#
| Listener | TLS | Notes |
|---|---|---|
| Web, Hunt API, edge links | Yes, through your proxy | Use HTTPS only. HSTS (one year, including sub-domains) is sent in production. |
| MQTT 1883 | No | Token as user name, password unchecked. |
| SMTP 25 | No | No authentication and no STARTTLS. |
| SIA DC-09 2000 | No | Optional AES encryption of the payload, and a clock window as replay protection. |
| Contact ID 2001 | No | No encryption. |
Treat the four plain listeners as private: firewall them to the networks and addresses that need them, or put them behind a VPN, and turn off those you do not use. Unknown account codes and tokens are answered like known ones where that matters, and rate limits apply to the web door.
Web hardening#
- Security headers on every response:
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN,Referrer-Policy: strict-origin-when-cross-origin, a restrictivePermissions-Policyand a Content-Security-Policy withdefault-src 'self'. Because the interface is a Blazor application, the policy has to allow inline scripts and styles; it does not allow other origins for scripts or styles, and the only external origin it allows is the map tile server. - Rate limits: 300 requests per second per organisation for the Hunt API, 10 per second per address for unknown tokens, and limits on registration, sign-in and passkey endpoints and on the provisioning API.
- Rule webhooks and device calls go through an outbound policy that refuses private, loopback and link-local addresses unless you allow them, pins resolved addresses, follows no redirects and caps time and size.
- Health endpoints show only a status word to anonymous callers.
What connects out#
Huntress has no telemetry or analytics and loads no scripts, styles or fonts from anywhere else. The complete list of outbound connections:
| From | To | When | Turn off |
|---|---|---|---|
| The updater | The release channel on GitHub, over HTTPS | On the update check, if the updater is installed | Updates:Mode=Off, a local ManifestUrl, or an offline bundle |
| The server | Your SMTP server | When Notifications:Smtp is set | Leave the host empty |
| The server | Webhook and arm/disarm URLs you configure | When a rule runs them | Do not configure them |
| The server | The device addresses of HTTP outputs | When a command or status poll runs | Do not configure them |
| The viewer's browser | tile.openstreetmap.org | On a page with a map | Not drawing maps; this reveals the viewer's address and the area viewed to OpenStreetMap |
| The recipient's mail client | Google Fonts | When opening Huntress's HTML e-mails, if remote content is allowed | Use a mail client that blocks remote content |
What Huntress records#
Huntress keeps several records of what happened. It does not claim a tamper-evident audit log: these are ordinary database tables, deleted by retention and not signed or chained.
| Record | Holds | Kept |
|---|---|---|
| Raw requests | Every inbound payload with its protocol and source address, for HTTP, MQTT, SMTP, SIA and Contact ID. Cookies and authorisation headers are never stored. | Retention:EventDays; can be switched off with RawRequests:Enabled=false |
| Events | The normalised event of every reading, report and detection. | Retention:EventDays |
| Device commands | Who or which rule sent which command to which output, with arguments and the result of every step. | Retention:EventDays |
| Rule firings | When each rule fired, for what, and whether it notified. | Retention:EventDays |
| Incidents | Timeline, notes, and who or what resolved it. | Retention:ResolvedIncidentDays |
| Sign-in history | Time, address and browser of each sign-in. | Per account; not pruned by retention |
| Logs | What the program did. | 14 days |
Personal data#
- Positions, device descriptions and payloads are stored verbatim in events and in raw requests. Switch raw requests off, or shorten retention, if you do not need them.
- Object detection classifies objects only: no faces, no biometrics, no age, gender or emotion. The result is deleted with its picture.
- Deleting a sensor, place or person removes its pictures and links; incidents keep their denormalised names.
- Nothing leaves your server unless you configure it to (see above).
A hardening checklist#
- Serve the web door over HTTPS only and set
AppBaseUrlandAllowedHosts. - Firewall MQTT, SMTP, SIA and Contact ID to the networks that need them, or turn them off.
- Enable full-disk encryption and restrict the data and configuration folders to the service account.
- Set a long random
Registration:InvitationKey, or leave it empty to keep registration closed. - Turn on two-factor sign-in or a passkey for every Owner and Admin.
- Use view-only accounts on cameras and least-privilege API keys with an expiry.
- Keep clocks synchronised.
- Back up the database,
keys/,zigbee/and the configuration, and test a restore. - Set
Updates:Modedeliberately, and read the Updates page.