From 37430b3b445e2edd9df854e810229b038a8218ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Sun, 25 Oct 2026 18:10:59 +0100 Subject: [PATCH 1/3] build: Debian security updates in a single flattened layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gotenberg base only gets Debian fixes when upstream rebuilds its tag; Trivy found 659 fixable HIGH/CRITICAL CVEs (Chromium, LibreOffice, OpenSSL…). The runtime stage now runs apt-get upgrade, so every build, including the weekly one, ships them fixed: 0 left in Debian packages, 16 remain in the gotenberg and pdfcpu binaries (upstream only). As layers, the upgraded Chromium and LibreOffice would sit on top of the base's old copies (3.99 GB). The final stage is FROM scratch with one COPY of the runtime filesystem: 2.75 GB, smaller than before the upgrade. Ownership, modes and setuid files are identical to the layered image. FROM scratch drops the base image's config, so the Dockerfile restates Gotenberg's ENV and WORKDIR; make check-base (in the CI smoke job) fails when the base has entries we do not, e.g. after a GOTENBERG_VERSION bump. Changelog gets an Unreleased section; make release now says to rename it. --- .github/workflows/docker.yml | 3 +++ AGENTS.md | 5 +++-- CHANGELOG.md | 10 ++++++++++ Dockerfile | 29 ++++++++++++++++++++++++++++- Makefile | 14 ++++++++++++-- 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5c4609c..f8cf60c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -80,6 +80,9 @@ jobs: - name: Render a page run: make smoke DOCKER_IMAGE=dockette/docshot:test + - name: Check base config is restated + run: make check-base DOCKER_IMAGE=dockette/docshot:test + - name: Check HTML isolation run: make test-isolation DOCKER_IMAGE=dockette/docshot:test diff --git a/AGENTS.md b/AGENTS.md index 26f06ae..ea23c91 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,8 +7,9 @@ Dockette Docshot builds `dockette/docshot`, an HTTP service that renders any doc ## Images - Default image: `dockette/docshot:latest`. -- Build context: repository root `.` with `Dockerfile` (multi-stage: `golang:1.27-trixie` build → `gotenberg/gotenberg` runtime). -- Date versions: `VERSION` holds `YYYY.MM.DD` (`.N` for a same-day re-release), bumped by `make release`, with a matching `## ` section in `CHANGELOG.md` (user-facing: Breaking / Added / Fixed / Changed) that becomes the GitHub release notes; it's built into the binary (`main.version`, via `-ldflags -X`), tags images `latest`, `` and `sha-`, and CI creates the matching GitHub release. See README → *Development*. +- Build context: repository root `.` with `Dockerfile` (multi-stage: `golang:1.27-trixie` build → `gotenberg/gotenberg` runtime stage, which runs `apt-get upgrade` for Debian security fixes → `FROM scratch` + `COPY --from=runtime / /`, one layer so upgraded Chromium/LibreOffice do not stack on the base's copies). +- Flattening drops the base image's config: the Dockerfile restates Gotenberg's ENV and WORKDIR, and `make check-base` (CI smoke job) fails when they drift, e.g. after bumping `GOTENBERG_VERSION`. +- Date versions: `VERSION` holds `YYYY.MM.DD` (`.N` for a same-day re-release), bumped by `make release`, with `## Unreleased` in `CHANGELOG.md` renamed to `## ` (user-facing: Breaking / Added / Fixed / Changed) that becomes the GitHub release notes; it's built into the binary (`main.version`, via `-ldflags -X`), tags images `latest`, `` and `sha-`, and CI creates the matching GitHub release. See README → *Development*. - The Go binary (no CGO, `cmd/docshot` + `internal/*`) listens on `:3000` and supervises Gotenberg as a child on `127.0.0.1:3001`; the image `CMD` is Gotenberg's own command line. - docshot is a child subreaper: Gotenberg runs in its own process group, and the supervisor kills/reaps only children outside docshot's group. Tools run with `os/exec` (ffmpeg, bsdtar…) must stay in docshot's group (no `Setpgid`), or a Gotenberg restart would kill them. - On SIGTERM, HTTP drains first (30s), then Gotenberg stops; compose's `stop_grace_period` covers both. diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5ca80..5afdd39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ Versions are release dates (`YYYY.MM.DD`, `.N` for a same-day re-release); each one is the `dockette/docshot:` image and the GitHub release of the same name. See README → *Versions*. +## Unreleased + +### Changed + +- The image applies Debian security updates on top of the Gotenberg base at build time, and so on + every weekly rebuild: 659 fixable HIGH/CRITICAL CVEs in Chromium, LibreOffice, OpenSSL… down to 0. +- The image is a single flattened layer, so upgraded packages replace the base's copies instead of + stacking on them: 2.75 GB (layered with the upgrade it would be 3.99 GB). A pull downloads the + whole layer once per release instead of sharing layers with `gotenberg/gotenberg`. + ## 2026.10.24 ### Breaking diff --git a/Dockerfile b/Dockerfile index aeddd3a..d81c285 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,17 +10,44 @@ COPY internal ./internal ARG VERSION=dev RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o /docshot ./cmd/docshot -FROM gotenberg/gotenberg:${GOTENBERG_VERSION} +FROM gotenberg/gotenberg:${GOTENBERG_VERSION} AS runtime +# Debian security fixes for the base (Chromium, LibreOffice, OpenSSL…) without waiting for a Gotenberg rebuild; the weekly CI build picks up new ones. # ffmpeg and per-format helpers; /tmp/docshot is the disk cache (DOCSHOT_CACHE_DIR), owned by the runtime user so a named volume inherits it. libwebp symlinks: gen2brain/webp dlopens libwebp.so via purego for faster native encoding. USER root RUN apt-get update \ + && apt-get upgrade -y --no-install-recommends \ && apt-get install -y --no-install-recommends ffmpeg libwebp7 libwebpdemux2 \ libtiff-tools libarchive-tools djvulibre-bin libgxps-utils graphviz libraw-bin \ + && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && lib="$(dirname /usr/lib/*-linux-gnu/libwebp.so.7)" \ && ln -sf libwebp.so.7 "$lib/libwebp.so" && ln -sf libwebpdemux.so.2 "$lib/libwebpdemux.so" \ && install -d -o gotenberg -g gotenberg /tmp/docshot COPY --from=build /docshot /usr/bin/docshot +USER gotenberg + +# One layer: as layers, the upgraded Chromium and LibreOffice would sit on top of the base's old copies (~1 GB). +# FROM scratch drops the base image's config, so it is restated here; CI (make check-base) fails if Gotenberg adds ENV we lack. +FROM scratch +# hadolint ignore=DL3067 +COPY --from=runtime / / +ENV PATH=/opt/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ + TZ=UTC \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 \ + PDFTK_BIN_PATH=/usr/bin/pdftk \ + QPDF_BIN_PATH=/usr/bin/qpdf \ + EXIFTOOL_BIN_PATH=/usr/bin/exiftool \ + PDFCPU_BIN_PATH=/usr/bin/pdfcpu \ + GOTENBERG_VERSIONS_DIR_PATH=/opt/gotenberg/versions \ + OTEL_TRACES_EXPORTER=none \ + OTEL_METRICS_EXPORTER=none \ + OTEL_LOGS_EXPORTER=none \ + CHROMIUM_BIN_PATH=/usr/bin/chromium \ + CHROMIUM_HYPHEN_DATA_DIR_PATH=/opt/gotenberg/chromium-hyphen-data \ + LIBREOFFICE_BIN_PATH=/usr/lib/libreoffice/program/soffice.bin \ + UNOCONVERTER_BIN_PATH=/usr/bin/unoconverter +WORKDIR /home/gotenberg ARG VERSION=dev LABEL org.opencontainers.image.version=$VERSION USER gotenberg diff --git a/Makefile b/Makefile index 5088380..6c91a65 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ DOCKER_IMAGE ?= dockette/docshot:latest VERSION := $(shell cat VERSION) .DEFAULT_GOAL := up -.PHONY: test-formats build run up down restart logs ps validate test test-params test-all formats test-unit lint smoke test-isolation version release +.PHONY: test-formats build run up down restart logs ps validate test test-params test-all formats test-unit lint smoke test-isolation check-base version release build: ## Build the public image (docker buildx) docker buildx build --build-arg VERSION=$(VERSION) -t ${DOCKER_IMAGE} . @@ -30,6 +30,16 @@ smoke: ## Start the public image, wait for /readyz, render Markdown t ct=$$(printf '# docshot\n\nsmoke test\n' | curl -sS -m 120 -o /dev/null -w '%{http_code} %{content_type}' --data-binary @- 'http://localhost:39994/upload?name=smoke.md'); \ docker rm -f docshot-smoke >/dev/null; echo "smoke: $$ct"; [ "$$ct" = "200 image/png" ] +# The image is flattened FROM scratch, which drops the base image's config: fail when Gotenberg +# has ENV entries or a WORKDIR the Dockerfile does not restate (e.g. after a GOTENBERG_VERSION bump). +check-base: ## ${DOCKER_IMAGE} keeps every ENV and the WORKDIR of its Gotenberg base + @v=$$(sed -n 's/^ARG GOTENBERG_VERSION=//p' Dockerfile); base=gotenberg/gotenberg:$$v; docker pull -q $$base >/dev/null; \ + env='{{range .Config.Env}}{{println .}}{{end}}'; \ + missing=$$(docker image inspect --format "$$env" $$base | grep -vxF "$$(docker image inspect --format "$$env" ${DOCKER_IMAGE})"); \ + wb=$$(docker image inspect --format '{{.Config.WorkingDir}}' $$base); wo=$$(docker image inspect --format '{{.Config.WorkingDir}}' ${DOCKER_IMAGE}); \ + if [ -n "$$missing" ] || [ "$$wb" != "$$wo" ]; then echo "check-base: $$base config not restated in the Dockerfile:"; printf ' ENV %s\n' $$missing; [ "$$wb" = "$$wo" ] || echo " WORKDIR $$wb (ours: $$wo)"; exit 1; fi; \ + echo "check-base: ${DOCKER_IMAGE} keeps the config of $$base" + test-isolation: ## Untrusted HTML: no JavaScript, no remote fetches (starts ${DOCKER_IMAGE}) test/isolation.sh ${DOCKER_IMAGE} @@ -45,7 +55,7 @@ release: ## Set VERSION to today's date (see README → Versions) while git rev-parse -q --verify "refs/tags/$$v" >/dev/null; do n=$$((n+1)); v=$$(date -u +%Y.%m.%d).$$n; done; \ echo "$$v" > VERSION; \ echo "VERSION: $(VERSION) -> $$v"; \ - echo "next: add a '## $$v' section to CHANGELOG.md, then git commit -am 'release: $$v' && git push" + echo "next: rename '## Unreleased' in CHANGELOG.md to '## $$v', then git commit -am 'release: $$v' && git push" up: ## Build (if needed) + start docshot (Gotenberg inside) DOCSHOT_VERSION=$(VERSION) $(COMPOSE) up -d --build From 31f0bd94a1c57a2bc6f2371b95ba91878f2c6fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Sun, 25 Oct 2026 20:32:53 +0100 Subject: [PATCH 2/3] Docker: drop make check-base, shorten Dockerfile comments The flattened image still restates Gotenberg's ENV and WORKDIR; the smoke and isolation tests cover the rest. --- .github/workflows/docker.yml | 3 --- AGENTS.md | 2 +- Dockerfile | 10 ++++------ Makefile | 12 +----------- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f8cf60c..5c4609c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -80,9 +80,6 @@ jobs: - name: Render a page run: make smoke DOCKER_IMAGE=dockette/docshot:test - - name: Check base config is restated - run: make check-base DOCKER_IMAGE=dockette/docshot:test - - name: Check HTML isolation run: make test-isolation DOCKER_IMAGE=dockette/docshot:test diff --git a/AGENTS.md b/AGENTS.md index ea23c91..a62665b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ Dockette Docshot builds `dockette/docshot`, an HTTP service that renders any doc - Default image: `dockette/docshot:latest`. - Build context: repository root `.` with `Dockerfile` (multi-stage: `golang:1.27-trixie` build → `gotenberg/gotenberg` runtime stage, which runs `apt-get upgrade` for Debian security fixes → `FROM scratch` + `COPY --from=runtime / /`, one layer so upgraded Chromium/LibreOffice do not stack on the base's copies). -- Flattening drops the base image's config: the Dockerfile restates Gotenberg's ENV and WORKDIR, and `make check-base` (CI smoke job) fails when they drift, e.g. after bumping `GOTENBERG_VERSION`. +- Flattening drops the base image's config: the Dockerfile restates Gotenberg's ENV and WORKDIR; re-check them after bumping `GOTENBERG_VERSION`. - Date versions: `VERSION` holds `YYYY.MM.DD` (`.N` for a same-day re-release), bumped by `make release`, with `## Unreleased` in `CHANGELOG.md` renamed to `## ` (user-facing: Breaking / Added / Fixed / Changed) that becomes the GitHub release notes; it's built into the binary (`main.version`, via `-ldflags -X`), tags images `latest`, `` and `sha-`, and CI creates the matching GitHub release. See README → *Development*. - The Go binary (no CGO, `cmd/docshot` + `internal/*`) listens on `:3000` and supervises Gotenberg as a child on `127.0.0.1:3001`; the image `CMD` is Gotenberg's own command line. - docshot is a child subreaper: Gotenberg runs in its own process group, and the supervisor kills/reaps only children outside docshot's group. Tools run with `os/exec` (ffmpeg, bsdtar…) must stay in docshot's group (no `Setpgid`), or a Gotenberg restart would kill them. diff --git a/Dockerfile b/Dockerfile index d81c285..0a937dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# All-in-one: docshot on the Gotenberg image; the Go binary listens on :3000 and supervises Gotenberg on 127.0.0.1:3001 (internal). +# docshot on the Gotenberg image: the Go binary listens on :3000 and supervises Gotenberg on 127.0.0.1:3001. ARG GOTENBERG_VERSION=8.37 FROM golang:1.27-trixie AS build @@ -11,8 +11,7 @@ ARG VERSION=dev RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o /docshot ./cmd/docshot FROM gotenberg/gotenberg:${GOTENBERG_VERSION} AS runtime -# Debian security fixes for the base (Chromium, LibreOffice, OpenSSL…) without waiting for a Gotenberg rebuild; the weekly CI build picks up new ones. -# ffmpeg and per-format helpers; /tmp/docshot is the disk cache (DOCSHOT_CACHE_DIR), owned by the runtime user so a named volume inherits it. libwebp symlinks: gen2brain/webp dlopens libwebp.so via purego for faster native encoding. +# Debian security updates, ffmpeg and format helpers; libwebp.so symlinks for gen2brain/webp; /tmp/docshot is the disk cache. USER root RUN apt-get update \ && apt-get upgrade -y --no-install-recommends \ @@ -26,8 +25,7 @@ RUN apt-get update \ COPY --from=build /docshot /usr/bin/docshot USER gotenberg -# One layer: as layers, the upgraded Chromium and LibreOffice would sit on top of the base's old copies (~1 GB). -# FROM scratch drops the base image's config, so it is restated here; CI (make check-base) fails if Gotenberg adds ENV we lack. +# Flatten to one layer so upgraded packages do not stack on the base's copies; the base's ENV and WORKDIR are restated below. FROM scratch # hadolint ignore=DL3067 COPY --from=runtime / / @@ -54,7 +52,7 @@ USER gotenberg EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 CMD ["/usr/bin/docshot", "-healthcheck"] ENTRYPOINT ["/usr/bin/tini", "--", "/usr/bin/docshot"] -# Defaults for untrusted input: no JavaScript, no network for Chromium/LibreOffice, no downloadFrom/webhooks (SSRF). +# Untrusted input: no JavaScript, no network, no downloadFrom or webhooks. CMD ["gotenberg", \ "--chromium-disable-javascript=true", \ "--chromium-deny-public-ips=true", "--chromium-deny-private-ips=true", \ diff --git a/Makefile b/Makefile index 6c91a65..e4926aa 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ DOCKER_IMAGE ?= dockette/docshot:latest VERSION := $(shell cat VERSION) .DEFAULT_GOAL := up -.PHONY: test-formats build run up down restart logs ps validate test test-params test-all formats test-unit lint smoke test-isolation check-base version release +.PHONY: test-formats build run up down restart logs ps validate test test-params test-all formats test-unit lint smoke test-isolation version release build: ## Build the public image (docker buildx) docker buildx build --build-arg VERSION=$(VERSION) -t ${DOCKER_IMAGE} . @@ -30,16 +30,6 @@ smoke: ## Start the public image, wait for /readyz, render Markdown t ct=$$(printf '# docshot\n\nsmoke test\n' | curl -sS -m 120 -o /dev/null -w '%{http_code} %{content_type}' --data-binary @- 'http://localhost:39994/upload?name=smoke.md'); \ docker rm -f docshot-smoke >/dev/null; echo "smoke: $$ct"; [ "$$ct" = "200 image/png" ] -# The image is flattened FROM scratch, which drops the base image's config: fail when Gotenberg -# has ENV entries or a WORKDIR the Dockerfile does not restate (e.g. after a GOTENBERG_VERSION bump). -check-base: ## ${DOCKER_IMAGE} keeps every ENV and the WORKDIR of its Gotenberg base - @v=$$(sed -n 's/^ARG GOTENBERG_VERSION=//p' Dockerfile); base=gotenberg/gotenberg:$$v; docker pull -q $$base >/dev/null; \ - env='{{range .Config.Env}}{{println .}}{{end}}'; \ - missing=$$(docker image inspect --format "$$env" $$base | grep -vxF "$$(docker image inspect --format "$$env" ${DOCKER_IMAGE})"); \ - wb=$$(docker image inspect --format '{{.Config.WorkingDir}}' $$base); wo=$$(docker image inspect --format '{{.Config.WorkingDir}}' ${DOCKER_IMAGE}); \ - if [ -n "$$missing" ] || [ "$$wb" != "$$wo" ]; then echo "check-base: $$base config not restated in the Dockerfile:"; printf ' ENV %s\n' $$missing; [ "$$wb" = "$$wo" ] || echo " WORKDIR $$wb (ours: $$wo)"; exit 1; fi; \ - echo "check-base: ${DOCKER_IMAGE} keeps the config of $$base" - test-isolation: ## Untrusted HTML: no JavaScript, no remote fetches (starts ${DOCKER_IMAGE}) test/isolation.sh ${DOCKER_IMAGE} From 6389327132688fa7c819f7376deae50858ce1aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Sun, 25 Oct 2026 20:39:27 +0100 Subject: [PATCH 3/3] Docker: section banners and spacing in the Dockerfile Comments and whitespace only; the instructions are unchanged. --- Dockerfile | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0a937dc..61cb63b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,42 @@ -# docshot on the Gotenberg image: the Go binary listens on :3000 and supervises Gotenberg on 127.0.0.1:3001. +################################################################################ +# docshot: the Go binary listens on :3000 and supervises Gotenberg on +# 127.0.0.1:3001 (internal). Three stages: build -> runtime -> flattened final. +################################################################################ + ARG GOTENBERG_VERSION=8.37 + +################################################################################ +# Stage 1: build the Go binary (no CGO) +################################################################################ + FROM golang:1.27-trixie AS build + WORKDIR /src + +# Dependencies first, so they stay cached until go.mod/go.sum change. COPY go.mod go.sum ./ RUN go mod download + COPY cmd ./cmd COPY internal ./internal + ARG VERSION=dev RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o /docshot ./cmd/docshot + +################################################################################ +# Stage 2: runtime packages on top of Gotenberg +################################################################################ + FROM gotenberg/gotenberg:${GOTENBERG_VERSION} AS runtime -# Debian security updates, ffmpeg and format helpers; libwebp.so symlinks for gen2brain/webp; /tmp/docshot is the disk cache. + USER root + +# - apt-get upgrade: Debian security updates (Chromium, LibreOffice, OpenSSL...) without waiting for a Gotenberg rebuild +# - ffmpeg and the per-format helpers (tiff, archives, djvu, xps, dot, raw) +# - libwebp.so symlinks: gen2brain/webp dlopens them for native WebP encoding +# - /tmp/docshot: disk cache, owned by the runtime user so a named volume inherits it RUN apt-get update \ && apt-get upgrade -y --no-install-recommends \ && apt-get install -y --no-install-recommends ffmpeg libwebp7 libwebpdemux2 \ @@ -22,13 +46,27 @@ RUN apt-get update \ && lib="$(dirname /usr/lib/*-linux-gnu/libwebp.so.7)" \ && ln -sf libwebp.so.7 "$lib/libwebp.so" && ln -sf libwebpdemux.so.2 "$lib/libwebpdemux.so" \ && install -d -o gotenberg -g gotenberg /tmp/docshot + COPY --from=build /docshot /usr/bin/docshot + USER gotenberg -# Flatten to one layer so upgraded packages do not stack on the base's copies; the base's ENV and WORKDIR are restated below. + +################################################################################ +# Stage 3: flatten into one layer +# +# As separate layers, the upgraded Chromium and LibreOffice would sit on top of +# the base's old copies (~1 GB). FROM scratch drops the base image's config, so +# its ENV and WORKDIR are restated below: re-check them after a +# GOTENBERG_VERSION bump. +################################################################################ + FROM scratch + # hadolint ignore=DL3067 COPY --from=runtime / / + +# Restated from the Gotenberg base image. ENV PATH=/opt/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ TZ=UTC \ LANG=C.UTF-8 \ @@ -46,13 +84,22 @@ ENV PATH=/opt/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/ LIBREOFFICE_BIN_PATH=/usr/lib/libreoffice/program/soffice.bin \ UNOCONVERTER_BIN_PATH=/usr/bin/unoconverter WORKDIR /home/gotenberg + ARG VERSION=dev LABEL org.opencontainers.image.version=$VERSION + USER gotenberg EXPOSE 3000 + HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 CMD ["/usr/bin/docshot", "-healthcheck"] ENTRYPOINT ["/usr/bin/tini", "--", "/usr/bin/docshot"] -# Untrusted input: no JavaScript, no network, no downloadFrom or webhooks. + + +################################################################################ +# Gotenberg's command line, as docshot starts it for untrusted input: +# no JavaScript, no network (public and private IPs), no downloadFrom, no webhooks. +################################################################################ + CMD ["gotenberg", \ "--chromium-disable-javascript=true", \ "--chromium-deny-public-ips=true", "--chromium-deny-private-ips=true", \