600 lines
30 KiB
Markdown
600 lines
30 KiB
Markdown
# Dockwatch v9.6.0
|
||
|
||
> Go module: `git.send.nrw/sendnrw/dockwatch`
|
||
|
||
Dockwatch is a single-binary Go control plane for Docker Compose, Docker resources and uptime monitoring. It combines Dockge/Dockhand-style operational workflows with Uptime-Kuma-style monitoring while keeping its own implementation and UI.
|
||
|
||
The same binary runs as `standalone`, `master` or `agent`. SQLite uses `modernc.org/sqlite`, so the application itself builds with `CGO_ENABLED=0`.
|
||
|
||
## v9.6.0 System-Updates
|
||
|
||
- neuer Admin-Bereich **System-Updates** für den lokalen Host und ausgewählte Master/Agent-Nodes
|
||
- Update-Erkennung und Installation über apt, dnf/yum, pacman sowie weiterhin zypper/apk als bereits unterstützte Paketmanagerfamilien
|
||
- Arch-Abfragen nutzen bevorzugt `checkupdates` aus `pacman-contrib`, damit keine isolierte `pacman -Sy`-Synchronisierung und damit kein Partial-Upgrade-Risiko entsteht
|
||
- Arch-Systemupdates werden ausschließlich als vollständiges `pacman -Syu --noconfirm` ausgeführt
|
||
- AUR-Unterstützung erkennt `paru` (bevorzugt) oder `yay`; `HOST_AUR_USER` legt den Nicht-root-Benutzer fest, unter dem AUR-Abfrage/Build läuft
|
||
- AUR-Installation fordert die Paket-Eskalation des Helpers non-interaktiv an (`--sudoflags=-n`); Hosts müssen diesen Benutzer entsprechend für den Paketmanager autorisieren
|
||
- neue Master/Agent-Endpunkte für Update-Liste, Paketmetadaten-Refresh und Installation; alle Mutationen bleiben Admin-only und laufen durch Origin-Schutz/Audit-Logging
|
||
- Browser kann weiterhin keine freien Paketnamen oder Shell-Kommandos einreichen, sondern nur die vordefinierten Scopes `system`, `aur` oder `all`
|
||
- statische Assets verwenden `v=9.6.0` zur sicheren Cache-Aktualisierung
|
||
|
||
## v9.5.3 deutsche Weboberfläche
|
||
|
||
- die Weboberfläche ist nun durchgängig deutsch beschriftet; interne API-/Compose-Werte bleiben unverändert
|
||
- Navigation, Compose-Designer, Monitoring, Dienste, Statusseiten, Docker-Ressourcen, Git, Benachrichtigungen und Host-Sicherheit wurden sprachlich vereinheitlicht
|
||
- Status- und Rollenwerte werden im UI deutsch dargestellt, ohne die API-Werte (`up`, `down`, `admin`, `operator` usw.) zu verändern
|
||
- die öffentliche Statusseite ist ebenfalls deutsch
|
||
- häufige serverseitige Diagnosemeldungen werden für die Anzeige im WebUI verständlich ins Deutsche übertragen
|
||
- statische Assets verwenden `v=9.5.3` zur sicheren Cache-Aktualisierung
|
||
|
||
|
||
## v9.5.1 UI / Compose editor fixes
|
||
|
||
- Compose scalar edits no longer re-render the complete visual tree after every keystroke, so text/port fields keep focus while typing. Structural changes (add/remove/type changes) still re-parse the YAML AST.
|
||
- Compose designer controls now use the application theme instead of browser-default white form controls in dark mode.
|
||
- Stack container cards format Compose publisher JSON into readable port mappings and wrap long image/command values safely.
|
||
- Docker inventory tables now have horizontal overflow handling, sensible column widths, compact label summaries, and a sticky action column so buttons are no longer squeezed off-screen.
|
||
- Static assets are cache-busted to `v9.5.1`.
|
||
|
||
|
||
## v9.5 firewall-provider layer
|
||
|
||
- Host Security firewall management now uses a provider model: **Auto detect / UFW / firewalld / native nftables**
|
||
- Auto detect adopts an already-active UFW or firewalld frontend instead of refusing all changes simply because one exists
|
||
- multiple truly competing active frontends are still treated as a hard conflict
|
||
- UFW rules are added with deterministic `dockwatch:` comments and Dockwatch removes only those managed rules; it never runs `ufw reset`
|
||
- firewalld uses the selected native zone and managed rich rules while preserving unrelated services, ports, sources and rich rules
|
||
- native nftables keeps the isolated `table inet dockwatch` model and never flushes the global ruleset
|
||
- existing provider state/rules are displayed read-only in the firewall editor before changes are applied
|
||
- provider selection, default inbound management, firewalld zone selection, ALLOW/DENY/REJECT/LIMIT rules and rule reordering are available in the unified UI
|
||
- provider changes retain the timed rollback/explicit commit safety model; rollback restores the previous Dockwatch-managed policy and provider default snapshot where applicable
|
||
- package installation/upgrade now installs the selected firewall frontend (`ufw`, `firewalld` or `nftables`) rather than always installing nftables
|
||
|
||
## v9.4.1 fixes
|
||
|
||
- fixed monitor creation when no monitor is selected (`state.monitor == null`)
|
||
- static UI assets are served with `Cache-Control: no-store`, preventing stale frontend code after upgrades
|
||
- Compose visual-editor patches now use a real queue so rapid edits on different fields are not lost
|
||
- the **Volumes** section now reconciles every service mount with top-level `volumes:` declarations
|
||
- named volumes missing a top-level declaration can be added individually or with **Add missing declarations**
|
||
- bind mounts, anonymous mounts and dynamic mounts are shown in the reconciliation view but are never silently converted into named volumes
|
||
- unused top-level volume declarations are highlighted and can be removed explicitly without deleting the Docker volume itself
|
||
|
||
## What is included
|
||
|
||
### Compose / stack management
|
||
|
||
- multiple Compose stacks per environment
|
||
- local Docker plus remote agents
|
||
- staged `docker compose config --quiet` validation before a save
|
||
- atomic managed-file writes and rollback on save failure
|
||
- full bidirectional Compose designer backed by a YAML AST
|
||
- recursive editing of all YAML maps/arrays/scalars, including unknown/future Compose fields
|
||
- `compose.yaml`, root `.env`, `envs/`, `secrets/` and `configs/`
|
||
- Up / Start / Stop / Restart / Down / Pull / Update / Force recreate
|
||
- service/container status
|
||
- live SSE logs with pause, follow, filter and download
|
||
- interactive PTY/WebSocket `docker compose exec` terminal
|
||
- normalized service/dependency/network/volume graph from `docker compose config --format json`
|
||
- registry digest based image-update checks
|
||
- safe stack deletion: only Dockwatch-managed definition files are removed by default
|
||
- explicit, typed-name confirmation for full stack-folder purge
|
||
|
||
The editor keeps unsaved drafts in browser storage. Background refresh never overwrites a dirty editor.
|
||
|
||
### Git-backed stacks
|
||
|
||
Git sources support:
|
||
|
||
- local or remote-agent environment
|
||
- repository, branch, workdir and Compose filename
|
||
- manual sync
|
||
- optional deploy after sync
|
||
- signed webhooks (`X-Hub-Signature-256`, `X-Gitlab-Token` or `X-Webhook-Token`)
|
||
- encrypted webhook secret at rest
|
||
- last commit / last sync / last error
|
||
|
||
A Git sync is now **managed-file based**. Dockwatch stores a small `.dockwatch/git-manifest.json` in the stack directory and only replaces/removes files that belong to the Git checkout. Unrelated runtime or bind-mount data beside the Compose project is preserved. Syncs of the same source are serialized and a failed apply rolls back the touched file set.
|
||
|
||
Symlink stack destinations and symlink paths inside Git-managed writes are rejected.
|
||
|
||
### Docker resources
|
||
|
||
**Containers**
|
||
|
||
- list/filter
|
||
- start / stop / restart / force-remove
|
||
- operator-only inspect
|
||
- one-shot CPU/memory/network/block stats
|
||
|
||
### Container identity / host UID-GID checks
|
||
|
||
Container rows include an **Identity** action. Dockwatch inspects the selected container and reports:
|
||
|
||
- configured Compose/image user and the effective runtime UID/GID where resolvable
|
||
- whether the process currently runs as UID 0
|
||
- a conservative root assessment based on `privileged`, Docker socket mounts, passed-through devices and added Linux capabilities
|
||
- bind-mount sources and their host UID/GID ownership when host access is configured
|
||
- single-container detail checks plus a throttled **Identity audit** across all containers in the selected environment
|
||
- whether the container UID/GID already maps to a local host account/group
|
||
|
||
Dockwatch deliberately does **not** claim that a root container can always be converted to non-root. Application-internal filesystem permissions, entrypoints and image-specific `PUID`/`PGID` conventions cannot be proven from Docker metadata alone. It also never rewrites Compose `user:` automatically.
|
||
|
||
A matching local host username is **not required by Docker**. Linux file ownership is numeric; creating a host account can nevertheless make bind-mount ownership, backups and administration easier.
|
||
|
||
Host inspection is opt-in. For read-only inspection, set:
|
||
|
||
```env
|
||
HOST_ROOT=/host
|
||
ALLOW_HOST_USER_MANAGEMENT=false
|
||
ALLOW_HOST_PERMISSION_MANAGEMENT=false
|
||
```
|
||
|
||
and mount the host root read-only at `/host`. `examples/compose-host-identity.override.yml` shows this setup.
|
||
|
||
Admins may optionally create a locked/non-login local host account using the container's server-side re-resolved UID/GID. This requires both a writable host-root mount and:
|
||
|
||
```env
|
||
ALLOW_HOST_USER_MANAGEMENT=true
|
||
```
|
||
|
||
See `examples/compose-host-user-management.override.yml`. The browser cannot supply an arbitrary UID/GID: Dockwatch re-inspects the container immediately before the change and derives the IDs itself. Existing numeric users/groups are reused and UID/name collisions are refused. Docker rootless/user-namespace remapping is detected where possible; automatic same-numbered host-account creation is refused when IDs are remapped. This operation is intentionally admin-only and disabled by default.
|
||
|
||
### Identity & Bind Mount Permissions
|
||
|
||
Dockwatch can now diagnose the actual bind-mount permission problem instead of stopping at "container runs as UID X". The container **Identity** dialog and each stack's **Permissions** tab show, per bind mount:
|
||
|
||
- effective PID 1 UID/GID and a separate expected **bind UID/GID**
|
||
- `PUID/PGID` or `USER_ID/GROUP_ID` when the image exposes those paired conventions
|
||
- host owner UID/GID and POSIX mode bits
|
||
- static writeability (`w+x` for directories, `w` for files)
|
||
- extended POSIX ACL detection when `getfacl` is available
|
||
- an optional non-mutating runtime `test -w` using the expected numeric identity
|
||
- read-only mounts, rootless/userns remapping and unsafe symlinked host paths as hard repair blockers
|
||
|
||
The repair flow is **Analyze → Preview → Repair → Verify**. For recursive ownership repair Dockwatch scans the tree first and shows how many files/directories differ. Automatic recursive repair is refused above 200,000 entries. Symlinks are never followed or chowned. `:ro` mounts are never repaired automatically.
|
||
|
||
Permission repair is a separate high-trust opt-in from host-user creation:
|
||
|
||
```env
|
||
HOST_ROOT=/host
|
||
ALLOW_HOST_PERMISSION_MANAGEMENT=true
|
||
```
|
||
|
||
and `/` must intentionally be mounted read-write at `/host`. See `examples/compose-host-permissions.override.yml`. The browser supplies only the container and its mount destination (for example `/config`); the backend re-reads `docker inspect` and resolves the real host source itself. Arbitrary host paths and arbitrary UID/GID values cannot be submitted for repair.
|
||
|
||
Ownership repair can operate on only the bind root or recursively. `chmod` is separate, optional, explicit, and only applies to the bind root; Dockwatch never automatically applies `chmod 777` and never recursively rewrites modes. After a repair Dockwatch re-runs the ownership/writeability analysis and reports the result.
|
||
|
||
**Images**
|
||
|
||
- list/filter
|
||
- pull / remove / prune
|
||
- registry login/logout
|
||
- persistent Docker auth via `DOCKER_CONFIG=/data/docker-config`
|
||
|
||
**Volumes**
|
||
|
||
- list/filter
|
||
- create / remove / prune
|
||
- driver and labels
|
||
|
||
**Networks**
|
||
|
||
- list/filter
|
||
- create / remove / prune
|
||
- driver, labels, `internal`, `attachable`
|
||
|
||
Docker positional arguments and Compose service names are validated before invoking the CLI so option-like values cannot be interpreted as Docker CLI flags.
|
||
|
||
### Host Security layer
|
||
|
||
Dockwatch can optionally act as a host-security control plane for the selected local or remote-agent environment. The feature is **disabled by default** and uses three independent capabilities so that audit, configuration changes and package management can be granted separately.
|
||
|
||
The **System → Host Security** page provides a posture overview and managed workflows for:
|
||
|
||
**Firewall (Auto / UFW / firewalld / nftables)**
|
||
|
||
- **Auto detect** adopts the already-active host firewall frontend: UFW first when it is the sole active frontend, firewalld when it is the sole active frontend, otherwise native nftables is used when available
|
||
- an explicit provider can be selected when you intentionally want to migrate or standardize a host
|
||
- only genuinely competing active frontends are treated as conflicts; for example UFW + firewalld, or UFW + an already-loaded Dockwatch nftables table
|
||
- the editor shows installed/active frontends plus the provider's existing runtime rules/state before any mutation
|
||
- common managed policy supports trusted IPv4/IPv6 CIDRs and ordered TCP/UDP **ALLOW / DENY / REJECT / LIMIT** rules
|
||
- provider-global default inbound management is an explicit opt-in for UFW/firewalld; nftables keeps its default policy inside Dockwatch's own table
|
||
- **UFW:** Dockwatch uses native `ufw` commands, tags its rules with deterministic `dockwatch:` comments and removes only those tagged rules; it never runs `ufw reset` and preserves foreign UFW rules
|
||
- **firewalld:** Dockwatch targets a selected zone, uses native rich rules, and preserves unrelated zone services/ports/sources/rich rules; default zone target changes are opt-in
|
||
- **nftables:** Dockwatch continues to own only `table inet dockwatch`, validates with `nft -c`, never runs `flush ruleset`, and does not alter Docker NAT/FORWARD chains
|
||
- when an enabled policy is applied to an inactive UFW/firewalld provider, Dockwatch activates the selected frontend inside the rollback window; rollback restores the previous active state
|
||
- **timed rollback** (30–600 seconds, UI default 90 seconds) after apply; changes must be explicitly kept after management connectivity is verified
|
||
- package installation/upgrade uses the selected provider package instead of always installing nftables
|
||
- browser clients never submit arbitrary firewall command text; the backend renders and executes only the typed policy model
|
||
|
||
**Fail2Ban**
|
||
|
||
- package/runtime/boot status
|
||
- package installation/upgrade when separately enabled
|
||
- managed global ban/find/max-retry/backend settings
|
||
- ignore IP/CIDR list
|
||
- configurable jails with filter, port, backend, log path and per-jail retry threshold
|
||
- server-side `fail2ban-client -t` validation before activation/reload
|
||
- Dockwatch owns only `/etc/fail2ban/jail.d/dockwatch.local`; distro and administrator configuration is preserved
|
||
|
||
**Linux Audit (auditd)**
|
||
|
||
- package/runtime/boot status
|
||
- managed watches for identity files, sudoers, SSH configuration, Docker socket/configuration, systemd units and kernel-module configuration
|
||
- additional explicitly configured file watches
|
||
- path/key/permission validation
|
||
- `augenrules --check` validation and `augenrules --load` activation
|
||
- Dockwatch owns only `/etc/audit/rules.d/90-dockwatch.rules`; unrelated audit rules remain untouched
|
||
|
||
**Installation and maintenance**
|
||
|
||
- install or upgrade the selected firewall provider (`ufw`, `firewalld` or `nftables`), `fail2ban` and `auditd`/`audit` using the detected host package manager
|
||
- supported package-manager families: apt, dnf, yum, zypper, apk and pacman
|
||
- enable, disable, restart and (where meaningful) reload the corresponding host services
|
||
- current package/service/config-drift information in the UI
|
||
- backups of Dockwatch-managed host configuration before replacement
|
||
- all mutation endpoints are administrator-only and flow through Dockwatch's existing origin/CSRF guard and activity audit log
|
||
|
||
Dockwatch intentionally manages its own drop-in/configuration scope rather than rewriting the host's entire security configuration. It is a focused administration layer, not a replacement for a complete CIS/STIG benchmark, SELinux/AppArmor policy management, an enterprise EDR, or distro-specific security tooling.
|
||
|
||
#### Capability modes
|
||
|
||
Read-only file/configuration audit:
|
||
|
||
```yaml
|
||
services:
|
||
dockwatch:
|
||
environment:
|
||
HOST_ROOT: /host
|
||
HOST_SECURITY_ENABLED: "true"
|
||
ALLOW_HOST_SECURITY_CHANGES: "false"
|
||
ALLOW_HOST_PACKAGE_MANAGEMENT: "false"
|
||
volumes:
|
||
- /:/host:ro
|
||
```
|
||
|
||
A ready-to-use example is `examples/compose-host-security-audit.override.yml`. In this mode Dockwatch cannot mutate the host. Runtime/service checks that require entering the host namespaces may be unavailable and are reported as such.
|
||
|
||
Full host-security management is intentionally high privilege:
|
||
|
||
```yaml
|
||
services:
|
||
dockwatch:
|
||
pid: host
|
||
privileged: true
|
||
environment:
|
||
HOST_ROOT: /host
|
||
HOST_SECURITY_ENABLED: "true"
|
||
ALLOW_HOST_SECURITY_CHANGES: "true"
|
||
ALLOW_HOST_PACKAGE_MANAGEMENT: "true"
|
||
# Arch/AUR optional, e.g. deploy
|
||
HOST_AUR_USER: ""
|
||
HOST_SECURITY_HOST_PID: "1"
|
||
volumes:
|
||
- /:/host:rw
|
||
```
|
||
|
||
See `examples/compose-host-security.override.yml`. Dockwatch verifies that `HOST_ROOT` and `/proc/<HOST_SECURITY_HOST_PID>/root` refer to the same host before enabling its namespace executor. Host commands are executed through `nsenter` in the target host namespaces; the browser can select only predefined operations and cannot submit arbitrary shell commands.
|
||
|
||
Because this mode grants Dockwatch broad host-administration capability, use OIDC, restrict the `admin` role carefully, protect the Dockwatch database/`APP_SECRET`, and do not expose an agent token or the UI to untrusted networks.
|
||
|
||
The same security APIs are available through the existing master/agent relay, so each agent can audit/manage **its own host** when that agent has been configured with the appropriate host-security capability. A master does not silently inherit host privileges on an agent.
|
||
|
||
#### System package updates
|
||
|
||
The **System-Updates** page uses the same selected environment as Docker/Host Security. Read-only update discovery requires the host-security namespace executor. Installing updates additionally requires `ALLOW_HOST_SECURITY_CHANGES=true` and `ALLOW_HOST_PACKAGE_MANAGEMENT=true`.
|
||
|
||
Supported update strategies:
|
||
|
||
- **apt**: simulate `dist-upgrade` for discovery; refresh with `apt-get update`; install with non-interactive `apt-get dist-upgrade -y --no-remove`
|
||
- **dnf**: `check-update`; install with `dnf upgrade --refresh -y`
|
||
- **yum**: `check-update`; install with `yum update -y`
|
||
- **pacman**: prefer `checkupdates` for safe discovery; install official packages only through full `pacman -Syu --noconfirm`
|
||
- **AUR**: detect `paru` or `yay`, query with `-Qua`, update AUR-only with `-Sua`, or combine repository+AUR updates with `-Syu`
|
||
- **zypper/apk** remain supported because the Host Security package installer already supports those families
|
||
|
||
For AUR, configure a real non-root host account:
|
||
|
||
```yaml
|
||
environment:
|
||
HOST_AUR_USER: deploy
|
||
```
|
||
|
||
That user must be able to run the helper and perform its package-manager escalation without an interactive password prompt. Dockwatch passes `--sudoflags=-n`; it never stores or submits a sudo password.
|
||
|
||
### Monitoring
|
||
|
||
Probe types:
|
||
|
||
- HTTP(S)
|
||
- TCP
|
||
- DNS
|
||
- Docker container running state
|
||
- Docker container running + optional `healthy` requirement
|
||
|
||
Monitor lifecycle:
|
||
|
||
- create / edit / delete
|
||
- pause / resume
|
||
- manual or timed maintenance
|
||
- maintenance notes
|
||
- manual **Check now**
|
||
- interval and timeout
|
||
- HTTP method and accepted status range
|
||
- request headers/body
|
||
- keyword / inverted keyword assertion
|
||
- optional HTTP TLS verification bypass
|
||
- local or remote-agent execution
|
||
- heartbeat history
|
||
- last latency/status message
|
||
- 24-hour uptime
|
||
- retention cleanup
|
||
|
||
The scheduler uses a lightweight monitor query and does not recompute 24-hour uptime statistics every two seconds. Manual checks are serialized with scheduled checks and do not accidentally resume paused/maintenance monitors. Shutdown-cancelled checks are not persisted as false outages.
|
||
|
||
### Services and public status pages
|
||
|
||
Probes can be grouped into user-facing Services. Aggregation is strict:
|
||
|
||
- any probe `down` -> Service `down`
|
||
- otherwise maintenance -> Service `maintenance`
|
||
- pending/paused states are retained
|
||
- otherwise Service `up`
|
||
|
||
Service dialogs can assign/unassign probes directly.
|
||
|
||
Admins can publish selected Services on public pages:
|
||
|
||
```text
|
||
/status/<slug>
|
||
/public/api/status/<slug>
|
||
```
|
||
|
||
The public JSON API uses a dedicated DTO and exposes only public status information (name, status, uptime, latency and last-check timestamp), not targets, node IDs, request headers, bodies or credentials. HTML/JSON status responses are not cached.
|
||
|
||
### Notifications
|
||
|
||
Providers:
|
||
|
||
- generic JSON webhook
|
||
- ntfy
|
||
- Gotify
|
||
- SMTP
|
||
|
||
SMTP supports:
|
||
|
||
- `STARTTLS`
|
||
- implicit `SSL/TLS`
|
||
- `None`
|
||
- authentication on/off independently of TLS
|
||
- optional certificate-verification bypass for private/self-signed infrastructure
|
||
|
||
SMTP connections inherit the notification deadline after connect as well, so a stalled server cannot leave a delivery goroutine hanging indefinitely. SMTP/password/token/secret values are AES-GCM encrypted at rest. Notification configuration is admin-only in both API and UI.
|
||
|
||
### PocketID / OIDC and roles
|
||
|
||
Roles are mapped from OIDC/PocketID groups:
|
||
|
||
- `viewer` – dashboards/status/inventory, sanitized monitor configuration
|
||
- `operator` – stack configuration/logs/terminal, Docker actions, monitor/service operations, Git stacks
|
||
- `admin` – environments, notifications, status-page administration and audit log
|
||
|
||
Viewer API responses do not expose monitor request headers/body/keywords. Stack definition detail and live logs require Operator access because `.env`, secrets and runtime logs may contain sensitive data.
|
||
|
||
Required OIDC setup:
|
||
|
||
```env
|
||
AUTH_DISABLED=false
|
||
BASE_URL=https://dockwatch.example.com
|
||
OIDC_ISSUER=https://id.example.com
|
||
OIDC_CLIENT_ID=...
|
||
OIDC_CLIENT_SECRET=...
|
||
APP_SECRET=replace-with-at-least-32-random-characters
|
||
OIDC_ADMIN_GROUP=dockwatch-admins
|
||
OIDC_OPERATOR_GROUP=dockwatch-operators
|
||
```
|
||
|
||
Redirect URI:
|
||
|
||
```text
|
||
https://dockwatch.example.com/auth/callback
|
||
```
|
||
|
||
OIDC discovery, token exchange and verification are bounded by `HTTP_TIMEOUT_SECONDS`. Session/state cookies are `HttpOnly`, `SameSite=Lax` and become `Secure` when `BASE_URL` uses HTTPS.
|
||
|
||
### Master / agent
|
||
|
||
Modes:
|
||
|
||
```text
|
||
APP_MODE=standalone
|
||
APP_MODE=master
|
||
APP_MODE=agent
|
||
```
|
||
|
||
An agent exposes only `/agent/v1/*` and requires an `AGENT_TOKEN` of at least 24 characters. Agent tokens are AES-GCM encrypted in the master's SQLite database.
|
||
|
||
Remote-capable features include:
|
||
|
||
- stacks and actions
|
||
- logs and PTY terminal
|
||
- Compose graph
|
||
- image update checks
|
||
- containers/images/networks/volumes
|
||
- container identity analysis, bind-mount permission repair and optional host-account creation on the agent host
|
||
- monitoring probes
|
||
- Git clone/sync/deploy
|
||
- host-security audit/management when explicitly enabled on that agent
|
||
|
||
See `examples/compose-master.yml` and `examples/compose-agent.yml`.
|
||
|
||
## Full Compose designer
|
||
|
||
The Compose tab uses two synchronized representations:
|
||
|
||
1. the raw YAML source of truth
|
||
2. a recursive visual designer
|
||
|
||
YAML changes are parsed after a short debounce. Visual changes patch the selected YAML AST path instead of reserializing a simplified Compose model. Unknown keys, `x-*` extensions and comments are therefore preserved where possible. Invalid/incomplete YAML pauses visual synchronization and never overwrites the source editor.
|
||
|
||
The designer is not limited to a hard-coded subset: arbitrary maps, arrays and scalar types remain editable, including current/future Compose fields and long syntax objects.
|
||
|
||
Server-side save validation still uses Docker Compose itself after all related `.env`, secret, env-file and config files have been staged.
|
||
|
||
## Reliability and security work in v9 / v9.2
|
||
|
||
The v9.2 identity extension is opt-in, admin-gated and preserves the existing least-surprise rule: diagnostics are read-only by default and no container user or host account is changed automatically.
|
||
|
||
|
||
The v9 review includes, among other changes:
|
||
|
||
- fixed fresh-database migration ordering before monitor indexes are created
|
||
- SQLite WAL/busy-timeout/foreign-key configuration with a small connection pool
|
||
- strict environment parsing instead of silently accepting invalid numeric/boolean values
|
||
- graceful HTTP shutdown and sensible server timeouts
|
||
- security headers and same-origin checks for browser mutations
|
||
- request JSON size limit and single-value decoding
|
||
- admin-only audit activity and notification configuration
|
||
- audit retention cleanup
|
||
- public-status DTO that does not leak internal monitor configuration
|
||
- safe IP parsing for audit entries
|
||
- sensitive viewer responses reduced
|
||
- Compose save snapshot/rollback
|
||
- safe stack delete vs. explicit purge
|
||
- symlink protection on stack/Git file paths
|
||
- atomic managed-file writes
|
||
- Git sync serialization and managed-file manifest
|
||
- Git copy file-descriptor leak fixed
|
||
- Node URL validation rejects credentials/query/fragment
|
||
- remote terminal WebSocket URL/query handling fixed
|
||
- registry/Docker CLI positional-argument validation
|
||
- scheduler query and shutdown behaviour improved
|
||
- defensive frontend collection handling (`null` cannot break list pages)
|
||
- admin-only navigation entries hidden for non-admin users
|
||
- persisted dark/light theme and collapsible/mobile sidebar
|
||
- API connectivity indicator and manual refresh
|
||
- denser Docker/monitor/service tables and better empty/error states
|
||
|
||
## Configuration
|
||
|
||
`.env.example` contains the defaults. Important values:
|
||
|
||
```env
|
||
APP_MODE=standalone
|
||
LISTEN_ADDR=:8080
|
||
BASE_URL=http://localhost:8080
|
||
DATA_DIR=/data
|
||
STACKS_DIR=/stacks
|
||
AUTH_DISABLED=true
|
||
APP_SECRET=change-me-change-me-change-me-32bytes
|
||
CHECK_CONCURRENCY=8
|
||
CHECK_RETENTION_DAYS=30
|
||
HTTP_TIMEOUT_SECONDS=10
|
||
AUDIT_RETENTION_DAYS=180
|
||
HOST_ROOT=
|
||
ALLOW_HOST_USER_MANAGEMENT=false
|
||
ALLOW_HOST_PERMISSION_MANAGEMENT=false
|
||
HOST_SECURITY_ENABLED=false
|
||
ALLOW_HOST_SECURITY_CHANGES=false
|
||
ALLOW_HOST_PACKAGE_MANAGEMENT=false
|
||
HOST_AUR_USER=
|
||
HOST_SECURITY_HOST_PID=1
|
||
```
|
||
|
||
`AUTH_DISABLED=true` is for local development only. Do not expose that configuration publicly.
|
||
|
||
## Run with Docker Compose
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
docker compose up -d --build
|
||
```
|
||
|
||
Runtime mounts normally include:
|
||
|
||
```text
|
||
/data SQLite, Docker registry config
|
||
/stacks Compose projects
|
||
/var/run/docker.sock Docker Engine access
|
||
```
|
||
|
||
Giving Dockwatch access to the Docker socket grants highly privileged control of that Docker host. Protect the UI and agent endpoint accordingly.
|
||
|
||
## v9.3.1 build-context fix
|
||
|
||
v9.3 accidentally used the broad ignore pattern `dockwatch` in both `.gitignore` and `.dockerignore`. Because patterns without a slash match path components recursively, that could hide `cmd/dockwatch/` from Git and from the Docker build context. v9.3.1 removes that pattern, writes local Makefile builds to `bin/dockwatch`, ignores only `bin/`/`dist/`, and makes the Dockerfile fail early with a clear message if `cmd/dockwatch/main.go` is ever missing from the build context.
|
||
|
||
## Build from source
|
||
|
||
The pinned OIDC/OAuth2 releases require **Go 1.25**. The Docker build uses `golang:1.25-alpine`.
|
||
|
||
```bash
|
||
go mod tidy
|
||
go test ./...
|
||
CGO_ENABLED=0 go build ./cmd/dockwatch
|
||
```
|
||
|
||
Build metadata can be injected through the supplied Makefile/Dockerfile (`VERSION`, `COMMIT`, `BUILD_DATE`) and is visible in `/healthz` and the UI.
|
||
|
||
## Database
|
||
|
||
SQLite database:
|
||
|
||
```text
|
||
/data/dockwatch.db
|
||
```
|
||
|
||
`modernc.org/sqlite` is used, so no SQLite CGO binding is required. Migrations are additive and include users/sessions, nodes, monitors/checks/services, public status pages, audit events, notification channels and Git sources.
|
||
|
||
## Verification for this archive
|
||
|
||
The artifact-building environment cannot reach `proxy.golang.org`, so it cannot download the real external modules or generate a trustworthy `go.sum` here. The repository intentionally does **not** ship fake checksums or test stubs.
|
||
|
||
For quality control, the project is copied into a temporary test workspace where API-compatible local stubs replace only the external dependencies. Those stubs are not included in the ZIP. The checks used for this source snapshot include:
|
||
|
||
```text
|
||
# all packages type/compile checked with temporary external-module stubs
|
||
go test -run='^$' ./...
|
||
|
||
go test ./internal/config ./internal/gitops ./internal/httpapi \
|
||
./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks ./internal/hostsecurity
|
||
|
||
go vet ./...
|
||
go test -race ./internal/config ./internal/gitops ./internal/httpapi ./internal/monitor ./internal/nodes ./internal/notify ./internal/stacks ./internal/hostsecurity
|
||
node --check web/app.js
|
||
```
|
||
|
||
The SQLite migration SQL is additionally smoke-tested against Python's SQLite engine because the temporary `modernc.org/sqlite` stub does not implement a real SQL driver.
|
||
|
||
On a normal networked development machine or during `docker build`, run `go mod tidy && go test ./...` once against the real pinned dependencies.
|
||
|
||
### Module metadata / `go mod tidy`
|
||
|
||
The Docker builder runs `go mod tidy` after the full source tree has been copied and before compiling. This is intentional: Go 1.17+ module graph pruning may require transitive dependencies to be recorded as indirect requirements once the actual imported package graph is known. A checkout with an older/incomplete `go.mod` or without `go.sum` therefore still builds reproducibly in CI instead of failing late with `updates to go.mod needed`.
|
||
|
||
For repository maintenance, run:
|
||
|
||
```bash
|
||
go mod tidy
|
||
git add go.mod go.sum
|
||
git commit -m "chore: tidy Go modules"
|
||
```
|
||
|
||
CI can use `make verify` to fail when `go.mod`/`go.sum` are not committed in tidy form.
|
||
|
||
|
||
## v9.3.2 module-build fix
|
||
|
||
v9.3.2 fixes Docker/CI builds that stopped at `go: updates to go.mod needed; to update it: go mod tidy`. The builder now runs `go mod tidy` after the complete source tree has been copied and before `go build`, so indirect requirements required by Go module graph pruning are materialized in the build stage. The Makefile also includes `tidy` and `verify` targets for maintaining committed `go.mod`/`go.sum` files.
|
||
|
||
### Build-context note (v9.3.3)
|
||
|
||
Runtime directories in `.dockerignore` and `.gitignore` are root-anchored (`/stacks/`, `/data/`, `/bin/`, `/dist/`). This is intentional: unanchored patterns such as `stacks/` also match the source package `internal/stacks/` and can make Go try to resolve the project's own internal package as a remote module during `go mod tidy`.
|
||
|
||
### v9.5.2 UI polish
|
||
|
||
- Firewall rule editor uses a wide responsive modal and keeps long CIDRs, comments and provider output inside the dialog.
|
||
- Security details and notices wrap long unbroken values safely.
|
||
- Light mode now has consistent navigation, buttons, inputs, tables, tabs, code/editor surfaces, notices, modals and Host Security cards instead of inheriting dark-only hard-coded colors.
|