From 449267e19b73008f8d54499dce0302e3d60f4204 Mon Sep 17 00:00:00 2001 From: Vijit Singh Date: Thu, 4 Jun 2026 08:13:42 -0500 Subject: [PATCH] Log rotation on all services + pin base images by digest (#123, #135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #123: caddy, docker-proxy, and docker-control were missing `logging:`, so they used Docker's uncapped json-file default — their logs could grow without bound and fill the disk on a long-running host. Apply the shared 10 MB × 3 cap to all three. #135: pin every externally-pulled image by immutable @sha256 digest — caddy:2.11, tecnativa/docker-socket-proxy:v0.4.2 (×2), the Tari node, and the ubuntu:24.04 / python:3.11-slim / alpine build bases — so a re-pushed tag or registry MITM can't silently change the running image. Digests are the multi-arch index digests. Locked in with test_compose.sh assertions (log rotation on every service; the three external images carry an @sha256 digest). CHANGELOG updated. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 6 ++++++ build/dashboard/Dockerfile | 3 ++- build/monero/Dockerfile | 3 ++- build/p2pool/Dockerfile | 3 ++- build/tor/Dockerfile | 4 +++- build/xmrig-proxy/Dockerfile | 3 ++- docker-compose.yml | 12 ++++++++---- tests/stack/test_compose.sh | 10 ++++++++++ 8 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73b872e..35bf67a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,9 @@ per the process in [`docs/releasing.md`](docs/releasing.md). writes its SQLite history into a host-user-owned volume as root. Caddy and the two Docker socket proxies additionally run with a read-only root filesystem (ephemeral `tmpfs` for scratch; Caddy's certs persist in `caddy_data`). +- Log rotation (`json-file`, 10 MB × 3) now applies to **every** service — `caddy`, + `docker-proxy`, and `docker-control` previously fell back to Docker's uncapped default, so + their logs could grow without bound and fill the disk on a long-running host (#123). ### Security @@ -70,3 +73,6 @@ per the process in [`docs/releasing.md`](docs/releasing.md). environment via a script. - Documented that the stratum port defaults to all interfaces and should be firewalled to the LAN — see [Connecting Miners › Firewall](docs/workers.md#firewall). +- All externally-pulled base/runtime images are now pinned by immutable `@sha256` digest + (caddy, docker-socket-proxy, the Tari node, and the `ubuntu`/`python`/`alpine` build bases), + so a re-pushed tag or a registry MITM can't silently change the running image (#135). diff --git a/build/dashboard/Dockerfile b/build/dashboard/Dockerfile index 4bfc9c7..b58a597 100644 --- a/build/dashboard/Dockerfile +++ b/build/dashboard/Dockerfile @@ -1,7 +1,8 @@ # ========================================================================== # Shared base: system deps + package metadata + source. # ========================================================================== -FROM python:3.11-slim AS base +# Pinned by digest (#135) so the python:3.11-slim tag can't be silently re-pointed. +FROM python:3.11-slim@sha256:a3ab0b966bc4e91546a033e22093cb840908979487a9fc0e6e38295747e49ac0 AS base # System dependencies. RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/build/monero/Dockerfile b/build/monero/Dockerfile index 0b50330..73b8f14 100644 --- a/build/monero/Dockerfile +++ b/build/monero/Dockerfile @@ -1,4 +1,5 @@ -FROM ubuntu:24.04 +# Pinned by digest (#135) so the ubuntu:24.04 tag can't be silently re-pointed. +FROM ubuntu:24.04@sha256:786a8b558f7be160c6c8c4a54f9a57274f3b4fb1491cf65146521ae77ff1dc54 # Install system dependencies required for downloading, verifying, and configuring the node # 'gettext-base' provides 'envsubst' for template processing in entrypoint.sh diff --git a/build/p2pool/Dockerfile b/build/p2pool/Dockerfile index 75c3bd6..f69beb8 100644 --- a/build/p2pool/Dockerfile +++ b/build/p2pool/Dockerfile @@ -1,4 +1,5 @@ -FROM ubuntu:24.04 +# Pinned by digest (#135) so the ubuntu:24.04 tag can't be silently re-pointed. +FROM ubuntu:24.04@sha256:786a8b558f7be160c6c8c4a54f9a57274f3b4fb1491cf65146521ae77ff1dc54 ARG P2POOL_VERSION=v4.15.1 ARG P2POOL_HASH=efd8b23579774711a5b86743da980e0936b7c220894063296719116d7f9ba254 diff --git a/build/tor/Dockerfile b/build/tor/Dockerfile index d7509f2..66c7851 100644 --- a/build/tor/Dockerfile +++ b/build/tor/Dockerfile @@ -1,4 +1,6 @@ -FROM alpine:latest +# Pinned by digest (#135). NOTE: still the floating `latest` tag — a follow-up could move this to a +# specific alpine version; the digest already makes the build reproducible regardless. +FROM alpine:latest@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 # Install Tor plus the tools the bootstrap healthcheck needs: netcat (talk to the # control port) and xxd (hex-encode the auth cookie). --no-cache keeps the image small. diff --git a/build/xmrig-proxy/Dockerfile b/build/xmrig-proxy/Dockerfile index bf6cf20..afd797c 100644 --- a/build/xmrig-proxy/Dockerfile +++ b/build/xmrig-proxy/Dockerfile @@ -1,4 +1,5 @@ -FROM ubuntu:24.04 +# Pinned by digest (#135) so the ubuntu:24.04 tag can't be silently re-pointed. +FROM ubuntu:24.04@sha256:786a8b558f7be160c6c8c4a54f9a57274f3b4fb1491cf65146521ae77ff1dc54 # Define XMRig Proxy version and SHA256 hash for binary verification # Refer to https://github.com/xmrig/xmrig-proxy/releases for validation diff --git a/docker-compose.yml b/docker-compose.yml index a42fa01..782fb42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,7 +67,8 @@ services: # --- Tari Base Node --- tari: - image: quay.io/tarilabs/minotari_node:v5.3.1-mainnet + # Pinned by digest (#135) so the v5.3.1-mainnet tag can't be silently re-pointed. + image: quay.io/tarilabs/minotari_node:v5.3.1-mainnet@sha256:824fd6ec21d618805317d7eede374d6782906eeae17d2fc8aaad4df6205f94e0 container_name: tari restart: unless-stopped stop_grace_period: 1m @@ -288,9 +289,10 @@ services: # POST stays off, so this proxy can never write — container control goes through the # separate, tightly-scoped docker-control proxy below. docker-proxy: - image: tecnativa/docker-socket-proxy:v0.4.2 + image: tecnativa/docker-socket-proxy:v0.4.2@sha256:1f3a6f303320723d199d2316a3e82b2e2685d86c275d5e3deeaf182573b47476 container_name: docker-proxy restart: unless-stopped + logging: *default-logging security_opt: - no-new-privileges:true cap_drop: @@ -318,9 +320,10 @@ services: # (create/kill/exec/reads). Kept separate from the read-only proxy above so opening POST # here can't widen that proxy's container access. docker-control: - image: tecnativa/docker-socket-proxy:v0.4.2 + image: tecnativa/docker-socket-proxy:v0.4.2@sha256:1f3a6f303320723d199d2316a3e82b2e2685d86c275d5e3deeaf182573b47476 container_name: docker-control restart: unless-stopped + logging: *default-logging security_opt: - no-new-privileges:true cap_drop: @@ -345,9 +348,10 @@ services: # Bridges the local 127.0.0.1 application binding to the LAN # Serves HTTPS or HTTP depending on configuration caddy: - image: caddy:2.11 + image: caddy:2.11@sha256:cb9d71ad83182011b79355cd57692686374bd78d6fe327efe0ff8507da03ab13 container_name: caddy restart: unless-stopped + logging: *default-logging network_mode: "host" # Drop all capabilities, then add back only NET_BIND_SERVICE — Caddy serves the dashboard on # :80/:443 (privileged ports) and would otherwise fail to bind once ALL caps are dropped (#90). diff --git a/tests/stack/test_compose.sh b/tests/stack/test_compose.sh index 055fc22..5001464 100755 --- a/tests/stack/test_compose.sh +++ b/tests/stack/test_compose.sh @@ -96,6 +96,16 @@ expect_present "monerod healthcheck via script" "monerod-healthcheck.sh" expect_present "p2pool healthcheck via script" "p2pool-healthcheck.sh" expect_absent "no get_info (creds) in compose healthcheck" "get_info" +# Log rotation (#123): every service must carry the json-file size cap, including caddy and the two +# socket proxies that previously fell back to Docker's uncapped default. All 9 services (monerod is +# present under the local_node profile in this env) render one `max-size` line each. +expect_min "log rotation on every service" "max-size:" 9 +# Image digest pinning (#135): the externally-pulled images must reference an immutable @sha256 +# digest, not just a mutable tag, so a re-pushed tag can't silently change the running image. +expect_present "tecnativa socket-proxy pinned by digest" "tecnativa/docker-socket-proxy:v0.4.2@sha256:" +expect_present "caddy pinned by digest" "caddy:2.11@sha256:" +expect_present "tari node pinned by digest" "minotari_node:v5.3.1-mainnet@sha256:" + if [ "$fails" -ne 0 ]; then echo " ✗ $fails hardening check(s) failed" exit 1