# Security hardening

> How Falak protects secrets, agents and servers, plus a production hardening checklist — registration, two-factor auth, tokens, backups and firewalls.

Source: https://falak.sh/docs/operations/security/

This page explains Falak's security model and what you should do on top of the defaults.

## What Falak does by default

### Control plane

- TLS everywhere: Let's Encrypt for the panel, HSTS (`max-age=31536000`), the agent API on its own host with **mutual TLS** against Falak's Fleet CA.
- Secrets (site variables, git and provider credentials, database passwords, DNS tokens, GitHub App keys) are stored **encrypted** with `APP_KEY` and never sent to the UI unless explicitly revealed. Reveals are audited.
- `/opt/falak/.env` is mode 600; `backups/` is mode 700.
- Containers run with `no-new-privileges`. The app trusts proxy headers only from the edge subnet (`FALAK_EDGE_SUBNET`).
- Outbound requests the control plane makes on users' behalf are guarded: alert webhooks, backup storage endpoints and template URL imports refuse private, loopback and link-local addresses by default.

### Servers

- The agent **dials out**; it listens only on loopback (OTLP) and a unix socket.
- Firewall: nftables, inbound **drop** by default; only SSH, plus 80/443 on servers that serve HTTP.
- SSH: keys only (`PasswordAuthentication no`, `PermitRootLogin prohibit-password`, `MaxAuthTries 4`), `fail2ban` running, unattended security upgrades on.
- The `falak` user has **no sudo**. Sites can run as isolated Linux users.
- Release and `shared/` directories are mode 0750 with an ACL for the edge only; other local users cannot read `.env` or `bootstrap/cache/config.php`.
- Deploys verify downloaded runtimes and agent binaries by SHA-256.
- Compose files are checked against a policy (no privileged containers, host namespaces, extra capabilities, host mounts, devices or Docker socket) unless an admin allows privileged Compose.

## Hardening checklist

| | Action |
|---|---|
| ☐ | **Restrict sign-up.** By default anyone who can reach the panel can create an account (with its own empty organization; it cannot see yours). On a public panel, set `FALAK_REGISTRATION=invite` (sign-up only through an invitation link) or `closed` (accounts only via `falak-ctl admin create`) in `/opt/falak/.env`, then `falak-ctl up`. See [Who can sign up](/docs/operations/configuration/#who-can-sign-up). |
| ☐ | Turn on **two-factor authentication** for every member (per user; not enforceable per organization yet). |
| ☐ | Give members the **lowest role** that works. `recipes.run`, `terminal.*`, `fleet.agents.manage` and `sites.compose.policy` are admin-only because they amount to root on your servers. |
| ☐ | Create **scoped API tokens** with expiry for CI and agents, never `*` tokens for automation. |
| ☐ | Schedule **encrypted backups** (`FALAK_BACKUP_PASSPHRASE`) and copy them **off the host**. The Fleet CA key and `APP_KEY` are in them. |
| ☐ | Restrict **Grafana** to operators: dashboards are not filtered per organization inside Grafana. |
| ☐ | Keep the database port closed; open it per source address only. See [Remote access](/docs/databases/remote-access/). |
| ☐ | Use your own domains in production, not shared `sslip.io` names (no cookie isolation between their users). |
| ☐ | Keep **Allow privileged compose** off unless you trust everyone who can create sites. |
| ☐ | Protect staging with basic auth or IP allow lists ([routing rules](/docs/guides/routing-rules/)). |
| ☐ | Limit SSH on servers to your IPs with a firewall rule if possible (the SSH port itself always stays open to avoid lock-out). |
| ☐ | Bind the panel to one address with `FALAK_BIND` if the host has several interfaces. |
| ☐ | Configure mail so password resets and alerts work. |
| ☐ | Watch the **Audit log** (`audit.view`). |

## Known limits

- The alert webhook SSRF guard checks the host but does not resolve DNS, so DNS rebinding is not blocked.
- Deleting an organization revokes its agents but does not destroy provider machines.
- Octane's FrankenPHP server binds its port on all interfaces; the default-drop firewall blocks it from outside.

Report security issues privately to the maintainers of [github.com/OthmanHaba/falak](https://github.com/OthmanHaba/falak) rather than in a public issue.
