-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContainerfile
More file actions
207 lines (189 loc) · 11.8 KB
/
Copy pathContainerfile
File metadata and controls
207 lines (189 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# syntax=docker/dockerfile:1.26.0@sha256:ecfaec9ed6d810b56388c508f4121597bfbba70d41a6dfeee4d8cad5f295fc32
# check=skip=UndefinedVar,UserExist
# The frontend above is pinned BY DIGEST, like every other image here, and for
# the same reason. It is the component that parses this file and compiles it
# into LLB, so the unpinned `docker/dockerfile:1` this file used to carry handed
# the interpretation of every COPY, RUN, --network=none and export directive to
# whatever version Docker Hub happened to be serving that day. That tag really
# does float, measured rather than assumed: on 2026-07-31 `docker/dockerfile:1`
# resolved to sha256:87999aa3..., a DIFFERENT image from the 1.26.0 pinned here.
# A frontend release can change emitted LLB, hence layer construction, hence the
# OCI manifest digest this recipe publishes as a constant.
#
# (As it happens both frontends produce the same artifacts for this file: builds
# on 87999aa3 and on ecfaec9e agree to the byte, on the binary and on the OCI
# tar. That is luck about two particular versions, not a property to rely on.)
#
# The sibling caution-zaino recipes still use the floating tag; that is a flaw
# inherited from them, not a precedent to follow. The directive cannot simply be
# deleted, because the `check=` line below needs frontend 1.8+. Bump this digest
# deliberately, with a hash re-baseline, and update deploy/README.md's
# determinism-ingredient list when you do.
# Reproducible StageX build of zero-indexer-shim: one static-musl binary.
#
# Sibling of deploy/caution-zaino/combined/Containerfile, which does the same
# job for zebrad + zainod. Same determinism ingredients (StageX bases pinned by
# digest, SOURCE_DATE_EPOCH=1, codegen-units=1, crt-static, --build-id=none,
# committed lockfile via --locked/--frozen, no BuildKit cache mounts), but with
# every rocksdb / libzcash_script workaround SUBTRACTED, because the shim's
# dependency graph contains neither.
#
# WHY THIS EXISTS: to make the build DETERMINISTIC, so a given commit yields a
# given hash on any machine.
#
# Corrected 2026-08-19: this used to say the auditor's job was to match that hash
# "against the hash bound into the enclave attestation". No attestation this
# platform produces contains a binary hash -- the Containerfile deploy path
# passes `None` for the manifest's `binary` field -- so that comparison was not
# available to anyone. The enclave is bound to the code by `caution verify`,
# which rebuilds the EIF from the manifest's app-source repo and compares
# PCR0/1/2 plus the TLS certificate binding. Reproducibility is the precondition
# that makes that comparison meaningful, not the comparison itself. Without it an
# attestation proves only that SOME enclave is running, which collapses the whole
# design back into trusting whoever compiled it.
#
# Build context = a partial mirror of the zero repo (assemble via assemble.sh):
# zeronym/shim/ the crate, including this file under deploy/
# zebra/Cargo.toml workspace root zebra-chain inherits from
# zebra/zebra-chain/ the vendored Zcash parser (the path dep)
# zebra/zebra-test/ optional dep of zebra-chain, manifest only
# zaino/Cargo.toml workspace root zaino-proto inherits from
# zaino/packages/zaino-proto/ the CompactTxStreamer codegen (the path dep)
# zeronym/vendor/nym-upgrade-mode-check/ the crypto-common [patch] target
# The layout is the repo's own, so the shim's `../../zebra/zebra-chain`,
# `../../zaino/packages/zaino-proto` and `../vendor/nym-upgrade-mode-check` path
# references resolve unchanged. No manifest is edited, anywhere.
#
# BUILD THIS FILE FROM INSIDE THE CONTEXT, not from the working tree:
# docker build -f "$CTX/zeronym/shim/deploy/Containerfile" "$CTX" ...
# assemble.sh puts a `git archive HEAD` copy there precisely so that the recipe,
# which IS the definition of the build, is pinned to the same commit as the
# sources it compiles. Building the working-tree copy instead would let an
# auditor at the recorded commit unknowingly build a different recipe. Nothing
# under deploy/ is compiled, so its presence in the context does not affect the
# binary hash; that is measured, not assumed.
#
# Neither the shim nor the repo root carries a rust-toolchain.toml, so the
# pinned pallet-rust digest IS the toolchain pin. If one is ever added and its
# channel differs from the image, rustup will try to download a toolchain, which
# needs network and destroys determinism. Do not add one.
ARG TARGET_ARCH="x86_64-unknown-linux-musl"
############################################################
# StageX bases, pinned by digest
############################################################
# pallet-rust is the ONLY builder pallet needed. It already ships rustc 1.96.0,
# clang 22.1.5 targeting x86_64-unknown-linux-musl, /usr/bin/cc, ar, mold,
# ld.lld, /usr/include and /usr/lib/libc.a. The reference recipe additionally
# copies pallet-clang, user-protobuf and user-abseil-cpp; all three are
# unnecessary here and user-protobuf is actively harmful (see below).
FROM stagex/pallet-rust:1.96.0@sha256:abe9b95c93a5afa271f69fcd5eb18c8cd405fe5df6491a63c9418e3a170573dc AS pallet-rust
FROM stagex/core-busybox:1.38.0@sha256:e4a30addc8939c8e232472de904d1d9e97fc2e735fca9a9701ce49db04c6c181 AS busybox
############################################################
# Builder
############################################################
FROM pallet-rust AS builder
ARG TARGET_ARCH
# Which cargo features to compile. The deploy target is the mixnet shim, so this
# defaults to `mixnet-driver` (links nym-sdk; the binary still runs clearnet when
# --hub-nym is unset). The feature CHANGES the binary and therefore EXPECTED_SHA256,
# so a rebaseline goes with any change to it. Build the leaner clearnet-only shim
# with `--build-arg CARGO_FEATURES=` (empty), which drops nym-sdk entirely.
ARG CARGO_FEATURES="mixnet-driver"
SHELL ["/bin/sh", "-euo", "pipefail", "-c"]
# DO NOT add stagex/user-protobuf here. zaino-proto's build.rs regenerates its
# committed src/proto/*.rs whenever protoc is reachable, and while
# default-features = false already removes the `which::which("protoc")` branch,
# the PROTOC env-var branch of protoc_available() is NOT feature-gated. An image
# with no protoc in it at all is the second, independent lock: nothing to find,
# nothing to regenerate, and the committed protos compile verbatim. Never set
# PROTOC either.
WORKDIR /usr/src/app
# CARGO_HOME and WORKDIR are load-bearing for reproducibility, not taste. rustc
# embeds absolute paths, and this recipe pins them rather than relying on
# --remap-path-prefix. An auditor who rebuilds at a different path gets a
# different hash and will think the build failed.
ENV CARGO_HOME=/usr/local/cargo
ENV CARGO_INCREMENTAL=0
ENV RUST_BACKTRACE=1
ENV RUSTFLAGS="-C codegen-units=1"
ENV RUSTFLAGS="${RUSTFLAGS} -C target-feature=+crt-static"
ENV RUSTFLAGS="${RUSTFLAGS} -C linker=clang -C link-arg=-fuse-ld=mold"
ENV RUSTFLAGS="${RUSTFLAGS} -C link-arg=-Wl,--build-id=none"
ENV SOURCE_DATE_EPOCH=1
# Deliberately ABSENT versus the reference: CXXSTDLIB, CXXFLAGS,
# ROCKSDB_USE_PKG_CONFIG, the libc++.a / libc++abi.a / libzstd.a / libz.a
# link-args, --whole-archive, --allow-multiple-definition, and the
# /usr/lib/libstdc++.a INPUT() shim. Every one of those exists for rocksdb or
# libzcash_script. The shim's graph has neither: its only C dependency is
# secp256k1-sys (pure C, via cc), and zcash_script 0.4.5 is the pure-Rust
# reimplementation. It links clean without them. If a future dependency breaks
# the link, restore the reference's flags before debugging anything else.
# Repo-shaped context. Three COPYs rather than one so the layer cache is
# invalidated by the piece that actually changed.
COPY zebra/ ./zebra/
COPY zaino/ ./zaino/
COPY zeronym/ ./zeronym/
WORKDIR /usr/src/app/zeronym/shim
# Two phases so the network is only open for the fetch. --locked here and
# --frozen below make the committed Cargo.lock authoritative: any drift is a
# hard build failure rather than a silent re-resolution. That is what pins the
# shim's orchard 0.15.4 from crates.io (the shim is its own workspace and does
# not inherit zebra's [patch.crates-io]), and it is also a free guard on the
# zaino-proto feature set, since a regression to default features would change
# the lock and fail the build.
RUN cargo fetch --locked --target ${TARGET_ARCH}
# No BuildKit cache mounts, anywhere. `docker build --no-cache` does NOT clear
# cache mounts, so a recipe that uses them cannot honestly support a
# two-cold-builds reproducibility proof.
#
# NETWORK RELAXATION, MIXNET BUILD ONLY IN SPIRIT. This RUN keeps the network ON.
# The clearnet build does not need it, but nym-sdk (the mixnet-driver feature)
# pulls nym-network-defaults, whose build.rs runs `cargo metadata` over the WHOLE
# nym workspace purely to locate its own envs/mainnet.env. That resolves nym's
# unrelated wasm members and their git deps (e.g. nymtech/smoltcp), which are NOT
# in this crate's lockfile and so were never `cargo fetch`ed; offline it dies
# resolving github. Determinism is NOT lost: every version is still pinned (our
# --frozen lock here, nym's own committed lock at the tag for the transitive
# resolution), so the network only fetches content already pinned by rev/hash.
# git-fetch-with-cli makes those arbitrary-rev git deps fetch reliably.
# CAVEAT: this weakens the "offline build (--network=none)" ingredient in
# deploy/README.md. A fully hermetic mixnet build must pre-warm nym's workspace
# metadata cache during the network-on fetch phase and set CARGO_NET_OFFLINE for
# this RUN; that is the follow-up, tracked in NYM_PLAN.md M6.
RUN CARGO_NET_GIT_FETCH_WITH_CLI=true \
cargo build --release --frozen --target ${TARGET_ARCH} \
${CARGO_FEATURES:+--features "${CARGO_FEATURES}"} --bin zero-indexer-shim && \
install -D -m 0755 target/${TARGET_ARCH}/release/zero-indexer-shim \
/usr/local/bin/zero-indexer-shim
############################################################
# Export stage: the artifact under audit, with nothing around it
############################################################
# `docker build --target export --output type=local,dest=DIR` drops the bare
# binary on the host. This is what the reproducibility check hashes.
FROM scratch AS export
COPY --from=builder /usr/local/bin/zero-indexer-shim /zero-indexer-shim
############################################################
# Runtime
############################################################
FROM busybox AS runtime
# The stagex busybox base is usr-merged (/lib -> usr/lib, /lib64 -> usr/lib) but
# ships no usr/lib, so /lib and /lib64 are DANGLING symlinks. Caution's EIF
# builder runs `test -e <rootfs>/lib || mkdir -p <rootfs>/lib`, and mkdir cannot
# create through a dangling symlink, so initramfs assembly dies with "No such
# file or directory". Materialising the targets makes the `test -e` succeed.
# The binary is static musl, so these stay empty. USER root is required because
# the stagex base runs as uid 1000 and cannot mkdir in /usr.
USER root
RUN mkdir -p /usr/lib /etc/ssl/certs /tmp && chmod 1777 /tmp
# The shim speaks plaintext h2c and has no TLS stack in its dependency graph
# today, so this bundle is purely defensive (a stale zaino image once failed at
# startup with "No CA certificates were loaded from the system"). Sourcing it
# from the same pinned pallet-rust keeps it deterministic.
COPY --from=pallet-rust /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
# Copy THROUGH the export stage, so the bytes an auditor hashes and the bytes
# that ship are provably the same file.
COPY --from=export /zero-indexer-shim /zero-indexer-shim
# Configuration is two flags, or ZIS_LISTEN / ZIS_BACKEND in the environment.
# Defaults are 127.0.0.1:9068 (listen) and 127.0.0.1:9067 (backend); a container
# or enclave deployment will want ZIS_LISTEN=0.0.0.0:9068.
ENTRYPOINT ["/zero-indexer-shim"]