Security Architecture¶
Authentication - Authentik OIDC¶
Orcastra uses Authentik as a centralized identity provider via the OpenID Connect (OIDC) protocol.
Flow¶
- User accesses the dashboard → redirected to Authentik login
- Authentik authenticates the user and issues an ID Token + Access Token
- Frontend stores the session via NextAuth.js
- Backend validates the token against Authentik's JWKS endpoint on every API request
Token Validation¶
The backend verifies:
- Signature - via Authentik's JWKS (JSON Web Key Set) endpoint
- Audience - must match
AUTHENTIK_CLIENT_ID - Issuer - must match
AUTHENTIK_ISSUER - Expiration - rejects expired tokens
Authorization - Role-Based Access Control¶
Three Authentik groups map to application roles:
| Authentik Group | Dashboard Role | Permissions |
|---|---|---|
orcastra-admin |
Admin | Full access: manage users, nodes, settings, view all logs |
orcastra-operator |
Operator | Operational access: manage nodes, view logs, limited settings |
orcastra-viewer |
Viewer | Read-only: view dashboards and logs |
Groups are assigned in the Authentik admin panel and passed to the application via OIDC token claims.
Secrets Management - HashiCorp Vault¶
Secret Engines¶
| Engine | Path | Purpose |
|---|---|---|
| KV v2 | secret/ |
Application secrets (API keys, credentials) |
| PKI Root CA | pki/ |
Root certificate authority |
| PKI Intermediate CA | pki_int/ |
Issues certificates for services |
Access Policy¶
The backend uses a scoped Vault token with the orcastra-dashboard policy:
# KV v2 - read/write application secrets
path "secret/data/*" { capabilities = ["create", "read", "update", "delete", "list"] }
path "secret/metadata/*" { capabilities = ["list", "read", "delete"] }
# PKI - issue and manage certificates
path "pki_int/issue/lxd" { capabilities = ["create", "update"] }
path "pki_int/certs" { capabilities = ["list"] }
path "pki_int/revoke" { capabilities = ["create", "update"] }
path "pki/cert/ca" { capabilities = ["read"] }
Encryption¶
In Transit¶
| Connection | Encryption | Notes |
|---|---|---|
| Browser → Frontend | HTTPS (via Cloudflare Tunnel) | Automatic with domain setup |
| Frontend → Backend | HTTP (internal Docker network) | Same VM, bridge network |
| Backend → Vault | HTTP | Internal LXD network |
| Backend → OpenSearch | HTTPS (self-signed) | TLS verify disabled |
| Fluent Bit → OpenSearch | HTTPS (self-signed) | TLS verify disabled |
At Rest¶
| Data | Encryption | Location |
|---|---|---|
| Vault secrets | AES-256-GCM (Vault's barrier) | VM 2 filesystem |
| PostgreSQL data | Docker volume (unencrypted) | VM 4 filesystem |
| Redis cache | Fernet symmetric encryption | VM 4 (application-level) |
| OpenSearch indices | Unencrypted at rest | VM 3 filesystem |
Production Hardening
For production environments, consider enabling:
- TLS between all internal services (Vault, OpenSearch)
- Disk-level encryption (LUKS) on all VMs
- OpenSearch encryption at rest plugin
Audit Logging¶
Vault Audit¶
Vault's file audit device logs every API operation:
Logs are:
- Rotated via
logrotate(daily, 90 days retention) - Forwarded to OpenSearch via Fluent Bit on VM 2
- Indexed as
vault-audit-*with 1-year ISM policy
Application Audit¶
The backend generates structured audit logs for:
- User authentication events (login, logout, token refresh)
- RBAC operations (role changes, permission checks)
- Data modifications (CRUD operations on nodes, secrets)
- Administrative actions (settings changes, user management)
These are indexed in OpenSearch as orcastra-audit-* with a 3-year retention ISM policy.
Network Security¶
Minimal Attack Surface¶
- No public-facing ports when using Cloudflare Tunnel
- Internal services (PostgreSQL, Redis) are not exposed outside Docker networks
- Vault listens on
0.0.0.0:8200but only accessible within LXD network
Container Security¶
The Docker Compose configuration includes:
security_opt:
- no-new-privileges:true # Prevent privilege escalation
tmpfs:
- /tmp:mode=1777,size=100m # Limit temp filesystem
Found an issue or have a suggestion? Open an issue on GitHub →