diff --git a/docker/Dockerfile.fips b/docker/Dockerfile.fips new file mode 100644 index 00000000000..1aa0f893e1d --- /dev/null +++ b/docker/Dockerfile.fips @@ -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"] diff --git a/docker/fips_elf_inventory.txt b/docker/fips_elf_inventory.txt new file mode 100644 index 00000000000..758e2a4f579 --- /dev/null +++ b/docker/fips_elf_inventory.txt @@ -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 diff --git a/docker/scan_bundled_crypto.sh b/docker/scan_bundled_crypto.sh new file mode 100755 index 00000000000..fb8bd53b4ce --- /dev/null +++ b/docker/scan_bundled_crypto.sh @@ -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 -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"