Files
truf-server/docker/build-dependencies/README.md
T
2026-09-30 20:30:56 +03:00

274 lines
16 KiB
Markdown

# Runtime Dependency Build
This directory records the public build inputs and pip-tools generator. It is not
an application configuration directory and must never contain credentials.
## Pins
| Input | Pin |
| --- | --- |
| Python image | `python:3.12-slim-bookworm@sha256:782412e85d0f0984994c290652577d4018aff08145c85b262bb63dc0c7522254` |
| Python version | `3.12.14` |
| Linux amd64 manifest | `sha256:9c47360a2a0355e2da18516d0b1c2126ec22c195d2185e97347c9d98398c5bef` |
| Linux arm64/v8 manifest | `sha256:d04f49f5882f49a3b91f874e75e19f0c265f7222da8659741a9d7eab148f22a9` |
| Debian and Debian security snapshots | `20260914T000000Z` |
| Git and git-man | `1:2.39.5-0+deb12u3` |
| tini | `0.19.0-1+b3` |
| CA certificates (already present in the pinned base) | `20250419~deb12u1` |
| PGDG server, client, libpq | `16.15-1.pgdg12+2` |
| PGDG common and client-common | `293.pgdg12+1` |
| PGDG signing-key fingerprint | `B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8` |
| PGDG signing-key SHA-256 | `0144068502a1eddd2a0280ede10ef607d1ec592ce819940991203941564e8e76` |
| TruffleHog | `3.97.4` |
| TruffleHog Linux amd64 archive SHA-256 | `dc24007c2f233bd61c05beabeb44aa27ea9b43288166279209abe0458c5ce76b` |
| TruffleHog Linux amd64 archive size | `34970205` bytes |
| TruffleHog Linux arm64 archive SHA-256 | `7e65e771d2a247964056aa5edba0f8ae3945895e5dce867fe0ffbc7b0128239a` |
| TruffleHog Windows amd64 archive SHA-256 | `6ce9a957ac62bfb19463048333d9e8481327dbbf5bdc0c43f5ab5327b9631fb9` |
| Windows embeddable Python | `3.12.10`, SHA-256 `4acbed6dd1c744b0376e3b1cf57ce906f9dc9e95e68824584c8099a63025a3c3` |
| Windows MinGit | `2.47.1.windows.1`, SHA-256 `50b04b55425b5c465d076cdb184f63a0cd0f86f6ec8bb4d5860114a713d2c29a` |
| pip-tools | `7.6.1` |
| Generator pip | `26.2.1` |
| pytest | `8.4.2` |
| httpx (test target only) | `0.28.1` |
| zstandard | `0.23.0` |
| pandas | `3.0.5` |
| plotly | `7.0.0` |
| streamlit | `1.63.0` |
| psycopg and psycopg-binary | `3.3.5` |
| boto3 and botocore | `1.43.94` |
TruffleHog's expected hashes were checked against the public release's
[`trufflehog_3.97.4_checksums.txt`](https://github.com/trufflesecurity/trufflehog/releases/download/v3.97.4/trufflehog_3.97.4_checksums.txt).
The PGDG key's primary OpenPGP fingerprint was independently calculated from the
hash-pinned public key and matches the fingerprint above.
The Dockerfile pins the base index, download digests, PGDG package versions, and
Debian snapshot. Apt verifies Debian signatures with its shipped archive keyring
and PGDG signatures with the separately hash-pinned, repository-scoped armored
key. Full GnuPG is not installed. Expired `Valid-Until` checks are disabled only
for the immutable Debian snapshots, never signature verification. The official
PGDG archive retains older package versions; apt preferences exclude every PGDG
package except the five exact pins above.
The complete Debian Git package and HTTPS helper are retained. Native executable
symlinks in the Python/Git tool directories, and PG client version-wrapper links,
are replaced with root-owned regular hard links. Dependencies stay in the
interpreter's `/usr/local/lib/python3.12/site-packages`, not a virtual environment
or user site. Installation requires hashes and binary wheels and disables
bytecode generation. The application lock includes both manifests, optional
dashboard packages, and pytest in one environment. The test target layers its
separate hash lock containing Starlette TestClient's `httpx` dependency; the
runtime target does not contain `httpx`. The separate worker lock contains only
Requests, PyYAML, zstandard, and their four transitives; it excludes PostgreSQL,
server, dashboard, test, and detailed-keycheck dependencies.
`docker/worker-package-pins.json` is the canonical public build-input record for
Linux and Windows worker artifacts. Worker manifests hash every application,
dependency, scanner, detector-policy, Python-runtime, and complete Git-runtime
file. The package grants no provider or detailed keycheck authority and contains
no server or database credentials.
PostgreSQL service starts and automatic cluster creation are disabled during
installation. No database is initialized by the build. Generated distribution
snakeoil TLS keys are removed in the same layer. Package pins make dependency
selection repeatable; this is not a claim of byte-for-byte identical OCI images
across BuildKit versions or package-maintainer timestamp generation.
## Generate Locks
All four requirements lock files are generated by pip-tools on Linux with the pinned
Python 3.12.14 interpreter. Do not edit any generated lock by hand. The initial
compiler bootstrap installs only public `pip==26.2.1` and `pip-tools==7.6.1`, then
resolves and hashes the compiler's entire dependency closure. Subsequent compiler
installs use that generated hash lock.
The generator also copies the existing locks, so normal regeneration retains
valid pins. Use pip-compile's `--upgrade` only for an intentional dependency
refresh, then rebuild and revalidate the dependency image.
Run these Docker commands from the isolated source checkout (`D:\truf-workers` for
this change), using WSL's
`sudo -n docker -H unix:///var/run/docker.sock` in place of `docker` on this host.
The generator receives only the three public application manifests and compiler
inputs. There are no host bind mounts.
```sh
docker build --target lock-generator -t truf-lock-generator:py3.12.14 .
docker run --name truf-runtime-lock truf-lock-generator:py3.12.14
docker cp truf-runtime-lock:/src/docker/requirements.lock docker/requirements.lock
docker rm truf-runtime-lock
docker run --name truf-test-lock truf-lock-generator:py3.12.14 \
python3 -m piptools compile --generate-hashes --allow-unsafe \
--resolver=backtracking --strip-extras --no-emit-index-url \
--no-emit-trusted-host --index-url=https://pypi.org/simple \
--pip-args=--only-binary=:all: \
--output-file=docker/requirements-test.lock docker/requirements-test.in
docker cp truf-test-lock:/src/docker/requirements-test.lock docker/requirements-test.lock
docker rm truf-test-lock
docker run --name truf-worker-lock truf-lock-generator:py3.12.14 \
python3 -m piptools compile --generate-hashes --allow-unsafe \
--resolver=backtracking --strip-extras --no-emit-index-url \
--no-emit-trusted-host --index-url=https://pypi.org/simple \
--pip-args=--only-binary=:all: \
--output-file=docker/requirements-worker.lock docker/requirements-worker.in
docker cp truf-worker-lock:/src/docker/requirements-worker.lock docker/requirements-worker.lock
docker rm truf-worker-lock
docker run --name truf-compiler-lock truf-lock-generator:py3.12.14 \
python3 -m piptools compile --generate-hashes --allow-unsafe \
--resolver=backtracking --strip-extras --no-emit-index-url \
--no-emit-trusted-host --index-url=https://pypi.org/simple \
--pip-args=--only-binary=:all: \
--output-file=docker/build-dependencies/requirements.lock \
docker/build-dependencies/requirements.in
docker cp truf-compiler-lock:/src/docker/build-dependencies/requirements.lock docker/build-dependencies/requirements.lock
docker rm truf-compiler-lock
```
## Build Targets
```sh
docker build --target dependencies -t truf-dependencies:py3.12.14-pg16.15-th3.97.4 .
docker build --target runtime -t truf-runtime:local .
docker build --target test -t truf-test:local .
docker build --target worker -t truf-remote-worker:linux-x86_64 .
```
## Remote Worker Artifacts
The `worker` target is independent of the server runtime. It uses the pinned image
Python and includes the worker authority, required shared DB-free modules, worker
dependency lock, detector policy, TruffleHog, CA roots, tini, and a package-local
complete Git helper tree. The final build executes the scanner and Git version
checks and verifies the full schema-3/protocol-2 capability package as unprivileged UID 10001. PostgreSQL
tools, server runtime/control authority, test code, `httpx`, provider and detailed
keycheck authority, server credentials, and database credentials are absent.
Build the Windows amd64 portable directory and deterministic ZIP from the same
isolated checkout:
```powershell
New-Item -ItemType Directory -Path dist -Force | Out-Null
python -B app/worker_package_builder.py windows `
--project-root . `
--output dist/truf-worker-windows-x86_64 `
--archive dist/truf-worker-windows-x86_64.zip `
--cache build/worker-cache
```
The builder downloads the hash-and-size-pinned Python 3.12.10 embeddable runtime,
MinGit 2.47.1, and TruffleHog 3.97.4, installs the cross-platform worker lock with
hash checking, verifies the complete package, and writes adjacent release JSON.
After extracting the ZIP, run `prepare-worker.ps1` once to replace inherited ACLs,
then use `run-worker.cmd`. Local files rely on private OS ACLs rather than
application-layer encryption; the client has no TLS-verification bypass.
Build a complete distributable release (Windows ZIP, Linux image archive,
Docker bundle, manifests, README, Compose file, and SHA-256 lists) with one
PowerShell command:
```powershell
.\build_worker_release.ps1 -ReleaseName release-YYYYMMDD-vN
```
The script uses native Docker when available and otherwise uses the Docker Engine
in `Ubuntu-24.04` through WSL. Use `-WslDistro NAME` for another distribution.
The destination must not already exist; a failed build remains on disk for
inspection and is never published as a partial replacement.
While the main context allowlist is pending, the dependency build can use this
explicit two-file context from WSL. It does not send any application code, secret
files, findings, state, or original checkout directories to the builder:
```sh
tar -C /mnt/d/truf-docker -cf - Dockerfile docker/requirements.lock \
| sudo -n docker -H unix:///var/run/docker.sock build --file Dockerfile \
--target dependencies -t truf-dependencies:py3.12.14-pg16.15-th3.97.4 -
```
`dependencies` stops before copying application code. `runtime-base` holds the
production filesystem and launch configuration; `test` inherits those exact
contents and adds private test sources. The last/default target, `runtime`, is a
direct alias of `runtime-base` and contains no test tree. The test entrypoint uses
the same interpreter isolation flags and the real `child_bootstrap.py` dependency
path loader, without processing `.pth` files or starting the application.
Application and test copies are owned by `10001:10001`; their directories are
`0700` and regular files `0600`. Build-time checks reject symlinks, special files,
and cached bytecode in these controlled copies. No recursive permission changes
are made to host paths or runtime-mounted data. The default user is `10001:10001`;
the image precreates private `/data` and `/data/home` directories.
## Verified Results
Validated on 2026-09-15 using Ubuntu 24.04 under WSL2, stock Docker Engine 29.8.0,
the local Unix socket, and `sudo -n`. Builds and generation were polled without
printing full dependency logs. A temporary WSL keepalive prevented idle shutdown
during detached lock generation; no host configuration changes were made.
| Result | Value |
| --- | --- |
| Dependency image tag | `truf-dependencies:py3.12.14-pg16.15-th3.97.4` |
| Local dependency image ID | `sha256:3fbdc2ea6fd1199aa742f54fb653e433d79ad3f1a5e4bfeed8b413dc9f704eb5` |
| Built and executed platform | `linux/amd64` |
| Runtime lock | 49 exact package pins, 1059 SHA-256 wheel hashes |
| Runtime lock SHA-256 | `82c69394b116fd8a762c3dea481682045af87c013974fcb78ac0529892188537` |
| Compiler lock | 8 exact package pins, 8 SHA-256 wheel hashes |
| Compiler lock SHA-256 | `0172b08004c6f2b0702ea9a472300cc63243492df2d3d782fd4dbaa612497fd2` |
| Lock replay in the hash-locked generator image | Both files byte-for-byte identical |
| `pip check` | No broken requirements |
- The `dependencies` and `lock-generator` targets built successfully with hash-required, binary-wheel-only installs.
- All 49 package import checks passed normally, then with `-I -S -B` through the actual `child_bootstrap.py` loader for all 10 child kinds. Application entrypoints and providers were not executed.
- Isolated `sys.path` contained only the interpreter ZIP path, standard library, `lib-dynload`, and `/usr/local/lib/python3.12/site-packages`. Neither the working directory nor the user site was added.
- 14,202 dependency-tree permission checks found root ownership, no group/world write access, and no symlinks or bytecode files. The unprivileged UID could not write these dependencies.
- Eleven selected native executables were regular root-owned files. Version and `ldd` checks passed for Python, Git, the Git HTTPS helper, tini, TruffleHog, and the six PG16 tools. TruffleHog is static; other checked ELF binaries had no missing shared libraries.
- TruffleHog global, Git, filesystem, Docker, and Hugging Face help flags were checked, including the scanner's legacy `--local-dev` and `--log-level` options. No scans or provider requests were made.
- No PostgreSQL `PG_VERSION` file or initialized cluster was present. No PostgreSQL server, application, or provider was started. Full GnuPG is absent.
- Minimal bootstrap-only runtime and test fixtures exercised the actual copy stages: `10001:10001`, directories `0700`, files `0600`. Generated in-memory contexts containing a symlink or `.pyc` file were rejected by the build.
- The test target's real tini/Python/bootstrap entrypoint reported `pytest 8.4.2` with networking disabled, a read-only root, and a UID-10001 private `/tmp` tmpfs. Pytest capture needs writable temporary storage even for `--version`.
The temporary runtime/test fixtures were deliberately incomplete and were used
only for dependency and filesystem-policy verification. They are not deployable
application images. No application test suite, PostgreSQL initialization test,
provider integration, or arm64 build was run. The arm64 base and TruffleHog assets
are pinned, but that platform still requires a native or emulated build/test.
## Integration Boundary
The main integration owns `.dockerignore`, `app/container_runtime.py`, Compose,
and tests. The context allowlist must include the exact Dockerfile, dependency
input/lock paths, this metadata file, the new runtime entrypoint, and the intended
test files. Do not replace the deny-by-default context rules with directory-wide
or wildcard exceptions. Keep `.env`, credentials, findings, original runtime
directories, and generated caches excluded.
New exact metadata/input exceptions required in the main-owned `.dockerignore`:
```text
!docker/requirements.in
!docker/requirements.lock
!docker/build-dependencies/requirements.in
!docker/build-dependencies/requirements.lock
!docker/build-dependencies/README.md
```
The Dockerfile is already allowlisted. Main must separately allowlist its
`app/container_runtime.py` and intended test source files once they exist. The
test target copies only `app/` and `tests/`; repository tests that read root-level
Compose, Docker, or PowerShell fixtures still need main-owned fixture integration.
Compose must select the runtime target, enforce a read-only root filesystem,
provide newly initialized Linux writable volumes and a temporary filesystem as
needed, and preserve the unprivileged UID/GID. Runtime startup, PostgreSQL
initialization, provider execution, and existing-data integration are explicitly
outside dependency-build validation.
For a read-only test image, provide private temporary storage, for example
`--tmpfs /tmp:rw,nosuid,nodev,noexec,size=64m,mode=0700,uid=10001,gid=10001` for
version/import checks. Main must choose temporary-storage size and execution
policy appropriate for its full tests. After the entrypoint, allowlist, fixtures,
and Compose changes are integrated, rebuild complete `runtime` and `test` images
and perform the separately authorized integration checks. No files outside the
Dockerfile and `docker/` build inputs were edited, and nothing was staged,
committed, or pushed.