What's public, and what needs signing in
The app is at /, each of its pages at its own address (/stats,
/applications/2), and its data under /api: those need signing in. The
rest needs nobody: signing in (/login), the health check (/healthz),
the scripts, styles, fonts and logo, and a shared calendar's link. Addresses
from when the app lived under /app (/app/companies/3) lead to the same
page without it, and the website's pages hunt once served (/docs,
/privacy, /terms, /data-deletion, /screenshots, and /ar/…) lead
to them on the website (HUNT_SITE_URL, below).
There are three ways to sign in.
A password, for one person
HUNT_PASSWORD='a long passphrase' hunt serve
docker run -e HUNT_PASSWORD='a long passphrase' -p 4848:4848 -v hunt-data:/data ghcr.io/arabesquelabs/hunt
- The app and its data ask for it, at
/login, and remember you for 30 days. The sidebar has Log out. - The CLI sends its own
HUNT_PASSWORD: set it where you runhunt. - Changing it signs everyone out. It needs 8 characters or more; a wrong one costs a second.
- Over the internet, put hunt behind HTTPS (a reverse proxy such as
Caddy, which does it on its own): the cookie is marked secure when the
proxy says the visit was over HTTPS (
X-Forwarded-Proto).
A sign-in proxy, for several people
If you already sign people in at a proxy (Authelia, Authentik, oauth2-proxy, Tailscale), it can tell hunt who's asking, in a header:
hunt serve --tenants --admins you@example.com \
--auth-header Remote-Email --trusted-proxies 172.18.0.0/16
# or HUNT_TENANTS=1 HUNT_ADMINS=… HUNT_AUTH_HEADER=… HUNT_TRUSTED_PROXIES=…
| Proxy | Its header |
|---|---|
| Authelia | Remote-Email |
| Authentik | X-authentik-email |
| oauth2-proxy | X-Auth-Request-Email (with --set-xauthrequest) |
Tailscale (tailscale serve) | Tailscale-User-Login |
--trusted-proxies(orHUNT_TRUSTED_PROXIES) lists the addresses, or ranges, the proxy's requests come from: this machine (127.0.0.1,::1) unless said, and in Docker, the proxy's network (172.18.0.0/16, say). The header counts from those alone, so nobody reaching hunt another way can claim to be anyone. Keep hunt's own port off the internet all the same.- The proxy has to set the header itself, replacing any a visitor sent: Authelia's and oauth2-proxy's usual setups do.
- People then join and are let in as with Cloudflare Access: see "Several people", below.
- The CLI, from a trusted address (on hunt's own machine, say), says
who's asking itself: set
HUNT_AUTH_HEADER(the same header) andHUNT_USER(your email) where you run it.
With Caddy and Authelia, for instance:
hunt.example.com {
forward_auth authelia:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
reverse_proxy hunt:4848
}
One person's hunt behind a sign-in proxy needs nothing from hunt: the proxy keeps everyone else out.
Cloudflare Access
Protect the whole hostname (hunt.example.com). Kubernetes' probes reach
/healthz inside the cluster, not through Access; a calendar app
subscribing to a shared calendar's link needs a bypass for /api/calendar/.
In a shared hunt, hunt also checks Access's signed token on every request
for data, so a mistake in Access's setup doesn't open anyone's data.
Behind Cloudflare Access, the sidebar has Log out (ending the Access
session), and the admin centre's Debugging info has the identity Access
holds for you and the Zero Trust dashboard, with hunt's own health check,
calendar feed, backup, source and image builds. The CLI uses a service
token: set HUNT_ACCESS_CLIENT_ID and HUNT_ACCESS_CLIENT_SECRET to its
id and secret, and HUNT_URL to the public address. The "Send to hunt"
bookmarklet opens hunt in a tab, so it signs in like any other visit.