forked from librefang/librefang
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
123 lines (118 loc) · 6.2 KB
/
Copy pathDockerfile
File metadata and controls
123 lines (118 loc) · 6.2 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
# syntax=docker/dockerfile:1
# Stage 1: Build React dashboard
# Pinned to a specific minor (not floating `node:20-alpine`) so rebuilds of a
# tagged release months later produce a bit-for-bit identical builder image.
# Track Node 20 LTS — CI's setup-node also uses node-version: 20
# (.github/workflows/ci.yml, .github/workflows/dashboard-build.yml).
FROM node:20.20.2-alpine AS dashboard-builder
WORKDIR /build
COPY crates/librefang-api/dashboard ./dashboard
WORKDIR /build/dashboard
# `corepack enable` alone hits `fetchLatestStableVersion2` against the npm
# registry, which has flaked on us during builds. Activate the pinned pnpm
# version (matches the `packageManager` field in package.json) directly so
# the build never has to ask the registry "what's the latest stable?".
# We also refresh corepack itself first: the keyring bundled with the node
# base image goes stale as pnpm rotates signing keys, manifesting as
# "Internal Error: Cannot find matching keyid" during `corepack prepare`.
# Node ≥20.19 is also required by vite 8 / rolldown's optional native
# bindings (engines: ^20.19.0), without which `pnpm install` silently skips
# the linux-x64-musl binding and `vite build` fails at require-time.
RUN npm install --global corepack@latest \
&& corepack enable \
&& corepack prepare pnpm@10.33.0 --activate \
&& pnpm install --frozen-lockfile --ignore-scripts \
&& pnpm run build
# Stage 2: Build Rust binary
# Pinned to a specific minor (not floating `rust:1-slim-bookworm`). Tracks the
# workspace MSRV declared in Cargo.toml's [workspace.package].rust-version
# (currently 1.94.1) so the build image is guaranteed to satisfy it.
FROM rust:1.94-slim-bookworm AS builder
WORKDIR /build
# libdbus-1-dev is required by libdbus-sys (transitive dep of keyring's
# sync-secret-service feature, added in #3180). Without it the cargo build
# panics with exit 101 in the build script — same root cause as #3259, and
# why the v2026.4.27-beta6 docker image was never published.
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
pkg-config \
libssl-dev \
libdbus-1-dev \
perl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY Cargo.toml Cargo.lock ./
COPY crates ./crates
COPY xtask ./xtask
COPY packages ./packages
# librefang-channels embeds the Python SDK tree at compile time via
# include_dir!("$CARGO_MANIFEST_DIR/../../sdk/python/librefang") in
# embedded_sdk.rs (added in #5472). Without this COPY the proc macro
# panics with "sdk/python/librefang is not a directory". Only this one
# subtree is needed — .dockerignore keeps the rest of sdk/ out.
COPY sdk/python/librefang ./sdk/python/librefang
# librefang-api uses include_str!("../../../deploy/...") to embed the
# observability stack (prometheus / tempo / otel-collector / grafana
# configs) at compile time — added in #3062. Without this COPY the
# build fails with "couldn't read deploy/grafana/...". flake.nix
# already lists the same paths in its source fileset.
COPY deploy ./deploy
COPY --from=dashboard-builder /build/static/react ./crates/librefang-api/static/react
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/build/target \
# `--features telemetry`: the published Docker image is the full
# daemon, so it ships with telemetry on. Channel adapters all run as
# out-of-process sidecars now (see #5408 / #5461), so the old
# `all-channels` / `core-channels` aliases are gone — `telemetry` is
# the whole opt-in surface left, matching the CLI's own `default`.
cargo build --release --bin librefang --features telemetry && \
cp target/release/librefang /usr/local/bin/librefang
# Pinned to a specific Node 22 LTS minor (not floating `node:lts-bookworm-slim`)
# so a rebuild months later doesn't quietly land on a new major when the
# `lts` alias rolls forward. `curl` is added for the HEALTHCHECK below.
FROM node:22.11.0-bookworm-slim
# libdbus-1-3 = runtime SO that libdbus-sys links against. Without it the
# binary fails to start (the keyring init path runs early in boot and
# exits 101 if the .so can't be resolved).
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
python3 \
python3-venv \
libicu72 \
libdbus-1-3 \
gosu \
&& rm -rf /var/lib/apt/lists/*
RUN addgroup --system --gid 1001 librefang && \
adduser --system --uid 1001 --ingroup librefang librefang
COPY --from=builder /usr/local/bin/librefang /usr/local/bin/
COPY --from=builder /build/packages /opt/librefang/packages
COPY deploy/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# CIS Docker Benchmark §4.1: run the service as a dedicated non-root
# user with no login shell. The user `librefang` (uid/gid 1001) is
# already created above via addgroup/adduser; the redundant
# `groupadd -r librefang && useradd -r ...` block introduced by #3948
# collides with that user — `groupadd` exits with code 9 ('group
# already exists'), breaking `docker build` on every clean tree.
# Apply the CIS shell-restriction with `usermod` instead, then chown
# /opt/librefang/packages so the runtime user can read its own asset
# tree (the COPYs above land as root:root by default).
RUN usermod -s /sbin/nologin librefang && \
chown -R librefang:librefang /opt/librefang/packages
EXPOSE 4545
ENV LIBREFANG_HOME=/data
# Native restart-on-failure signal for orchestrators (Docker/Swarm/Compose;
# Kubernetes uses its own probes and ignores this). 20 s start-period gives
# the daemon time to bind, run `librefang init` on first boot, and start the
# axum server. The shell form is required so ${PORT:-4545} expands at
# runtime — Railway/Render/Fly inject $PORT and the entrypoint rewrites
# api_listen accordingly (see deploy/docker-entrypoint.sh).
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s \
CMD curl -fsS http://127.0.0.1:${PORT:-4545}/api/health || exit 1
# docker-entrypoint.sh uses gosu to exec as the librefang user, so we
# keep the entrypoint itself running as root to allow bind-mount chown
# and data-dir initialisation before privilege drop.
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["librefang", "start", "--foreground"]