Files
Auto_Bangumi/Dockerfile
Estrella Pan 27c04f5f5d feat(update): sign update bundles (ed25519) and stop root uv sync (P1)
Closes the update-chain trust gap: the sha256 came from the same GitHub
release as the bundle (integrity, not authenticity), and the boot overlay
re-applied ab-writable content as root — including uv sync on an
attacker-controllable lockfile.

- signing: new module/update/signing.py verifies an ed25519 signature;
  public key embedded in the image at /app/ab_update_pubkey.pem (outside
  the overlay-replaceable tree, so it is the trust root). Private key lives
  only in CI (UPDATE_SIGNING_KEY secret).
- updater: discover the .zip.sig asset, verify the signature after the
  sha256 check, reject any release without a signature, retain the verified
  bundle.zip+.sig for boot re-verification, and swap them on rollback.
- boot_overlay: re-verify the signed bundle at every boot and extract the
  module tree / webui dist FROM the verified zip — never from the
  ab-writable current/ tree; read the version from the zip manifest, not
  the ab-writable applied.json; drop to the ab user before any fallback
  uv sync so lockfile build hooks can't run as root; zip-slip guard uses
  is_relative_to (also fixed in updater._safe_extract).
- CI: build.yml signs the bundle with UPDATE_SIGNING_KEY and publishes the
  .zip.sig; the build fails if the secret is unset.
- deps: cryptography made an explicit dependency.

Tests cover bad-signature reject, unsigned-release reject, legacy overlay
without a signed bundle reject, verified-bundle retention, and boot-overlay
signature verification. Full backend suite: 1079 passed.

Ops: run `gh secret set UPDATE_SIGNING_KEY < ~/.autobangumi/update-signing-key.pem`
before the 3.3 release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
2026-07-03 11:15:03 +02:00

71 lines
2.2 KiB
Docker
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# syntax=docker/dockerfile:1
FROM ghcr.io/astral-sh/uv:0.5-python3.13-alpine AS builder
WORKDIR /app
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
# Install dependencies (cached layer)
COPY backend/pyproject.toml backend/uv.lock ./
RUN uv sync --frozen --no-dev
# Copy application source
COPY backend/src ./src
FROM python:3.13-alpine AS runtime
RUN apk add --no-cache \
bash \
su-exec \
shadow \
tini \
tzdata
ENV LANG="C.UTF-8" \
TZ=Asia/Shanghai \
PUID=1000 \
PGID=1000 \
UMASK=022
WORKDIR /app
# Copy venv and source from builder
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/src .
COPY --chmod=755 entrypoint.sh /entrypoint.sh
# uv 二进制 + 基线 lock:在线自动更新落地时,若覆盖层的 uv.lock 相对基线变化,
# entrypoint 会用 uv 按锁文件把依赖同步到 /app/.venv。
COPY --from=builder /usr/local/bin/uv /usr/local/bin/uv
COPY --from=builder /app/uv.lock /app/uv.lock
# 更新签名公钥:在线更新的信任根。必须位于覆盖层不会替换的位置(/app 下、
# 而非 /app/module 内),boot_overlay 每次启动都用它验签留存的 bundle。私钥
# 只在 CI 的 UPDATE_SIGNING_KEY secret 里。它随 src 一起 COPY 进来,这里显式
# 再放一次以固化信任根位置、防止后续目录调整把它漏掉。
COPY backend/src/ab_update_pubkey.pem /app/ab_update_pubkey.pem
# 构建期写入镜像基线版本,供 boot_overlay 判断“镜像 vs 覆盖层”谁更新,以及
# 在线更新的 min_image_version 兼容性检查。CI 通过 build-arg 注入真实版本。
ARG VERSION=DEV_VERSION
RUN echo "${VERSION}" > /app/IMAGE_VERSION
# Add user
RUN mkdir -p /home/ab && \
addgroup -S ab -g 911 && \
adduser -S ab -G ab -h /home/ab -s /sbin/nologin -u 911
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 7892
VOLUME ["/app/config", "/app/data"]
# Liveness probe against the unauthenticated /health route; give the app
# room to run migrations on first boot before failures count.
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:7892/health || exit 1
ENTRYPOINT ["tini", "-g", "--", "/entrypoint.sh"]