This commit is contained in:
gru committed 2026-10-03 00:27:09 +01:00
commit 5e23e9af02
240 files changed
+99957

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
# Zot / OCI registry. Do not include a path in REGISTRY.
REGISTRY=zot.linuxiarz.pl
IMAGE_NAME=rustproxy
# Alternative shorthand instead of REGISTRY + IMAGE_NAME:
# OCI_IMAGE=zot.example.net/network/rustproxy
# Zot Basic Auth / htpasswd or token credentials.
OCI_USERNAME=admin
OCI_PASSWORD=
# OCI_TOKEN=
# RouterOS targets used by this helper.
BUILD_PLATFORMS=linux/amd64,linux/arm64
#BUILD_PLATFORMS=linux/amd64
BUILDER=rustproxy
# RustProxy currently requires Rust 1.88.
RUST_VERSION=1.88
DEBIAN_SUITE=trixie
# Empty OCI_TAG means the script uses Cargo.toml package version.
OCI_TAG=
OCI_LATEST=1
OCI_EXTRA_TAGS=
# Build behavior.
OCI_PULL=1
OCI_NO_CACHE=0
OCI_VERIFY=1
OCI_PROGRESS=plain
# 1 stores BuildKit cache in Zot as IMAGE_NAME:buildcache.
# Recommended for CI/ephemeral buildx builders.
OCI_REGISTRY_CACHE=0
# Optional BuildKit config, useful for an internal Zot with a custom CA.
# BUILDER_CONFIG=/etc/buildkit/buildkitd.toml
@@ -0,0 +1,40 @@
# Zot / OCI registry. Do not include a path in REGISTRY.
REGISTRY=zot.example.net
IMAGE_NAME=network/rustproxy
# Alternative shorthand instead of REGISTRY + IMAGE_NAME:
# OCI_IMAGE=zot.example.net/network/rustproxy
# Zot Basic Auth / htpasswd or token credentials.
OCI_USERNAME=
OCI_PASSWORD=
# OCI_TOKEN=
# RouterOS targets used by this helper.
BUILD_PLATFORMS=linux/amd64,linux/arm64
BUILDER=rustproxy-routeros
# RustProxy currently requires Rust 1.88.
RUST_VERSION=1.88
DEBIAN_SUITE=trixie
# Empty OCI_TAG means the script uses Cargo.toml package version.
OCI_TAG=
OCI_LATEST=1
OCI_EXTRA_TAGS=routeros
# Build behavior.
OCI_PULL=1
OCI_NO_CACHE=0
OCI_VERIFY=1
OCI_PROGRESS=plain
# 1 stores BuildKit cache in Zot as IMAGE_NAME:buildcache.
# Recommended for CI/ephemeral buildx builders.
OCI_REGISTRY_CACHE=0
# Optional BuildKit config, useful for an internal Zot with a custom CA.
# BUILDER_CONFIG=/etc/buildkit/buildkitd.toml
# Optional manual repository root override.
# RUSTPROXY_PROJECT_ROOT=/home/user/RustProxy
@@ -0,0 +1,51 @@
# syntax=docker/dockerfile:1
FROM rust:1.88-trixie AS build
WORKDIR /build
COPY . .
RUN if [ ! -f Cargo.lock ]; then cargo generate-lockfile; fi \
&& cargo build --locked --release
FROM debian:trixie-slim AS runtime-trixie
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
python3 \
certbot \
openssl \
gosu \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --system --gid 10001 rustproxy \
&& useradd --system \
--uid 10001 \
--gid rustproxy \
--home /var/lib/rustproxy \
--shell /usr/sbin/nologin \
rustproxy \
&& install -d -o rustproxy -g rustproxy -m 0700 /var/lib/rustproxy \
&& install -d -o rustproxy -g rustproxy -m 0750 /var/log/rustproxy
COPY --from=build /build/target/release/rust-proxy /usr/local/bin/rust-proxy
COPY scripts/routeros-oci-build/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod 0755 \
/usr/local/bin/rust-proxy \
/usr/local/bin/docker-entrypoint.sh
ENV RUST_LOG=rust_proxy=info,warn \
RUST_PROXY_DATA_DIR=/var/lib/rustproxy \
RUST_PROXY_ADMIN_LISTEN=0.0.0.0:9090 \
RUST_PROXY_PROXY_LISTEN=0.0.0.0:8080 \
RUST_PROXY_TLS_LISTEN=0.0.0.0:8443
VOLUME ["/var/lib/rustproxy"]
EXPOSE 8080/tcp
EXPOSE 8443/tcp
EXPOSE 9090/tcp
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["serve", "--allow-remote-admin"]
@@ -0,0 +1,25 @@
.git
.gitignore
.github
.idea
.vscode
target
**/target
node_modules
**/node_modules
*.zip
*.tar
*.tar.gz
*.tgz
*.pkg
*.deb
*.rpm
.env
**/.env
*.pem
*.key
*.p12
*.pfx
*.sqlite
*.sqlite3
*.db
+137
View File
@@ -0,0 +1,137 @@
# RustProxy RouterOS OCI builder - Debian Trixie
Multi-arch build/push helper for RustProxy images intended for RouterOS Container.
It publishes one OCI manifest for `linux/amd64` and `linux/arm64`.
## Why ARM64 builds are faster
The Rust build stage is pinned to Docker BuildKit's `BUILDPLATFORM`:
```dockerfile
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-trixie AS build
```
On an amd64 build host:
- amd64 uses native `rustc` + `gcc`,
- arm64 uses the same native amd64 `rustc` + `aarch64-linux-gnu-gcc`.
The expensive Cargo/Rust/C compilation is therefore not executed by an ARM CPU emulator.
The final Debian arm64 runtime stage can still use binfmt/QEMU briefly for `apt/dpkg` if the build host is amd64; this is normally a small part of the total build and is cacheable.
## Repository layout
Copy this directory as:
```text
RustProxy/
Cargo.toml
Cargo.lock
scripts/
routeros-oci-build/
build-routeros-oci.sh
Dockerfile.routeros
Dockerfile.routeros.dockerignore
.env
```
Create `.env`:
```bash
cp scripts/routeros-oci-build/.env.example \
scripts/routeros-oci-build/.env
```
Then edit the Zot address and credentials.
## Zot example
```env
REGISTRY=zot.example.net
IMAGE_NAME=network/rustproxy
OCI_USERNAME=builder
OCI_PASSWORD=secret
BUILD_PLATFORMS=linux/amd64,linux/arm64
RUST_VERSION=1.88
DEBIAN_SUITE=trixie
```
`OCI_TOKEN` can be used instead of `OCI_PASSWORD`.
Credentials are sent to `docker login` using `--password-stdin`.
## Check configuration
```bash
ENV_FILE=scripts/routeros-oci-build/.env \
bash scripts/routeros-oci-build/build-routeros-oci.sh config
```
The repository root is detected using Git. If that is unavailable, the script walks upward until it finds `Cargo.toml`.
## Build and push multi-arch image
```bash
ENV_FILE=scripts/routeros-oci-build/.env \
bash scripts/routeros-oci-build/build-routeros-oci.sh push
```
With RustProxy version `0.24.5` and the defaults this publishes:
```text
zot.example.net/network/rustproxy:0.24.5
zot.example.net/network/rustproxy:latest
zot.example.net/network/rustproxy:routeros
```
Each tag points to the same amd64 + arm64 OCI manifest.
## Local single-architecture build
```bash
ENV_FILE=scripts/routeros-oci-build/.env \
bash scripts/routeros-oci-build/build-routeros-oci.sh load amd64
```
or:
```bash
ENV_FILE=scripts/routeros-oci-build/.env \
bash scripts/routeros-oci-build/build-routeros-oci.sh load arm64
```
`load arm64` loads an ARM64 image into Docker; it does not attempt to start the container.
## Cache
BuildKit cache mounts are used automatically for Cargo registry/git/target data.
For short-lived CI builders, enable registry cache:
```env
OCI_REGISTRY_CACHE=1
```
It is stored in Zot as:
```text
zot.example.net/network/rustproxy:buildcache
```
## Custom/self-signed Zot CA
Point `BUILDER_CONFIG` to a BuildKit configuration that trusts the registry CA, for example:
```env
BUILDER_CONFIG=/etc/buildkit/buildkitd.toml
```
Docker itself must also trust the registry CA for `docker login` and manifest inspection.
## Requirements
- Docker Engine
- Docker Buildx
- access to the Zot/OCI registry
- an existing RustProxy `Cargo.lock`
Do not add an ARM-native Rust builder stage. Doing that would put `rustc` and Cargo back under QEMU on an amd64 host.
+272
View File
@@ -0,0 +1,272 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# RustProxy multi-arch OCI builder for RouterOS.
# Rust compilation runs on BUILDPLATFORM. Target binaries are cross-compiled,
# so rustc/cargo do not run through QEMU for arm64 builds.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
resolve_project_root() {
if [[ -n "${RUSTPROXY_PROJECT_ROOT:-}" ]]; then
(cd "$RUSTPROXY_PROJECT_ROOT" && pwd)
return
fi
if command -v git >/dev/null 2>&1 && git -C "$SCRIPT_DIR" rev-parse --show-toplevel >/dev/null 2>&1; then
git -C "$SCRIPT_DIR" rev-parse --show-toplevel
return
fi
local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/Cargo.toml" ]]; then
printf '%s\n' "$dir"
return
fi
dir="$(dirname "$dir")"
done
return 1
}
PROJECT_ROOT="$(resolve_project_root)" || {
echo "Cannot find repository root (Cargo.toml)." >&2
echo "Set RUSTPROXY_PROJECT_ROOT=/path/to/RustProxy." >&2
exit 1
}
[[ -f "$PROJECT_ROOT/Cargo.toml" ]] || {
echo "Missing $PROJECT_ROOT/Cargo.toml" >&2
exit 1
}
# Supports the invocation:
# ENV_FILE=scripts/routeros-oci-build/.env bash scripts/routeros-oci-build/build-routeros-oci.sh push
ENV_FILE="${ROUTEROS_OCI_ENV_FILE:-${ENV_FILE:-$SCRIPT_DIR/.env}}"
if [[ "$ENV_FILE" != /* ]]; then
ENV_FILE="$PROJECT_ROOT/$ENV_FILE"
fi
[[ -f "$ENV_FILE" ]] || {
echo "Missing environment file: $ENV_FILE" >&2
echo "Copy $SCRIPT_DIR/.env.example to $SCRIPT_DIR/.env and edit it." >&2
exit 1
}
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
ACTION="${1:-push}"
VERSION="$(awk '
/^\[package\][[:space:]]*$/ { in_package=1; next }
/^\[/ && in_package { exit }
in_package && /^[[:space:]]*version[[:space:]]*=/ {
line=$0
sub(/^[^=]*=[[:space:]]*"/, "", line)
sub(/".*/, "", line)
print line
exit
}
' "$PROJECT_ROOT/Cargo.toml")"
[[ -n "$VERSION" ]] || { echo "Cannot determine package version from Cargo.toml" >&2; exit 1; }
# Registry/image. Preferred format: REGISTRY + IMAGE_NAME.
# OCI_IMAGE is kept as a compatible shorthand.
if [[ -n "${OCI_IMAGE:-}" ]]; then
IMAGE_REPO="${OCI_IMAGE%/}"
REGISTRY="${OCI_REGISTRY:-${IMAGE_REPO%%/*}}"
else
: "${REGISTRY:?Set REGISTRY in $ENV_FILE}"
: "${IMAGE_NAME:?Set IMAGE_NAME in $ENV_FILE}"
REGISTRY="${REGISTRY#https://}"
REGISTRY="${REGISTRY#http://}"
REGISTRY="${REGISTRY%/}"
IMAGE_REPO="${REGISTRY}/${IMAGE_NAME#/}"
fi
REGISTRY="${REGISTRY#https://}"
REGISTRY="${REGISTRY#http://}"
REGISTRY="${REGISTRY%/}"
IMAGE_REPO="${IMAGE_REPO#https://}"
IMAGE_REPO="${IMAGE_REPO#http://}"
IMAGE_REPO="${IMAGE_REPO%/}"
BUILD_PLATFORMS="${BUILD_PLATFORMS:-linux/amd64,linux/arm64}"
BUILDER="${BUILDER:-rustproxy-routeros}"
RUST_VERSION="${RUST_VERSION:-1.88}"
DEBIAN_SUITE="${DEBIAN_SUITE:-trixie}"
DOCKERFILE="${DOCKERFILE:-$SCRIPT_DIR/Dockerfile.routeros}"
OCI_TAG="${OCI_TAG:-$VERSION}"
OCI_LATEST="${OCI_LATEST:-1}"
OCI_EXTRA_TAGS="${OCI_EXTRA_TAGS:-routeros}"
OCI_PULL="${OCI_PULL:-1}"
OCI_NO_CACHE="${OCI_NO_CACHE:-0}"
OCI_VERIFY="${OCI_VERIFY:-1}"
OCI_REGISTRY_CACHE="${OCI_REGISTRY_CACHE:-0}"
OCI_PROGRESS="${OCI_PROGRESS:-plain}"
IMAGE_TAG="${IMAGE_REPO}:${OCI_TAG}"
[[ -f "$DOCKERFILE" ]] || { echo "Missing Dockerfile: $DOCKERFILE" >&2; exit 1; }
REGISTRY_USERNAME="${OCI_USERNAME:-${REGISTRY_USERNAME:-}}"
REGISTRY_SECRET="${OCI_TOKEN:-${OCI_PASSWORD:-${REGISTRY_PASSWORD:-}}}"
registry_login() {
if [[ -z "$REGISTRY_USERNAME" && -z "$REGISTRY_SECRET" ]]; then
return 0
fi
if [[ -z "$REGISTRY_USERNAME" || -z "$REGISTRY_SECRET" ]]; then
echo "Set both OCI_USERNAME and OCI_PASSWORD/OCI_TOKEN, or neither." >&2
exit 1
fi
printf '%s' "$REGISTRY_SECRET" | docker login "$REGISTRY" \
--username "$REGISTRY_USERNAME" \
--password-stdin
}
ensure_builder() {
command -v docker >/dev/null 2>&1 || { echo "docker is required" >&2; exit 127; }
docker buildx version >/dev/null 2>&1 || { echo "docker buildx is required" >&2; exit 127; }
if docker buildx inspect "$BUILDER" >/dev/null 2>&1; then
docker buildx use "$BUILDER"
else
local args=(create --name "$BUILDER" --driver docker-container --use)
[[ -z "${BUILDER_CONFIG:-}" ]] || args+=(--config "$BUILDER_CONFIG")
docker buildx "${args[@]}" >/dev/null
fi
docker buildx inspect --bootstrap >/dev/null
}
revision="unknown"
if command -v git >/dev/null 2>&1 && git -C "$PROJECT_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
revision="$(git -C "$PROJECT_ROOT" rev-parse --short=12 HEAD 2>/dev/null || printf 'unknown')"
fi
created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
common_build_args() {
BUILD_ARGS=(
build
--builder "$BUILDER"
--file "$DOCKERFILE"
--target runtime-trixie
--build-arg "BUILD_VERSION=$VERSION"
--build-arg "RUST_VERSION=$RUST_VERSION"
--build-arg "DEBIAN_SUITE=$DEBIAN_SUITE"
--provenance=false
--sbom=false
--progress "$OCI_PROGRESS"
--label "org.opencontainers.image.title=RustProxy"
--label "org.opencontainers.image.version=$VERSION"
--label "org.opencontainers.image.revision=$revision"
--label "org.opencontainers.image.created=$created"
)
[[ "$OCI_PULL" == "1" ]] && BUILD_ARGS+=(--pull)
[[ "$OCI_NO_CACHE" == "1" ]] && BUILD_ARGS+=(--no-cache)
if [[ "$OCI_REGISTRY_CACHE" == "1" ]]; then
BUILD_ARGS+=(
--cache-from "type=registry,ref=${IMAGE_REPO}:buildcache"
--cache-to "type=registry,ref=${IMAGE_REPO}:buildcache,mode=max,image-manifest=true,oci-mediatypes=true"
)
fi
}
build_push() {
registry_login
ensure_builder
common_build_args
BUILD_ARGS+=(
--platform "$BUILD_PLATFORMS"
--tag "$IMAGE_TAG"
)
if [[ "$OCI_LATEST" == "1" && "$OCI_TAG" != "latest" ]]; then
BUILD_ARGS+=(--tag "${IMAGE_REPO}:latest")
fi
if [[ -n "$OCI_EXTRA_TAGS" ]]; then
local tag
IFS=',' read -r -a extra_tags <<< "$OCI_EXTRA_TAGS"
for tag in "${extra_tags[@]}"; do
tag="${tag//[[:space:]]/}"
[[ -n "$tag" ]] && BUILD_ARGS+=(--tag "${IMAGE_REPO}:${tag}")
done
fi
BUILD_ARGS+=(--push "$PROJECT_ROOT")
cat <<INFO
==> RustProxy RouterOS OCI build
image: $IMAGE_TAG
platforms: $BUILD_PLATFORMS
Rust: $RUST_VERSION
runtime: debian:$DEBIAN_SUITE-slim
builder: $BUILDER
project: $PROJECT_ROOT
Dockerfile: $DOCKERFILE
Rust compilation runs on BUILDPLATFORM; arm64 is cross-compiled.
INFO
docker buildx "${BUILD_ARGS[@]}"
if [[ "$OCI_VERIFY" == "1" ]]; then
echo
echo "==> Published manifest"
docker buildx imagetools inspect "$IMAGE_TAG"
fi
}
build_load() {
local arch="${2:-amd64}" platform
case "$arch" in
amd64|x86_64) platform="linux/amd64"; arch="amd64" ;;
arm64|aarch64) platform="linux/arm64"; arch="arm64" ;;
*) echo "Usage: $0 load [amd64|arm64]" >&2; exit 2 ;;
esac
ensure_builder
common_build_args
BUILD_ARGS+=(
--platform "$platform"
--tag "${IMAGE_REPO}:local-${arch}"
--load
"$PROJECT_ROOT"
)
docker buildx "${BUILD_ARGS[@]}"
printf 'Loaded %s:local-%s (%s)\n' "$IMAGE_REPO" "$arch" "$platform"
}
show_config() {
cat <<CFG
project_root=$PROJECT_ROOT
env_file=$ENV_FILE
version=$VERSION
image=$IMAGE_TAG
registry=$REGISTRY
platforms=$BUILD_PLATFORMS
builder=$BUILDER
rust=$RUST_VERSION
debian_suite=$DEBIAN_SUITE
dockerfile=$DOCKERFILE
registry_cache=$OCI_REGISTRY_CACHE
CFG
}
case "$ACTION" in
push) build_push ;;
load) build_load "${2:-amd64}" ;;
config) show_config ;;
*)
echo "Usage: $0 [push|load [amd64|arm64]|config]" >&2
exit 2
;;
esac
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
set -eu
DATA_DIR="${RUST_PROXY_DATA_DIR:-/var/lib/rustproxy}"
RUSTPROXY_UID="${RUSTPROXY_UID:-10001}"
RUSTPROXY_GID="${RUSTPROXY_GID:-10001}"
if [ "$(id -u)" = "0" ]; then
mkdir -p "$DATA_DIR"
chown "$RUSTPROXY_UID:$RUSTPROXY_GID" "$DATA_DIR"
chmod 0700 "$DATA_DIR"
exec gosu "$RUSTPROXY_UID:$RUSTPROXY_GID" \
/usr/local/bin/rust-proxy "$@"
fi
exec /usr/local/bin/rust-proxy "$@"