Dockerfile.hub
Rama main · réplica de lectura del repositorio original.
# syntax=docker/dockerfile:1.7
# BloxOS hub image.
#
# One build of this checkout produces the hub binary and the agent binaries
# the hub serves to enrolling machines, so a running hub always offers
# agents from the same commit. Every image, whatever its own architecture,
# carries Linux agents for both amd64 and arm64 (cross-compiled, no
# emulation) plus the Windows amd64 agent, so one hub serves a mixed fleet;
# see docker/README.md.
#
# Builds are deterministic for a given commit and toolchain (-trimpath,
# no VCS stamping, stripped), so rebuilding does not change the served
# agent hash and does not trigger a fleet rollout.
FROM --platform=$BUILDPLATFORM golang:1.26.8-alpine AS build
ARG TARGETARCH
ARG BLOXOS_BUILD_VERSION=development
ARG BLOXOS_BUILD_REVISION=unknown
ENV CGO_ENABLED=0 \
GOTOOLCHAIN=local \
GOFLAGS="-trimpath -buildvcs=false"
WORKDIR /src
COPY proto/ proto/
COPY hub/go.mod hub/go.sum hub/
COPY agent/go.mod agent/go.sum agent/
RUN --mount=type=cache,target=/go/pkg/mod \
(cd hub && go mod download) && (cd agent && go mod download)
COPY hub/ hub/
COPY agent/ agent/
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
mkdir -p /out/agents \
&& (cd hub && GOOS=linux GOARCH="$TARGETARCH" go build -ldflags="-s -w -X main.buildVersion=$BLOXOS_BUILD_VERSION -X main.buildRevision=$BLOXOS_BUILD_REVISION -X main.agentBundleRequired=packaged" -o /out/bloxos-hub .) \
&& (cd agent && GOOS=linux GOARCH=amd64 go build -ldflags='-s -w' -o /out/agents/bloxos-agent-linux-amd64 .) \
&& (cd agent && GOOS=linux GOARCH=arm64 go build -ldflags='-s -w' -o /out/agents/bloxos-agent-linux-arm64 .) \
&& (cd agent && GOOS=windows GOARCH=amd64 go build -ldflags='-s -w' -o /out/agents/bloxos-agent-windows-amd64.exe .)
# The catalog that the hub reads at startup, built from the bytes just
# produced. It runs in its own stage so the runtime image never gains a Python
# interpreter, and it uses the SAME generator and format as the release
# catalog — one artifact identity, not a parallel in-image format.
#
# It omits the image digest, because the image it would name does not exist
# yet; a plain local `docker build` also has no release tag or commit SHA. Those
# fields are left out rather than filled with a placeholder, so nothing
# downstream can mistake a stand-in for provenance. The artifact checks —
# sha256, size, architecture and one consistent release marker across all three
# platforms — are enforced here exactly as they are for a release.
FROM alpine:3.22 AS catalog
RUN apk add --no-cache python3
COPY scripts/agent_bundle.py /tmp/agent_bundle.py
COPY --from=build /out/agents/ /out/agents/
ARG BLOXOS_BUILD_VERSION=development
ARG BLOXOS_BUILD_REVISION=unknown
RUN sha=$(python3 /tmp/agent_bundle.py manifest --bundle /out/agents \
--source "$BLOXOS_BUILD_REVISION" --version "$BLOXOS_BUILD_VERSION") \
&& python3 /tmp/agent_bundle.py check --bundle /out/agents --manifest-sha256 "$sha" \
--allow-missing-provenance >/dev/null
FROM alpine:3.22
RUN apk add --no-cache ca-certificates \
&& addgroup -g 65532 -S nonroot \
&& adduser -u 65532 -S -G nonroot -h /data -s /sbin/nologin nonroot \
&& mkdir -p /data && chown nonroot:nonroot /data && chmod 0700 /data
# The hub's served-binary resolver requires root-owned, non-writable
# artifacts and ancestors; the hub itself runs unprivileged. The Linux
# agents sit at the resolver's per-architecture defaults,
# /usr/local/lib/bloxos/linux/<arch>/bloxos-agent.
COPY --from=build --chown=root:root --chmod=0755 /out/bloxos-hub /usr/local/bin/bloxos-hub
# The managed bundle and the backward-compatible per-architecture paths, all
# created inside ONE layer.
#
# agents/ sits beside the hub executable, the SAME hub-relative layout the
# native server archive uses (<release>/hub/agents), so both deploy modes
# resolve agents the same way and the one production hub build can require a
# bundle in either.
#
# The compatibility paths are HARD LINKS, not symlinks: the trusted-binary
# resolver rejects a symlinked final path but accepts a regular file. Sharing an
# inode also means those paths cannot drift from the managed payloads the way a
# second copy of the same bytes silently could.
#
# The single RUN is what makes the links survive. Copying the payloads in one
# layer and linking them in another put the file and its links on opposite
# sides of a layer boundary; the copy-up and diff then materialised them as
# separate files, and CI measured a link count of 1 — the duplication this was
# meant to avoid, silently. A bind mount supplies the bytes read-only without
# committing a second copy of them to any layer.
RUN --mount=type=bind,from=catalog,source=/out/agents,target=/tmp/agent-input,ro \
set -eu \
&& mkdir -p /usr/local/bin/agents /usr/local/lib/bloxos/linux/amd64 \
/usr/local/lib/bloxos/linux/arm64 /usr/local/lib/bloxos/windows \
&& cp /tmp/agent-input/bloxos-agent-linux-amd64 /tmp/agent-input/bloxos-agent-linux-arm64 \
/tmp/agent-input/bloxos-agent-windows-amd64.exe /tmp/agent-input/agent-manifest.json \
/usr/local/bin/agents/ \
&& ln /usr/local/bin/agents/bloxos-agent-linux-amd64 /usr/local/lib/bloxos/linux/amd64/bloxos-agent \
&& ln /usr/local/bin/agents/bloxos-agent-linux-arm64 /usr/local/lib/bloxos/linux/arm64/bloxos-agent \
&& ln /usr/local/bin/agents/bloxos-agent-linux-amd64 /usr/local/lib/bloxos/linux/bloxos-agent \
&& ln /usr/local/bin/agents/bloxos-agent-windows-amd64.exe /usr/local/lib/bloxos/windows/bloxos-agent.exe \
&& chown -R root:root /usr/local/bin/agents /usr/local/lib/bloxos \
&& chmod 0755 /usr/local/bin/agents/bloxos-agent-* \
&& chmod 0644 /usr/local/bin/agents/agent-manifest.json \
&& chmod -R go-w /usr/local/bin/agents /usr/local/lib/bloxos
# State lives in one volume: bloxos.db in the working directory, secrets,
# setup token and signing key under $HOME/.bloxos.
ENV HOME=/data \
HUB_LISTEN=0.0.0.0:4000
WORKDIR /data
VOLUME ["/data"]
USER nonroot
EXPOSE 4000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://127.0.0.1:4000/health >/dev/null || exit 1
ENTRYPOINT ["/usr/local/bin/bloxos-hub"]