diff --git a/butane/monitor.yaml.tftpl b/butane/monitor.yaml.tftpl index bb2d67d..9e0a1e1 100644 --- a/butane/monitor.yaml.tftpl +++ b/butane/monitor.yaml.tftpl @@ -45,8 +45,9 @@ systemd: # wait-for-agents.sh can read the agent list from the same place the # container does. docker run forwards them by bare name below. Environment="DOZZLE_HOSTNAME=Oswald Logs" - Environment="DOZZLE_ENABLE_MCP=true" - Environment="DOZZLE_REMOTE_AGENT=192.168.7.60:7007|oswald-runner|Goose,192.168.7.61:7007|ai|Goose,192.168.7.62:7007|oswl-connector|Goose,192.168.7.64:7007|cisa-kev-rss|Goose" + # The agent list lives in a file because dozzle-mcp.service needs the + # identical list; keeping it inline in both units would drift. + EnvironmentFile=/etc/dozzle/agents.env # OIDC via Authelia. Dozzle builds its redirect from the incoming # request, so the proxy in front must pass the real Host and # X-Forwarded-Proto; the URI to register in Authelia is @@ -76,7 +77,6 @@ systemd: --tmpfs /data \ -p 8080:8080 \ -e DOZZLE_HOSTNAME \ - -e DOZZLE_ENABLE_MCP \ -e DOZZLE_REMOTE_AGENT \ -e DOZZLE_AUTH_PROVIDER \ -e DOZZLE_AUTH_OIDC_ISSUER \ @@ -91,9 +91,58 @@ systemd: RestartSec=5s [Install] WantedBy=multi-user.target + # Second Dozzle, dedicated to MCP on :8081. + # + # It exists because the two requirements cannot be met by one instance: + # the oidc provider is the only one that lets Authelia own the user list, + # but it offers no non-interactive login, so an MCP client can never + # authenticate against it. This instance therefore runs with no auth at + # all (DOZZLE_AUTH_PROVIDER unset) and is reached only through the + # Cloudflare tunnel, which is where access control has to live. + # + # ANYTHING that can open :8081 gets unauthenticated read access to every + # log on every agent. Do not publish this port beyond the tunnel, and put + # a Cloudflare Access policy (service token) on its hostname. + - name: dozzle-mcp.service + enabled: true + contents: | + [Unit] + Description=Dozzle MCP endpoint + After=docker.service + Requires=docker.service + [Service] + TimeoutStartSec=0 + # Same agents as the hub, same file, so the two cannot drift. + EnvironmentFile=/etc/dozzle/agents.env + Environment="DOZZLE_HOSTNAME=Oswald Logs (MCP)" + Environment="DOZZLE_ENABLE_MCP=true" + ExecStartPre=/usr/bin/docker pull amir20/dozzle:v11.1.1 + ExecStartPre=-/usr/bin/docker rm --force dozzle-mcp + ExecStartPre=-/usr/bin/timeout 180 /opt/dozzle/wait-for-agents.sh + ExecStart=/usr/bin/docker run --rm --name dozzle-mcp \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + --tmpfs /data \ + -p 8081:8080 \ + -e DOZZLE_HOSTNAME \ + -e DOZZLE_REMOTE_AGENT \ + -e DOZZLE_ENABLE_MCP \ + amir20/dozzle:v11.1.1 + ExecStop=/usr/bin/docker stop dozzle-mcp + Restart=always + RestartSec=5s + [Install] + WantedBy=multi-user.target storage: files: + # Single source of truth for the agent list, shared by dozzle.service and + # dozzle-mcp.service. systemd EnvironmentFile format: no quoting, so the + # "|" and "," separators are taken literally. + - path: /etc/dozzle/agents.env + mode: 0644 + contents: + inline: | + DOZZLE_REMOTE_AGENT=192.168.7.60:7007|oswald-runner|Goose,192.168.7.61:7007|ai|Goose,192.168.7.62:7007|oswl-connector|Goose,192.168.7.64:7007|cisa-kev-rss|Goose,192.168.7.4:7007|PortaNAS|Portanas # Blocks until every agent named in DOZZLE_REMOTE_AGENT accepts a TCP # connection. The list is read from the environment, so adding or removing # an agent above needs no change here.