# syntax=docker/dockerfile:1.4 # # Multi-stage build for NORA artifact registry. # Compiles inside Alpine — musl version always matches runtime. # # Usage: # docker build . # Alpine (default) # docker build --target redos . # RED OS # docker build --target astra . # Astra Linux SE # docker buildx build --target binary --output type=local,dest=out . # amd64 binary # docker buildx build --target binary-arm64 --output type=local,dest=out . # arm64 binary (cross-compiled) # # ── Build ────────────────────────────────────────────────────────────────── FROM rust:1-alpine3.21@sha256:7dba3db75b0d5861af2e5dcf5b87347411e84403b5c9835be00ef1e72e299d2a AS builder RUN apk add --no-cache musl-dev WORKDIR /build # Pin the build toolchain: rustup reads rust-toolchain.toml and installs the # exact channel, so the release binary is reproducible regardless of the base # image's bundled rustc. COPY rust-toolchain.toml ./ COPY Cargo.toml Cargo.lock ./ COPY nora-registry/ nora-registry/ # The fuzz member's manifest and targets (not its corpus, see .dockerignore) keep # the workspace intact so --locked holds; `-p nora-registry` never builds them. COPY fuzz/ fuzz/ # The binary shipped carries no fault injection route (`/__fi/`, src/fault_injection.rs), # whatever features or build inputs this build was given: grep refuses it. ARG CARGO_FEATURES="" RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/usr/local/cargo/git \ --mount=type=cache,target=/build/target \ cargo build --release --locked -p nora-registry $CARGO_FEATURES && \ cp target/release/nora /nora && \ strip /nora && \ ! grep -qF '/__fi/' /nora # ── Binary export (for CI release artifacts) ─────────────────────────────── FROM scratch AS binary COPY --from=builder /nora /nora # ── Cross-compile arm64 (runs natively on x86, no QEMU) ────────────────── FROM rust:1-alpine3.21@sha256:7dba3db75b0d5861af2e5dcf5b87347411e84403b5c9835be00ef1e72e299d2a AS cross-arm64 RUN apk add --no-cache musl-dev \ && wget -qO- https://musl.cc/aarch64-linux-musl-cross.tgz | tar xz -C /opt ENV PATH="/opt/aarch64-linux-musl-cross/bin:$PATH" \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc WORKDIR /build # Pin the toolchain before adding the cross target so the target lands on the # pinned channel (rust-toolchain.toml), keeping the arm64 build reproducible. COPY rust-toolchain.toml ./ RUN rustup target add aarch64-unknown-linux-musl COPY Cargo.toml Cargo.lock ./ COPY nora-registry/ nora-registry/ COPY fuzz/ fuzz/ # As for amd64: no fault injection route in the binary shipped. ARG CARGO_FEATURES="" RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/usr/local/cargo/git \ --mount=type=cache,target=/build/target \ cargo build --release --locked --target aarch64-unknown-linux-musl -p nora-registry $CARGO_FEATURES && \ cp target/aarch64-unknown-linux-musl/release/nora /nora && \ aarch64-linux-musl-strip /nora && \ ! grep -qF '/__fi/' /nora FROM scratch AS binary-arm64 COPY --from=cross-arm64 /nora /nora # ── RED OS (FSTEC-certified, RPM-based) ─────────────────────────────────── FROM registry.red-soft.ru/ubi8/ubi-minimal AS redos RUN microdnf install -y ca-certificates shadow-utils curl \ && microdnf clean all \ && groupadd -r nora && useradd -r -g nora -d /data -s /sbin/nologin nora \ && mkdir -p /data && chown nora:nora /data COPY --from=builder --chown=nora:nora /nora /usr/local/bin/nora ENV RUST_LOG=info \ NORA_HOST=:: \ NORA_CONFIG_PATH=/etc/nora/config.toml # Config *values* ship as a FILE, not ENV: env has the highest precedence in # Config::load and would silently override a user-provided config.toml (#719). # Only NORA_HOST stays in ENV — binding must survive a bind-mounted (possibly # partial) config.toml so the container stays reachable (-e NORA_HOST to change). COPY --chown=nora:nora deploy/config.docker.toml /etc/nora/config.toml EXPOSE 4000 VOLUME ["/data"] USER nora HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD ["/usr/local/bin/nora", "healthcheck"] ENTRYPOINT ["/usr/local/bin/nora"] CMD ["serve"] # ── Astra Linux SE (FSTEC-certified, Debian-based) ──────────────────────── FROM registry.astralinux.ru/library/astra/ubi17:latest AS astra RUN apt-get update \ && apt-get install -y --no-install-recommends ca-certificates curl \ && rm -rf /var/lib/apt/lists/* \ && groupadd -r nora && useradd -r -g nora -d /data -s /usr/sbin/nologin nora \ && mkdir -p /data && chown nora:nora /data COPY --from=builder --chown=nora:nora /nora /usr/local/bin/nora ENV RUST_LOG=info \ NORA_HOST=:: \ NORA_CONFIG_PATH=/etc/nora/config.toml # Config *values* ship as a FILE, not ENV: env has the highest precedence in # Config::load and would silently override a user-provided config.toml (#719). # Only NORA_HOST stays in ENV — binding must survive a bind-mounted (possibly # partial) config.toml so the container stays reachable (-e NORA_HOST to change). COPY --chown=nora:nora deploy/config.docker.toml /etc/nora/config.toml EXPOSE 4000 VOLUME ["/data"] USER nora HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD ["/usr/local/bin/nora", "healthcheck"] ENTRYPOINT ["/usr/local/bin/nora"] CMD ["serve"] # ── Alpine (default — must be last) ─────────────────────────────────────── FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d RUN apk upgrade --no-cache \ && apk add --no-cache ca-certificates \ && addgroup -S nora && adduser -S -G nora nora \ && mkdir -p /data && chown nora:nora /data COPY --from=builder --chown=nora:nora /nora /usr/local/bin/nora ENV RUST_LOG=info \ NORA_HOST=:: \ NORA_CONFIG_PATH=/etc/nora/config.toml # Config *values* ship as a FILE, not ENV: env has the highest precedence in # Config::load and would silently override a user-provided config.toml (#719). # Only NORA_HOST stays in ENV — binding must survive a bind-mounted (possibly # partial) config.toml so the container stays reachable (-e NORA_HOST to change). COPY --chown=nora:nora deploy/config.docker.toml /etc/nora/config.toml EXPOSE 4000 VOLUME ["/data"] USER nora HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD ["/usr/local/bin/nora", "healthcheck"] ENTRYPOINT ["/usr/local/bin/nora"] CMD ["serve"]