Security Hardening
This guide covers security best practices for production Control Zero Self-Managed deployments, including anti-tampering features, network isolation, least privilege configuration, and disaster recovery.
Anti-Tampering Features
Policy bundle integrity
Policy bundles are cryptographically signed and encrypted at rest. The policy engine verifies the bundle signature before every load. If verification fails, the engine enters fail-closed mode and denies all requests.
Tampering with the policy bundle on disk, in transit, or in memory triggers an immediate lockdown with an audit log entry recording the integrity violation.
Configuration integrity monitoring
Control Zero monitors its own configuration files for unexpected changes. If a configuration file is modified outside of the management API or dashboard, the system:
- Logs a warning-level event with the file path and detected change
- Continues operating with the on-disk configuration (does not auto-revert)
- Flags the configuration as "externally modified" in the dashboard
This allows you to detect unauthorized changes to gateway rules, proxy settings, or log levels.
Binary verification
Container images include embedded checksums. On startup, each service verifies its own binary integrity. If the checksum does not match, the service refuses to start and logs an error.
Network Isolation
Docker network segmentation
The default Docker Compose configuration creates an isolated bridge network for inter-service communication. No service port is exposed to the host network unless explicitly mapped.
Recommended network architecture:
Restricting exposed ports
For maximum isolation, expose only the ports your users need:
# docker-compose.yml
services:
dashboard:
ports:
- '443:443' # Expose to network
gateway:
ports:
- '8443:8443' # Expose to network
api:
expose:
- '8080' # Internal only, not mapped to host
audit-db:
expose:
- '9000' # Internal only, not mapped to host
Host firewall rules
Configure the host firewall to allow only necessary inbound traffic:
# Allow HTTPS dashboard access
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow gateway access
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
# Drop everything else from external networks
iptables -A INPUT -i eth0 -j DROP
Adjust rules based on your network topology and security requirements.
Principle of Least Privilege
Service accounts
Each Control Zero service runs as a non-root user inside its container. The default configuration:
| Service | Container User | UID |
|---|---|---|
| Gateway | cz-gateway | 1001 |
| API | cz-api | 1002 |
| Audit DB | cz-audit-db | 101 |
| SSL Proxy | cz-proxy | 1003 |
Do not run services as root. If you customize the Docker Compose configuration, preserve the user: directives.
File system permissions
Restrict access to configuration and data directories on the host:
# Configuration files: readable by the service user only
chmod 600 /opt/controlzero/config/*.yml
chmod 600 /opt/controlzero/config/controlzero.env
# Certificate private keys: most restricted
chmod 600 /opt/controlzero/config/certs/*-key.pem
# Log directory: writable by service users
chmod 750 /opt/controlzero/logs/
# Data directory: writable by service users
chmod 750 /opt/controlzero/data/
Dashboard access control
- Use strong passwords for all dashboard accounts.
- Enable role-based access: assign the minimum role needed (Viewer, Operator, Admin).
- Review the user list periodically and remove inactive accounts.
Backup and Restore
What to back up
| Component | Location | Frequency |
|---|---|---|
| Configuration | /opt/controlzero/config/ | After every change |
| Audit database | /opt/controlzero/data/audit/ | Daily |
| License key | Stored in config/controlzero.env | After every renewal |
| TLS certificates | /opt/controlzero/config/certs/ | After every renewal |
| Policy bundles | /opt/controlzero/data/policies/ | After every policy change |
Creating a backup
./scripts/backup.sh --output /path/to/backup/
# Output:
# [backup] Backing up configuration... done
# [backup] Backing up audit database... done
# [backup] Backing up policy bundles... done
# [backup] Backup complete: /path/to/backup/cz-backup-2026-04-03.tar.gz
Restoring from backup
# Stop services first
docker compose down
# Restore
./scripts/restore.sh --from /path/to/backup/cz-backup-2026-04-03.tar.gz
# Start services
docker compose up -d
# Verify
./scripts/postflight.sh
Disaster Recovery
Recovery Time Objective
With a current backup, a full restore takes approximately 15-30 minutes depending on audit database size and host performance.
Recovery procedure
- Provision a new host meeting the prerequisites
- Transfer the installation package and the latest backup to the new host
- Run the installer:
./install.sh --license-key <KEY> - Restore from backup:
./scripts/restore.sh --from <backup-file> - Run postflight checks:
./scripts/postflight.sh - Update DNS or load balancer to point to the new host
Testing disaster recovery
Schedule periodic DR drills. At minimum, verify quarterly that:
- A backup can be restored on a clean host
- All postflight checks pass after restore
- Policies are enforced correctly after restore
- Audit data from before the backup is accessible