Homelab Hand Docs
Administration

Security model & hardening

How Homelab Hand protects credentials and sessions, and a hardening checklist.

The hub holds root-equivalent access to everything it manages: Docker on every environment (which is root on those hosts), SSH keys, Proxmox tokens and NAS credentials. Treat it like a password manager.

What's protected, and how

AssetProtection
User passwordsArgon2id hashes.
SessionsRandom 256-bit tokens, stored only as their SHA-256. HttpOnly, SameSite=Strict, Secure on HTTPS. 12-hour lifetime; sessions waiting for a 2-step code last 15 minutes.
2-step verificationTOTP (±1 step) and WebAuthn passkeys. Passwordless passkey sign-in requires user verification. Login is rate-limited (10 failures per 15 minutes per IP).
Google / Apple sign-inOnly accounts already linked to a hub user; state, nonce and PKCE; single-use 10-minute links; 2-step still required.
API tokensShown once, stored as SHA-256, scoped read/write, tied to their user. They can't manage users or tokens.
Stored credentialsSSH keys, passwords, Proxmox / UniFi / Portainer / Hawser tokens, app integration keys and DSM device tokens are encrypted (Fernet, key derived from HUB_SECRET_KEY).
Agent tokensEdge tokens stored as hashes; standard-mode tokens encrypted (the hub must present them).
SSH and NAS certificatesHost keys and self-signed certificates are pinned on first use. A change blocks the connection before any password is sent.
Web UICSP script-src 'self' (all JavaScript vendored), X-Frame-Options: DENY, nosniff. State-changing requests from other web origins are refused.
Apps gatewaySeparate ports from the hub, private addresses only, short-lived passes, hub cookies never forwarded, and a kill switch for remote access.
App integrationsDefinitions can only call paths on their own app; same-origin redirects only; 4 MB response cap.
Shell injectionContainer IDs, image refs, stack and network names are validated, and shell arguments are quoted.
AuditContainer and host actions, terminals, deploys, renames, integration actions and AI actions are logged as audit.action / ai.* events.
Configuration backupsAES-256-GCM with a scrypt-derived key from your passphrase; Google Drive access limited to the hub's own files.

Hardening checklist

  • ☐ Serve over HTTPS behind a reverse proxy or tunnel, and set HUB_ORIGIN / HUB_RP_ID.
  • ☐ Set HUB_TRUSTED_PROXIES to your proxy or tunnel connector.
  • ☐ Create the admin account immediately after install.
  • ☐ Turn on TOTP and/or a passkey for every admin.
  • ☐ Use viewer accounts and read API tokens wherever possible.
  • ☐ Back up HUB_SECRET_KEY and your backup passphrase somewhere other than the server.
  • ☐ Prefer TLS for the Docker API and Hawser Standard. Never expose tcp://:2375.
  • ☐ Use wss:// for agents across untrusted networks.
  • ☐ Keep AI watchers on observe or approve until you trust them, and leave AI may run host commands off.
  • ☐ Only enable the privileged helper where you need OS updates without SSH or the agent.
  • ☐ Use a separate, dedicated account for Synology SSH restarts.
  • ☐ Don't expose the hub to the internet without a VPN or an identity-aware proxy (e.g. Cloudflare Access) in front of it.
  • ☐ Switch External app access off when you don't need remote apps.

Known limitations

  • Passkeys need a hostname with HTTPS (or localhost), not a bare IP. They aren't available inside the mobile app.
  • Docker socket access (the local test compose and deploy.sh) is root-equivalent on that host, as with Portainer and Dockhand.
  • Synology Secure SignIn push approvals can't be answered over the API, so use an OTP method.
  • Plain-HTTP access on the LAN (http://ip:port) can't hold a Secure cookie once the hub is configured for https. Sign in through the https address.

Reverse proxies and client IPs

Set HUB_TRUSTED_PROXIES (comma-separated IPs or CIDRs) to your reverse proxy or tunnel connector. Only requests from those addresses may set X-Forwarded-For; from anyone else it's ignored. Without this, every proxied visitor looks like the proxy, so login limits and "new sign-in IP" alerts apply to the proxy as a whole.