Ctrl K
ComunidadGuías…

Dockerfile

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# syntax=docker/dockerfile:1

# Digest-pinned base image keeps builds reproducible; bump deliberately.
# T-164 round 180: the previous pin (a17d50af…) had drifted a week behind its own tag
# `node:22-bookworm-slim`, which now points at 43ac6c60… (last_updated 2026-09-23). Measured, not
# assumed: the Debian tracker lists CVE-2026-86145 / -89157 / -89161 as resolved in bookworm with
# fixed_version 10.42-1+deb12u1 (bookworm-security), while this pin shipped libpcre2-8-0 10.42-1
# from the base repo — those three HIGH findings were a missing security update, not an unfixed
# upstream. Refreshing the pin is what picks the update up.
FROM node:22-bookworm-slim@sha256:43ac6c60b8f89723f746e8a92ce91abd5017e627ce1ddfe4238355d3a30b772c AS runtime

WORKDIR /app

ENV NODE_ENV=production

LABEL org.opencontainers.image.source="https://github.com/happy520ai/unified-ai-system"
LABEL org.opencontainers.image.licenses="Apache-2.0"

# T-164 round 181: the shipped base carries libpcre2-8-0 10.42-1+deb12u1, which is inside
# CVE-2026-103111's vulnerable range (PCRE2 before 10.49, OSV 7.6 High; the 10-04 Trivy SARIF
# upload opened exactly this one alert). Measured against the Debian tracker rather than
# guessed: the fix exists in bookworm-security as 10.42-1+deb12u2 (DLA-4816-1) while bookworm
# main still ships 10.42-1, and the pinned node image (last_updated 2026-09-23) predates that
# upload -- today `node:22-bookworm-slim` still resolves to this very digest, so refreshing
# the pin cannot pick the fix up. Upgrading this one library from the security pocket is the
# fix; it is not a scanner workaround. Deliberately scoped to the single package: a blanket
# apt-get upgrade would move the rest of the OS tree away from the digest pin's promise.
# 2026-10-13: the 10-13 Trivy SARIF opened seven perl-base alerts (CVE-2026-8376, -42496, -13221
# CRITICAL; -57433, -57432, -48962, -42497 HIGH) against the base's 5.36.0-7+deb12u3; Trivy reports the
# fix as 5.36.0-7+deb12u4 in bookworm-security, so perl-base is pinned up the same targeted way.
# 2026-11-07: the 11-06 Trivy scan of the published gateway image (first scan after the #209 fix)
# opened exactly two HIGH alerts, both CVE-2026-109518 in apt / libapt-pkg6.0 2.6.1. Measured inside
# this very digest, not guessed: `apt-cache policy` in the pinned base offers 2.6.2 as the candidate
# from bookworm-security, which is exactly the fix version Trivy names, so both packages are pinned
# up the same targeted way as libpcre2 and perl-base before them.
RUN apt-get update \
  && apt-get install -y --no-install-recommends libpcre2-8-0=10.42-1+deb12u2 perl-base=5.36.0-7+deb12u4 apt=2.6.2 libapt-pkg6.0=2.6.2 \
  && rm -rf /var/lib/apt/lists/*

# 直接安装 pnpm(不经 corepack):qemu 跨架构构建下 corepack 的 tarball
# 下载会确定性失败(exit 255),npm 的网络栈不受影响;同时运行时也
# 不再有 corepack 下载横幅污染 stdout。
#
# T-164 round 180: pnpm is installed BY the image's bundled npm, so that npm's dependency tree is on
# the real build path -- which is why Trivy's `library` findings were not decorative. Measured against
# the registry rather than guessed: npm 10.9.9 already ships tar ^7.5.22 (clearing the one CRITICAL,
# CVE-2026-59873) but still pacote ^19.0.1, and CVE-2026-9496 is fixed only at pacote 21.5.1. 11.21.0
# is the smallest line that clears both (tar ^7.5.22, pacote ^21.5.1) and its engines range
# (^20.17.0 || >=22.9.0) covers the 22.23.x this base image ships. Pinned exactly, never @latest: a
# floating version would make the image non-reproducible, which is the property the digest pin above
# exists to keep.
RUN npm install -g npm@11.21.0
RUN npm install -g pnpm@11.19.0

# npm has now done the only job it has in this image. pnpm@11.19.0 declares no runtime
# dependencies at all (measured: `dependencies: {}` in its package.json), and neither runtime stage
# below invokes npm -- they run `node` against the built tree. So npm's vendored tree is carried for
# no reason, and it is the last thing Trivy was flagging: with npm 11.21.0 pinned above, brace-expansion
# 5.0.9 and undici 6.28.0 are the only HIGH/CRITICAL findings left, and BOTH npm lines that exist
# (11.21.0 and 12.2.0) bundle exactly those two versions, so no version bump can clear them. Removing
# the tool is the honest fix -- it is not a scanner workaround: the vulnerable code simply is not in
# the shipped artifact any more. If a future stage needs npm, re-add it deliberately.
RUN rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx

# pnpm 的 verify-deps-before-run 会在项目根(/app,root 属主、node 只读)
# 写 _tmp_* 哈希文件,非 root 运行 `pnpm gateway demo` 时偶发 EACCES。
# 容器内依赖由 --frozen-lockfile 在构建期锁定,运行期无需再校验。
ENV npm_config_verify_deps_before_run=false

COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/ai-gateway-service/package.json apps/ai-gateway-service/package.json
COPY apps/agent-console/package.json apps/agent-console/package.json
COPY packages packages

RUN pnpm install --frozen-lockfile \
  --filter @unified-ai-system/ai-gateway-service... \
  --filter @unified-ai-system/agent-console... \
  --filter @unified-ai-system/mcp-server...

# Same discipline as the npm strip above, applied to pnpm once its one job (the frozen install) is
# done. pnpm's published bundle carries its own vendored node_modules, and Trivy reads undici 6.28.0
# out of `pnpm/dist/node_modules` and `pnpm/artifacts/exe/dist/node_modules` -- that was the last
# HIGH/CRITICAL pair left after the base pin and the npm pin. Both runtime stages below run `node`
# against the built tree and never invoke pnpm, and the CI smoke tests invoke `node` directly too
# (tools/verify-public-clone.mjs and the docker smoke step), so nothing in this image needs it.
# Note what this does NOT claim: it does not fix pnpm's undici, it stops shipping it. The build above
# still ran pnpm at the repo's pinned version. If a future stage needs pnpm in the image, re-add it
# deliberately and say why.
RUN rm -rf /usr/local/lib/node_modules/pnpm /usr/local/bin/pnpm /usr/local/bin/pnpx /usr/local/bin/pnpm.cmd 2>/dev/null || true

# 2026-11-07: the 11-06 Trivy scan also warned that pnpm's registry METADATA cache
# (root/.cache/pnpm/v11/metadata/*.jsonl, 15 MB files like wrangler.jsonl) is baked into the
# shipped image -- build-time state with no runtime purpose that bloats every pull and feeds
# the scanner noise. The tools strips above already ended pnpm's job; its cache, its
# content-addressable store, and its state end with it. node_modules keeps working across
# this removal because pnpm hardlinks store files into the tree and the same layer holds
# both, so the inodes stay alive through node_modules after the store entries are gone.
RUN rm -rf /root/.cache/pnpm /root/.npm /root/.local/share/pnpm /root/.local/state/pnpm

COPY apps/ai-gateway-service apps/ai-gateway-service
COPY apps/agent-console apps/agent-console
COPY tools/terminal-demo.mjs tools/terminal-demo.mjs
COPY tools/mcp-smoke.mjs tools/mcp-smoke.mjs
COPY tools/build-runtime-identity.mjs tools/build-runtime-identity.mjs

# This value is a build declaration, not proof of the source revision.
# The manifest separately fingerprints the source bytes shipped in this image.
# pnpm install artifacts can carry root-only permission bits; the shipped
# source tree must stay readable by the non-root runtime user so in-container
# verification can re-hash it. Read/execute bits only - file bytes unchanged.
RUN chmod -R a+rX apps packages tools package.json pnpm-lock.yaml pnpm-workspace.yaml
ARG UAI_DECLARED_REVISION=""
RUN node tools/build-runtime-identity.mjs --declared-revision "$UAI_DECLARED_REVISION"

# 运行时状态目录(审计日志、请求日志、企业存储)归 node 所有;
# 容器内进程以非 root 运行,缺这一步会在只读 /app 上 EACCES。
RUN mkdir -p .data/audit .data/request-logs .data/enterprise .data/knowledge apps/ai-gateway-service/.data \
  && chown -R node:node .data apps/ai-gateway-service/.data

# pnpm 11 默认开启 verify-deps-before-run,且镜像里没有仓库的 .npmrc。
# 依赖在构建期已由 --frozen-lockfile 锁定,运行期复核只会以非 root 身份
# 触发重装/清库。在镜像内的 .npmrc 显式关闭(pnpm 最权威的配置源)。
RUN printf 'verify-deps-before-run=false\n' > /app/.npmrc

# Runtime state is restricted to explicit node-owned mounts. The application
# root remains root-owned so deployments can enforce a read-only rootfs.
VOLUME ["/app/.data", "/app/apps/ai-gateway-service/.data"]

FROM runtime AS mcp

LABEL org.opencontainers.image.description="Credential-free Unified AI System MCP server"
LABEL io.modelcontextprotocol.server.name="io.github.happy520ai/unified-ai-system"

USER node
CMD ["node", "packages/mcp-server/src/index.js"]

FROM runtime AS gateway

ENV AI_GATEWAY_SERVICE_HOST=0.0.0.0
ENV AI_GATEWAY_SERVICE_PORT=3100

LABEL org.opencontainers.image.description="Terminal-first, self-hosted AI gateway"

USER node
EXPOSE 3100

# Keep enterprise authentication enabled for a bare `docker run` without
# storing a secret-looking boolean in an ENV layer. An explicit runtime value
# (including `false`) wins, and `exec` preserves Node as the signal-receiving
# process.
CMD ["sh", "-c", "PME_ENTERPRISE_AUTH_ENABLED=${PME_ENTERPRISE_AUTH_ENABLED:-true} exec node apps/ai-gateway-service/src/index.js"]