Hardening Guide
This is a checklist for running dockmesh in production or any multi-user environment. Work through it once at setup, then revisit quarterly.
Service account
Section titled “Service account”The installer creates a dedicated system user dockmesh and runs the service as that user, with docker-group membership so the daemon socket is reachable. An exploit in the HTTP or agent handlers therefore lands on a non-root account, not on uid 0.
curl … | sudo bash + the first-boot Setup Wizard sets this up automatically. Older root-owned installs are migrated to the non-root layout the next time the installer runs.
To verify:
systemctl show -p User --value dockmesh# → dockmesh (good)# → root (pre-migration — re-run the installer)
id dockmesh# → uid=N(dockmesh) gid=N(dockmesh) groups=N(dockmesh),M(docker)Docker-group membership ≈ root-equivalent on the host. A user in
dockercan launch a privileged container that mounts/and escapes. This is unavoidable when the service has to talk to the Docker daemon. Don’t treat thedockmeshaccount as a low-trust user — treat it as “root for this one narrow purpose”.
Server binary
Section titled “Server binary”Permissions
Section titled “Permissions”The dockmesh binary stays root-owned (it’s not a user program):
chown root:root /usr/local/bin/dockmeshchmod 755 /usr/local/bin/dockmeshThe data directory (DOCKMESH_DB_PATH parent) should be dockmesh-owned with 700 permissions:
chown -R dockmesh:dockmesh /var/lib/dockmeshchmod 700 /var/lib/dockmeshchmod 600 /var/lib/dockmesh/data/*.dbThe SQLite database contains the CA private key, session tokens, and encrypted secrets. Do not let it be world-readable.
Systemd hardening
Section titled “Systemd hardening”The unit the installer writes already includes:
[Service]User=dockmeshGroup=dockerNoNewPrivileges=trueProtectSystem=strictProtectHome=truePrivateTmp=trueProtectKernelTunables=trueProtectKernelModules=trueProtectControlGroups=trueReadWritePaths=/var/lib/dockmesh /var/run/docker.sockRestrictNamespaces=trueLockPersonality=trueOlder units can be upgraded by re-running curl -fsSL https://get.dockmesh.dev | sudo bash — the installer patches the unit in-place and restarts the service.
User accounts
Section titled “User accounts”Set a strong admin password during the Setup Wizard — there is no shipped admin/admin default. Better still: configure SSO and delete the local admin once SSO works.
Enforce 2FA for the admin role under Authentication → Sessions & sign-in flow → Require 2FA for admin role. The setting persists as auth.require_tfa_for_admin and applies from the next sign-in.
For the web UI
Section titled “For the web UI”Enable the embedded Caddy reverse proxy (see Reverse Proxy) and bind dockmesh to 127.0.0.1:8080. Caddy terminates TLS on 443.
If you use an external load balancer (Cloudflare, AWS ALB, nginx), set DOCKMESH_HTTP_ADDR=127.0.0.1:8080 so dockmesh never listens on a public IP directly.
For agents
Section titled “For agents”The agent protocol is mTLS by default and cannot be disabled. Rotate the CA if you suspect compromise:
# Snapshot the DB first — the CA private key lives inside itsudo install -m 0600 -o dockmesh -g dockmesh /var/lib/dockmesh/data/dockmesh.db /var/lib/dockmesh/data/dockmesh.db.bak
# Rotatesudo dockmesh ca rotate --reissue-all-agentsAll agents re-enroll on next connect. Old certs are revoked.
Network
Section titled “Network”Firewall
Section titled “Firewall”On the server host:
| Port | Direction | Source | Purpose |
|---|---|---|---|
| 443 | in | Public (if UI is internet-facing) or VPN subnet | HTTPS UI |
| 8443 | in | Agent IPs only | Agent mTLS |
| 80 | in | Public | ACME challenge (only if using auto-TLS) |
| 22 | in | Admin IPs only | SSH |
Block everything else. Example ufw:
ufw default deny incomingufw allow from <VPN-subnet> to any port 443ufw allow from <agent-subnet> to any port 8443ufw allow from <admin-ip> to any port 22ufw enableAgent hosts
Section titled “Agent hosts”Agents make outbound-only connections. No inbound ports needed. If you have an inbound firewall rule for them, remove it.
Secrets
Section titled “Secrets”Don’t commit to Git
Section titled “Don’t commit to Git”If stacks are Git-backed, don’t commit plaintext passwords. Options:
- SOPS with age encryption — files are encrypted in Git, decrypted at deploy time
- Docker native secrets with an external store (Vault, AWS Secrets Manager)
- dockmesh env var secrets — encrypted at rest in the dockmesh DB, never in Git
Rotation
Section titled “Rotation”Rotate these on a schedule:
| Secret | Real lifetime | Notes |
|---|---|---|
| Agent mTLS certs | 1 year, manual rotation | No auto-renewal yet — drift visible in the Hosts page; re-enrol the agent before expiry. Auto-renewal is on the roadmap. |
| API tokens | Operator-defined expiry per token | No org-wide rotation forcer today — set realistic TTLs at creation. |
| Refresh tokens | Configurable in Authentication → Sessions (default 24h absolute, 60min idle) | Access tokens are 15min JWTs auto-refreshed by the UI / dmctl. |
| SSO client secrets | Per-IdP policy | Update the value in the provider record under Authentication. |
| SMTP password | On staff change | Notification-channel credentials are encrypted at rest with the same age key as stack .env. |
Backups
Section titled “Backups”Encrypt everything
Section titled “Encrypt everything”Use age-encrypted backups (Backup docs). Store the passphrase in a password manager separate from the dockmesh instance.
Test restores
Section titled “Test restores”A backup you haven’t restored isn’t a backup. Schedule a quarterly test restore to a scratch host.
Off-site
Section titled “Off-site”At least one backup target should be off the same host (and ideally off the same provider). Local backup + S3/B2 is the common pattern.
Agent enrollment
Section titled “Agent enrollment”Enrollment tokens are powerful. Treat them like root SSH keys:
- One-time use (dockmesh enforces this)
- Transmit over encrypted channel (SSH, 1Password shared vault, Signal)
- Never commit to Git or pipeline config
- Rotate the server’s enrollment-signing key annually
Container runtime
Section titled “Container runtime”dockmesh doesn’t replace Docker security best practices:
- Run containers as non-root where possible (
USERin Dockerfile) - Drop capabilities (
cap_drop: [ALL], add only what’s needed) - Use read-only root filesystem (
read_only: true+ explicit write volumes) - Set resource limits (
cpus,mem_limit) on every container - Use
no-new-privileges: truein compose
The image-scanner in dockmesh catches known CVEs but not runtime misconfigurations. Separate runtime scanner (Falco, Tracee) for that.
Audit and monitoring
Section titled “Audit and monitoring”- Enable the Audit Log webhook to ship events to your SIEM
- Alert on: failed SSO attempts, mTLS handshake failures, audit chain breaks
- Review the audit log weekly for unexpected admin actions
Periodic reviews
Section titled “Periodic reviews”Quarterly:
- Who has Admin? Why?
- Which API tokens exist? Who owns each? Still needed?
- Expired or unused role assignments
- Hosts that haven’t connected in 30 days (stale agents)
- Stacks running on EOL’d base images
See also
Section titled “See also”- Upgrade Guide — safe upgrade procedure
- Disaster Recovery — what to do when things go wrong
- Audit Log — monitoring admin actions