Security Controls #
The controls that bound what a mistake or a compromise can reach. Each is summarized here with the record that decided or built it; the design itself lives in those records.
Network segmentation #
The site network is split into zones by trust — management, trusted, platform, storage, tenant transit, IoT, IoT vendor and guest ( Network Segmentation).
- Default deny between zones. The core router allows only the inter-zone flows that are declared in inventory, and removes anything it did not declare ( CHG-0007)
- Joining a network is not authorization. A device on the IoT network still needs a credential for every service it uses ( ADR-0020)
- Guest is internet-only; IoT reaches the broker and the internet, not tenant workloads or management
Tenant isolation #
- A routing domain per tenant. Tenants are separated by VRF, not by a firewall rule — there is no route between them to filter ( ADR-0001)
- Every tenant-facing service is partitioned by the platform, not by the tenant. DNS updates are bound to the tenant’s own zone by server-side key metadata; MQTT topics are prefixed by the API; log partitions are selected by the proxy from routes the API wrote. A tenant cannot claim another tenant’s identity in a payload
- Tenants hold no substrate credential — one API token, nothing else ( ADR-0015)
Encryption #
| In transit | At rest | |
|---|---|---|
| The Deevnet API | TLS, site CA | registry secrets in OpenBao |
| MQTT broker | TLS only; no plaintext listener | — |
| Log store | TLS through an authenticating proxy | on the observability VM’s disk |
| Substrate secrets | — | ansible-vault in the inventory; OpenBao for runtime secrets ( ADR-0016) |
| Build secrets (the Proxmox token for Packer and the fabric) | TLS to OpenBao | OpenBao’s runtime copy; the inventory vault is authoritative. Never on disk on the Builder |
| Terraform state store | TLS, site CA ( CHG-0030) | on one disk |
| Wi-Fi | WPA2 with a per-tenant key (PPSK) on the IoT SSID | — |
The site runs its own certificate authority; clients trust the Deevnet Root CA rather than a public CA, because nothing Deevnet serves is public. The Root CA and each Site CA are kept offline; the Substrate CA, whose key is in the inventory vault, issues every substrate certificate through Ansible, and the Tenant Device CA and the Tenant Service CA live in OpenBao ( ADR-0031, ADR-0037). The Tenant Service CA certifies names in tenants’ own zones: the Deevnet API builds each name inside the caller’s zone, and the secret store’s signing role refuses a name outside tenant space.
Credentials #
- Automation uses one account, by key.
a_autoprov, SSH key only, passwordless sudo, provisioned by the image factory. No password is written in a playbook; the passwords some devices and services need (Omada, the switch, the AP, Grafana, MinIO) are in the inventory’s vault, encrypted - Secrets are encrypted before they are committed, and a pre-commit hook refuses a plaintext vault
- A credential a change generates is encrypted, committed and pushed before its source is deleted. Losing one has cost a rebuild ( INC-0003)
- Build and deploy secrets are fetched per run, never rendered to a file. Packer and the substrate’s Terraform get their credentials from OpenBao, under a read-only identity with minute-long tokens. They go into the build’s own process environment, never onto a command line ( Build-Time Secrets, CHG-0026)
- An exposed credential is rotated, at once if a copy may have left Deevnet ( Exposed Credentials)
- Tenant credentials are issued, not chosen, and the tenant’s own state is their authoritative copy; the API keeps hashes where it can
- The long-term direction — short-lived credentials, identity held by the client — is the Secure Identity Standard