feat(docker): add FIPS 140-3 image with build-time crypto verification

Dockerfile.fips builds on chainguard-base-fips with FIPS-provider-linked
python/node/openssl from the org apk repo (CMVP #5132). A fips-check
stage runs openssl-fips-test and an ELF allowlist gate: every native
binary must be in the reviewed fips_elf_inventory.txt or carry an
explicit bundled-crypto waiver, so new native deps fail the build until
reviewed. Known offenders are waived pending the dependency-fix
workstream and the report ships at /fips-scan-report.txt
This commit is contained in:
Oliver Jensen 2026-09-18 10:28:20 +02:00
parent 4b368bf066
commit e43e984b28
3 changed files with 406 additions and 0 deletions

169
docker/Dockerfile.fips Normal file
View file

@ -0,0 +1,169 @@
# syntax=docker/dockerfile:1.7
# FIPS 140-3 variant (workstream A of the FIPS roadmap). Builds on
# chainguard-base-fips so python/node/openssl resolve to Chainguard's
# FIPS-provider-linked packages from the image's pinned virtualapk repo.
# Build requires a Chainguard token:
# CGR_TOKEN=$(chainctl auth token --audience apk.cgr.dev) \
# docker build --secret id=cgr-token,env=CGR_TOKEN -f docker/Dockerfile.fips .
ARG LITELLM_BUILD_IMAGE=cgr.dev/berri.ai/chainguard-base-fips@sha256:4f23c1a31be6a0778c4851c54474c7630b2feda7d113c7b1b5e7b1def8c89b04
ARG LITELLM_RUNTIME_IMAGE=cgr.dev/berri.ai/chainguard-base-fips@sha256:4f23c1a31be6a0778c4851c54474c7630b2feda7d113c7b1b5e7b1def8c89b04
ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a
# UI builder only emits static JS assets; no binary from it reaches the final
# image, so it does not need a FIPS base.
ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43
# Checksum from https://www.pgbouncer.org/downloads/
ARG PGBOUNCER_VERSION=1.25.2
ARG PGBOUNCER_SHA256=924ad35113fd0a71c8e2dbe85b5d03445532e2b7b37a9f8a48983beea238b332
FROM $UV_IMAGE AS uvbin
FROM $LITELLM_BUILD_IMAGE AS pgbouncer-builder
ARG PGBOUNCER_VERSION
ARG PGBOUNCER_SHA256
USER root
# openssl-dev here is the FIPS-configured system OpenSSL, so pgbouncer links
# libcrypto.so.3 and inherits the FIPS provider at runtime.
RUN --mount=type=secret,id=cgr-token \
HTTP_AUTH="basic:virtualapk.cgr.dev:user:$(cat /run/secrets/cgr-token)" \
apk add --no-cache build-base pkgconf libevent-dev openssl-dev curl
WORKDIR /build
RUN curl -fsSL -o pgbouncer.tar.gz "https://www.pgbouncer.org/downloads/files/${PGBOUNCER_VERSION}/pgbouncer-${PGBOUNCER_VERSION}.tar.gz" && \
echo "${PGBOUNCER_SHA256} pgbouncer.tar.gz" | sha256sum -c - && \
tar xzf pgbouncer.tar.gz --strip-components=1 && \
./configure --prefix=/usr/local --with-openssl=/usr && \
make -j"$(nproc)" pgbouncer && \
install -m 0755 pgbouncer /usr/local/bin/pgbouncer
FROM --platform=$BUILDPLATFORM $UI_BUILD_IMAGE AS ui-builder
ENV NEXT_TELEMETRY_DISABLED=1 \
npm_config_fund=false \
npm_config_audit=false
WORKDIR /ui
COPY ui/litellm-dashboard/package.json ui/litellm-dashboard/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --prefer-offline
COPY ui/litellm-dashboard/ ./
RUN npm run build
FROM $LITELLM_BUILD_IMAGE AS builder
WORKDIR /app
USER root
COPY --from=uvbin /uv /usr/local/bin/uv
COPY --from=uvbin /uvx /usr/local/bin/uvx
RUN --mount=type=secret,id=cgr-token \
HTTP_AUTH="basic:virtualapk.cgr.dev:user:$(cat /run/secrets/cgr-token)" \
apk add --no-cache \
bash \
gcc \
python-3.13 \
python-3.13-dev \
openssl \
openssl-dev \
nodejs \
npm \
libsndfile
# UV_PYTHON_DOWNLOADS=0: uv must never substitute a standalone CPython with
# its own bundled OpenSSL for the FIPS-linked system python.
ENV UV_PROJECT_ENVIRONMENT=/app/.venv \
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=0 \
PATH="/app/.venv/bin:${PATH}"
COPY pyproject.toml uv.lock ./
COPY enterprise/pyproject.toml enterprise/
COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/
RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \
--extra proxy \
--extra proxy-runtime \
--extra extra_proxy \
--extra semantic-router \
--extra saml \
--extra bedrock-realtime \
--python python3.13
COPY . .
RUN rm -rf litellm/proxy/_experimental/out
COPY --from=ui-builder /ui/out/. litellm/proxy/_experimental/out/
RUN sed -i 's/\r$//' docker/build_admin_ui.sh && chmod +x docker/build_admin_ui.sh && ./docker/build_admin_ui.sh
RUN uv sync --frozen --no-default-groups --no-editable \
--extra proxy \
--extra proxy-runtime \
--extra extra_proxy \
--extra semantic-router \
--extra saml \
--extra bedrock-realtime \
--python python3.13
RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \
npm_config_cache=/root/.npm \
prisma generate --schema=./schema.prisma
RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \
sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh
FROM $LITELLM_RUNTIME_IMAGE AS runtime
USER root
# Unlike the non-FIPS images, the public Wolfi repo is deliberately NOT added
# here: apk in this image may only resolve FIPS-linked packages from the
# pinned Chainguard repo.
RUN --mount=type=secret,id=cgr-token \
HTTP_AUTH="basic:virtualapk.cgr.dev:user:$(cat /run/secrets/cgr-token)" \
apk add --no-cache bash openssl openssl-fips-test tzdata nodejs python-3.13 libsndfile libevent
COPY --from=pgbouncer-builder /usr/local/bin/pgbouncer /usr/local/bin/pgbouncer
WORKDIR /app
ENV PATH="/app/.venv/bin:${PATH}" \
PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \
PRISMA_CLI_PATH=/opt/prisma/binaries/node_modules/.bin/prisma \
PRISMA_CLI_QUERY_ENGINE_TYPE=binary \
PRISMA_OFFLINE_MODE=true
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/docker /app/docker
COPY --from=builder /app/schema.prisma /app/schema.prisma
COPY --from=builder /app/litellm/proxy/prisma_migration.py /app/litellm/proxy/prisma_migration.py
COPY --from=builder /app/enterprise /app/enterprise
COPY --from=builder /app/litellm-proxy-extras /app/litellm-proxy-extras
COPY --from=builder /opt/prisma /opt/prisma
RUN find /app/.venv -type f -path "*/tornado/test/*" -delete && \
find /app/.venv -type d -path "*/tornado/test" -delete && \
chmod -R a+rX /opt/prisma && \
test -x /opt/prisma/binaries/node_modules/.bin/prisma && \
test -f /opt/prisma/binaries/node_modules/prisma/build/index.js && \
python -c "from prisma.client import BINARY_PATHS; paths = list(BINARY_PATHS.query_engine.values()); assert paths and all(p.startswith('/opt/prisma/') for p in paths), paths"
# Build-time FIPS assertions. binutils (objdump) is installed only in this
# throwaway stage; the final image below is FROM runtime and never sees it.
FROM runtime AS fips-check
RUN --mount=type=secret,id=cgr-token \
HTTP_AUTH="basic:virtualapk.cgr.dev:user:$(cat /run/secrets/cgr-token)" \
apk add --no-cache binutils
RUN openssl-fips-test
RUN chmod +x docker/scan_bundled_crypto.sh && \
docker/scan_bundled_crypto.sh /app/.venv /opt/prisma /usr/local/bin
FROM runtime AS final
# Forces the fips-check stage to build and records its evidence in the image.
COPY --from=fips-check /fips-scan-report.txt /fips-scan-report.txt
EXPOSE 4000/tcp
ENTRYPOINT ["docker/prod_entrypoint.sh"]
CMD ["--port", "4000"]

View file

@ -0,0 +1,124 @@
/app/.venv/lib/python3.13/site-packages/81d243bd2c585b0f4821__mypyc.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/_awscrt.abi3.so
/app/.venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/_polars_runtime_32/_polars_runtime.abi3.so
/app/.venv/lib/python3.13/site-packages/aiohttp/_http_parser.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/aiohttp/_http_writer.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/aiohttp/_websocket/mask.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/aiohttp/_websocket/reader_c.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/charset_normalizer/cd.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/charset_normalizer/md.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so
/app/.venv/lib/python3.13/site-packages/ddtrace/appsec/_ddwaf/libddwaf/ARCH/lib/libddwaf.so
/app/.venv/lib/python3.13/site-packages/ddtrace/appsec/_iast/_ast/iastpatch.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/appsec/_iast/_taint_tracking/_native.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/appsec/_shared/_stacktrace.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/_encoding.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/_tagset.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/_threads.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/datadog/profiling/ddup/_ddup.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/datadog/profiling/libdd_wrapper.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/datadog/profiling/stack/_stack.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/native/_native.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/internal/telemetry/metrics_namespaces.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/_threading.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/collector/_exception.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/collector/_fast_poisson.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/collector/_lock.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/collector/_memalloc.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/collector/_sampler.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/profiling/collector/_task.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/vendor/psutil/_psutil_linux.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ddtrace/vendor/psutil/_psutil_posix.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/fastuuid/fastuuid.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/frozenlist/_frozenlist.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/google/_upb/_message.abi3.so
/app/.venv/lib/python3.13/site-packages/google_crc32c.libs/libcrc32c.so.1.1.0
/app/.venv/lib/python3.13/site-packages/google_crc32c/_crc32c.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/granian/_granian.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/grpc/_cython/cygrpc.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/hf_xet/hf_xet.abi3.so
/app/.venv/lib/python3.13/site-packages/hiredis/hiredis.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ijson.libs/libyajl.so.2.1.1
/app/.venv/lib/python3.13/site-packages/ijson/backends/_yajl2.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/jiter/jiter.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/litellm/rust_bridge/_native.abi3.so
/app/.venv/lib/python3.13/site-packages/lxml/_elementpath.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/lxml/builder.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/lxml/etree.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/lxml/html/_difflib.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/lxml/html/diff.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/lxml/objectify.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/lxml/sax.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/markupsafe/_speedups.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/ml_dtypes/_ml_dtypes_ext.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/multidict/_multidict.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/nacl/_sodium.abi3.so
/app/.venv/lib/python3.13/site-packages/numpy.libs/libgfortran-daac5196.so.5.0.0
/app/.venv/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_multiarray_tests.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_multiarray_umath.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_operand_flag_tests.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_rational_tests.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_simd.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_struct_ufunc_tests.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/_core/_umath_tests.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/fft/_pocketfft_umath.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/linalg/_umath_linalg.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/linalg/lapack_lite.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_common.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_generator.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_mt19937.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_pcg64.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_philox.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/_sfc64.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/bit_generator.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/numpy/random/mtrand.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/orjson/orjson.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/propcache/_helpers_c.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libcom_err.so.2.1
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libcrypt.so.1.1.0
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libcrypto.so.1.1.1k
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libcrypto.so.3
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libgssapi_krb5.so.2.2
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libk5crypto.so.3.1
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libkeyutils.so.1.6
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libkrb5.so.3.3
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libkrb5support.so.0.1
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/liblber.so.2.0.200
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libldap.so.2.0.200
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libpcre2-8.so.0.7.1
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libpq.so.5.18
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libsasl2.so.3.0.0
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libselinux.so.1
/app/.venv/lib/python3.13/site-packages/psycopg_binary.libs/libssl.so.3
/app/.venv/lib/python3.13/site-packages/psycopg_binary/_psycopg.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/psycopg_binary/pq.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/pyroscope/_native__lib.cpython-39-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/regex/_regex.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/rpds/rpds.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/tiktoken/_tiktoken.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/tokenizers/tokenizers.abi3.so
/app/.venv/lib/python3.13/site-packages/tornado/speedups.abi3.so
/app/.venv/lib/python3.13/site-packages/uvloop/loop.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/websockets/speedups.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/wrapt/_wrappers.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/xmlsec.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/yaml/_yaml.cpython-313-ARCH-linux-gnu.so
/app/.venv/lib/python3.13/site-packages/yarl/_quoting_c.cpython-313-ARCH-linux-gnu.so
/opt/prisma/.cache/prisma/master/ac9d7041ed77bcc8a8dbd2ab6616b39013829574/debian-openssl-1.1.x/query-engine
/opt/prisma/.cache/prisma/master/ac9d7041ed77bcc8a8dbd2ab6616b39013829574/debian-openssl-3.0.x/query-engine
/opt/prisma/.cache/prisma/master/ac9d7041ed77bcc8a8dbd2ab6616b39013829574/linux-arm64-openssl-3.0.x/query-engine
/opt/prisma/.cache/prisma/master/ac9d7041ed77bcc8a8dbd2ab6616b39013829574/linux-arm64-openssl-3.0.x/schema-engine
/opt/prisma/.cache/prisma/master/ac9d7041ed77bcc8a8dbd2ab6616b39013829574/linux-musl-openssl-3.0.x/query-engine
/opt/prisma/.cache/prisma/master/ac9d7041ed77bcc8a8dbd2ab6616b39013829574/linux-musl/query-engine
/opt/prisma/binaries/node_modules/@prisma/engines/query-engine-linux-arm64-openssl-3.0.x
/opt/prisma/binaries/node_modules/@prisma/engines/schema-engine-linux-arm64-openssl-3.0.x
/opt/prisma/binaries/node_modules/prisma/query-engine-debian-openssl-1.1.x
/opt/prisma/binaries/node_modules/prisma/query-engine-debian-openssl-3.0.x
/opt/prisma/binaries/node_modules/prisma/query-engine-linux-arm64-openssl-3.0.x
/opt/prisma/binaries/node_modules/prisma/query-engine-linux-musl
/opt/prisma/binaries/node_modules/prisma/query-engine-linux-musl-openssl-3.0.x
/usr/local/bin/pgbouncer

113
docker/scan_bundled_crypto.sh Executable file
View file

@ -0,0 +1,113 @@
#!/bin/sh
# Build-time gate for the FIPS image. Allowlist logic: every ELF in the final
# layers must be accounted for, either present in fips_elf_inventory.txt
# (reviewed as crypto-free or linked against the validated system
# libcrypto.so.3) or waived below when it bundles its own crypto
# implementation. A binary that is neither fails the build, so a new native
# dependency cannot ship without review, whatever crypto it embeds.
#
# SCAN_EMIT_INVENTORY=1 prints normalized ELF paths instead of gating.
# Regenerate the inventory after a reviewed dependency change with:
# docker run --rm -v "$PWD/docker:/scan" --entrypoint sh <image> -c \
# 'SCAN_EMIT_INVENTORY=1 /scan/scan_bundled_crypto.sh /app/.venv /opt/prisma /usr/local/bin' \
# > docker/fips_elf_inventory.txt
set -eu
script_dir="$(cd "$(dirname "$0")" && pwd)"
inventory="${SCAN_INVENTORY:-$script_dir/fips_elf_inventory.txt}"
report="${SCAN_REPORT:-/fips-scan-report.txt}"
failures="$(mktemp)"
counts="$(mktemp)"
normalize() {
printf '%s\n' "$1" | sed \
-e 's/aarch64/ARCH/g' \
-e 's/x86_64/ARCH/g' \
-e 's/-[0-9a-f]\{8,\}\(\.so\)/\1/'
}
is_elf() {
[ "$(head -c 4 "$1" 2>/dev/null)" = "$(printf '\177ELF')" ]
}
crypto_markers() {
m=""
if grep -aq 'BoringSSL' "$1"; then m="BoringSSL"; fi
if grep -aq 'AWS-LC' "$1"; then m="${m:+$m,}AWS-LC"; fi
if grep -Eaq 'OpenSSL [0-9]+\.[0-9]+\.[0-9]+' "$1"; then
if ! objdump -p "$1" 2>/dev/null | grep -q 'NEEDED.*libcrypto\.so\.3'; then
m="${m:+$m,}bundled-OpenSSL"
fi
fi
if grep -aq 'sodium_init' "$1"; then m="${m:+$m,}libsodium"; fi
if grep -aq 'ring_core_' "$1"; then m="${m:+$m,}ring"; fi
if grep -aq 'rustls' "$1"; then m="${m:+$m,}rustls"; fi
if grep -Eaqi 'mbed.?tls' "$1"; then m="${m:+$m,}mbedTLS"; fi
if grep -aq 'wolfSSL' "$1"; then m="${m:+$m,}wolfSSL"; fi
if grep -aq 'libgcrypt' "$1"; then m="${m:+$m,}libgcrypt"; fi
printf '%s' "$m"
}
is_waived() {
case "$1" in
*/cryptography/hazmat/bindings/_rust.abi3.so) ;;
*/grpc/_cython/cygrpc*.so) ;;
*/hf_xet/hf_xet.abi3.so) ;;
*/_awscrt.abi3.so) ;;
*/pyroscope/_native__lib*.so) ;;
*/psycopg_binary.libs/libcrypto-*) ;;
*/nacl/_sodium*.so|*/libsodium*) ;;
*/litellm/rust_bridge/*|*/granian*) ;;
*/xmlsec*|*xmlsec.libs*) ;;
*/_polars_runtime_32/_polars_runtime.abi3.so) ;;
*/ddtrace/internal/native/_native*.so) ;;
/opt/prisma/*query-engine*|/opt/prisma/*schema-engine*) ;;
*) return 1 ;;
esac
}
if [ "${SCAN_EMIT_INVENTORY:-}" = "1" ]; then
find "$@" -type f 2>/dev/null | while read -r f; do
if is_elf "$f"; then
normalize "$f"
fi
done | sort -u
exit 0
fi
if [ ! -f "$inventory" ]; then
echo "FAIL: inventory file not found at $inventory" >&2
exit 1
fi
: > "$report"
find "$@" -type f 2>/dev/null | sort | while read -r f; do
if ! is_elf "$f"; then
continue
fi
echo scanned >> "$counts"
norm="$(normalize "$f")"
markers="$(crypto_markers "$f")"
if [ -n "$markers" ]; then
printf 'BUNDLED-CRYPTO %s [%s]\n' "$f" "$markers" >> "$report"
if is_waived "$f"; then
printf 'WAIVED %s [%s]\n' "$f" "$markers"
else
printf 'VIOLATION %s [%s] bundled crypto with no waiver\n' "$f" "$markers"
echo "$f" >> "$failures"
fi
elif grep -Fxq "$norm" "$inventory"; then
printf 'CLEAN %s\n' "$f" >> "$report"
else
printf 'VIOLATION %s [unreviewed] not in fips_elf_inventory.txt; review it, then regenerate the inventory\n' "$f"
echo "$f" >> "$failures"
fi
done
scanned="$(grep -c scanned "$counts" || true)"
if [ -s "$failures" ]; then
printf 'FAIL: %s of %s ELF binaries unaccounted for (see above)\n' "$(wc -l < "$failures" | tr -d ' ')" "$scanned"
exit 1
fi
printf 'scanned %s ELF binaries\n' "$scanned" >> "$report"
printf 'PASS: all %s ELF binaries accounted for (inventoried, system-linked, or waived)\n' "$scanned"