mirror of
https://github.com/tiennm99/composes.git
synced 2026-10-11 03:13:16 +00:00
feat(diun): add diun template reading the Docker API through a proxy
Diun needs the Docker API to enumerate containers and inspect each one's image. Mounting the socket into it directly is host-root-equivalent, and :ro on a socket mount is cosmetic, so the socket goes into a docker-socket-proxy sidecar and Diun reaches it at tcp://dockerproxy:2375. POST is revoked there, so container create and exec return 403. CONTAINERS and IMAGES are both required: with CONTAINERS alone the provider loads and enumerates containers, then every ImageInspect returns 403 and nothing is analysed. Verified against a live watch cycle — 25 images analysed, no errors. Pin crazymax/diun:4.33 rather than :latest, since this is the service whose job is to talk to the daemon.
This commit is contained in:
1 parent
c2502d5f44
commit
24cefe80a2
4 files changed
+110
No files matched your search
@@ -56,6 +56,7 @@ Each links to its own README for variables, ports, and storage.
|
||||
| [alloy](alloy/README.md) | Grafana Alloy shipping host and Docker telemetry to Grafana Cloud |
|
||||
| [code-server](code-server/README.md) | VS Code in the browser, as a remote dev box |
|
||||
| [couchbase](couchbase/README.md) | Couchbase Server |
|
||||
| [diun](diun/README.md) | Image-update notifier, reading the Docker API through a read-only proxy |
|
||||
| [gitea-mirror](gitea-mirror/README.md) | Gitea + PostgreSQL + gitea-mirror, mirroring GitHub repos |
|
||||
| [opencode-web](opencode-web/README.md) | opencode coding agent, served as a browser UI |
|
||||
| [openhands](openhands/README.md) | OpenHands coding agent, running each session in a container it spawns |
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
DIUN_NOTIF_TELEGRAM_TOKEN=
|
||||
DIUN_NOTIF_TELEGRAM_CHATIDS=
|
||||
DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true
|
||||
DIUN_WATCH_SCHEDULE=0 */6 * * *
|
||||
DIUN_WATCH_WORKERS=10
|
||||
DIUN_WATCH_JITTER=30s
|
||||
TZ=UTC
|
||||
LOG_LEVEL=info
|
||||
LOG_JSON=false
|
||||
@@ -0,0 +1,62 @@
|
||||
# diun
|
||||
|
||||
[Diun](https://crazymax.dev/diun/) watches the images of every running
|
||||
container and sends a Telegram message when one has an update. It only
|
||||
notifies — it never pulls or restarts anything.
|
||||
|
||||
Two containers: `diun` itself, and `dockerproxy`, which hands it a read-only
|
||||
slice of the Docker API.
|
||||
|
||||
## Docker API access
|
||||
|
||||
Diun needs the Docker API to enumerate containers and read the image reference
|
||||
each one runs. Mounting `/var/run/docker.sock` into it directly would be
|
||||
host-root-equivalent — the API has no read/write split, so anything that can
|
||||
talk to the socket can create a privileged container that mounts `/`. Adding
|
||||
`:ro` to the mount does not help: it stops the socket *file* being replaced, not
|
||||
the API being used.
|
||||
|
||||
So the socket is mounted into
|
||||
[tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
|
||||
instead, and Diun reaches it over the compose network at
|
||||
`tcp://dockerproxy:2375`. `POST` is revoked by default in that image, so
|
||||
container create, `exec`, start and kill return 403. A compromised Diun image
|
||||
can no longer become root on the host — which matters because Diun is the one
|
||||
service here whose whole job is to talk to the daemon.
|
||||
|
||||
Exactly two API sections are granted, both verified against a live watch cycle:
|
||||
|
||||
| Variable | Why |
|
||||
| --- | --- |
|
||||
| `CONTAINERS` | enumerate running containers |
|
||||
| `IMAGES` | `ImageInspect` on each container's image — without it every image logs `403 Forbidden` and nothing is analysed |
|
||||
|
||||
`INFO`, `NETWORKS`, `VOLUMES` and the rest stay revoked; a watch cycle runs
|
||||
clean without them.
|
||||
|
||||
## Environment
|
||||
|
||||
`DIUN_NOTIF_TELEGRAM_TOKEN` and `DIUN_NOTIF_TELEGRAM_CHATIDS` are required and
|
||||
fail fast if unset. Everything else has a working default.
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `DIUN_NOTIF_TELEGRAM_TOKEN` | — | Bot token from @BotFather |
|
||||
| `DIUN_NOTIF_TELEGRAM_CHATIDS` | — | Comma-separated chat ids to notify |
|
||||
| `DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT` | `true` | Watch every container without per-container labels |
|
||||
| `DIUN_WATCH_SCHEDULE` | `0 */6 * * *` | Cron for the watch cycle |
|
||||
| `DIUN_WATCH_WORKERS` | `10` | Parallel registry lookups |
|
||||
| `DIUN_WATCH_JITTER` | `30s` | Random delay before each job |
|
||||
| `TZ` | `UTC` | Timezone for the schedule and log timestamps |
|
||||
| `LOG_LEVEL` / `LOG_JSON` | `info` / `false` | Logging |
|
||||
|
||||
State lives in the `diun-data` volume (`DIUN_DB_PATH=/data/diun.db`). Diun
|
||||
notifies on first sight of an image, so a fresh volume produces one round of
|
||||
notifications for everything currently running.
|
||||
|
||||
## Version pinning
|
||||
|
||||
`crazymax/diun:4.33` rather than `:latest`. Diun holds Docker API access, so an
|
||||
unreviewed image change is the highest-leverage supply-chain step on the host;
|
||||
the proxy bounds what a bad image could do, and the pin means an image only
|
||||
changes when this file does.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Required env vars (set in Coolify/Dokploy, or a sibling .env):
|
||||
# DIUN_NOTIF_TELEGRAM_TOKEN, DIUN_NOTIF_TELEGRAM_CHATIDS
|
||||
|
||||
services:
|
||||
diun:
|
||||
image: crazymax/diun:4.33
|
||||
command: serve
|
||||
environment:
|
||||
DIUN_PROVIDERS_DOCKER: "true"
|
||||
DIUN_PROVIDERS_DOCKER_ENDPOINT: tcp://dockerproxy:2375
|
||||
DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: "${DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT:-true}"
|
||||
DIUN_NOTIF_TELEGRAM_TOKEN: ${DIUN_NOTIF_TELEGRAM_TOKEN:?required}
|
||||
DIUN_NOTIF_TELEGRAM_CHATIDS: ${DIUN_NOTIF_TELEGRAM_CHATIDS:?required}
|
||||
DIUN_DB_PATH: /data/diun.db
|
||||
DIUN_WATCH_SCHEDULE: "${DIUN_WATCH_SCHEDULE:-0 */6 * * *}"
|
||||
DIUN_WATCH_WORKERS: "${DIUN_WATCH_WORKERS:-10}"
|
||||
DIUN_WATCH_JITTER: "${DIUN_WATCH_JITTER:-30s}"
|
||||
TZ: "${TZ:-UTC}"
|
||||
LOG_LEVEL: "${LOG_LEVEL:-info}"
|
||||
LOG_JSON: "${LOG_JSON:-false}"
|
||||
volumes:
|
||||
- diun-data:/data
|
||||
depends_on:
|
||||
- dockerproxy
|
||||
|
||||
# Read-only slice of the Docker API. POST is revoked by default in this image.
|
||||
dockerproxy:
|
||||
image: tecnativa/docker-socket-proxy:v0.5.0
|
||||
environment:
|
||||
CONTAINERS: 1
|
||||
IMAGES: 1
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
volumes:
|
||||
diun-data:
|
||||
Reference in new issue
Block a user