This commit is contained in:
Mateusz Gruszczyński committed 2026-09-29 14:07:21 +02:00
1 parent 093e6a7c14
commit 1a2a5f0faf
24 files changed
+578 -537

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SOURCE_DIR="${RUSTPROXY_SOURCE_DIR:?RUSTPROXY_SOURCE_DIR is required}"
FREEBSD_RELEASE="${FREEBSD_RELEASE:-15.1-RELEASE}"
DOCKER_IMAGE="${DOCKER_IMAGE:-rustproxy-freebsd15-builder:15.1-rust1.88}"
DOCKER_CACHE_DIR="${DOCKER_CACHE_DIR:-/opt/opn-rustproxy/cache}"
RUN_LINUX_TESTS="${RUN_LINUX_TESTS:-1}"
command -v docker >/dev/null 2>&1 || { echo "Docker is required" >&2; exit 1; }
[[ -f "$SOURCE_DIR/Cargo.toml" ]] || { echo "Invalid RustProxy source: $SOURCE_DIR" >&2; exit 1; }
mkdir -p "$DOCKER_CACHE_DIR/cargo" "$DOCKER_CACHE_DIR/target" "$ROOT/dist/docker-build"
uid="$(id -u)"
gid="$(id -g)"
# Rebuild is cheap after the first run because the FreeBSD sysroot layer is cached.
docker build \
--build-arg "FREEBSD_RELEASE=$FREEBSD_RELEASE" \
-t "$DOCKER_IMAGE" \
-f "$ROOT/builder/docker/Dockerfile" \
"$ROOT/builder/docker"
run_args=(
--rm
--user "$uid:$gid"
-e CARGO_HOME=/cargo
-e CARGO_TARGET_DIR=/target
-v "$SOURCE_DIR:/src:ro"
-v "$DOCKER_CACHE_DIR/cargo:/cargo"
-v "$DOCKER_CACHE_DIR/target:/target"
-v "$ROOT/dist/docker-build:/out"
-w /src
)
if [[ "$RUN_LINUX_TESTS" == "1" ]]; then
echo "Running RustProxy tests in Linux builder container..."
docker run "${run_args[@]}" "$DOCKER_IMAGE" \
cargo test --locked --release
fi
echo "Cross-compiling RustProxy for x86_64-unknown-freebsd..."
docker run "${run_args[@]}" "$DOCKER_IMAGE" bash -lc '
set -euo pipefail
cargo build --locked --release --target x86_64-unknown-freebsd
install -m 0555 /target/x86_64-unknown-freebsd/release/rust-proxy /out/rust-proxy
desc="$(file -b /out/rust-proxy)"
echo "$desc"
header="$(llvm-readelf -h /out/rust-proxy)"
printf "%s\n" "$header" | grep -E "Class:|Machine:|OS/ABI:" || true
printf "%s\n" "$header" | grep -qi FreeBSD || { echo "Built ELF is not marked for FreeBSD" >&2; exit 1; }
dynamic="$(llvm-readelf -d /out/rust-proxy || true)"
printf "%s\n" "$dynamic" | grep NEEDED || true
if printf "%s\n" "$dynamic" | grep -q "libc\.so\.6"; then
echo "Linux libc detected in FreeBSD target binary" >&2
exit 1
fi
'
RUSTPROXY_BINARY="$ROOT/dist/docker-build/rust-proxy" \
"$ROOT/builder/debian/package-freebsd.sh"
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SOURCE_DIR="${RUSTPROXY_SOURCE_DIR:-$ROOT/app}"
TARGET="x86_64-unknown-freebsd"
if [[ ! -f "$SOURCE_DIR/Cargo.toml" ]]; then
echo "RustProxy source not found. Set RUSTPROXY_SOURCE_DIR to the dynamically fetched checkout." >&2
exit 1
fi
if ! command -v docker >/dev/null 2>&1; then
echo "Docker is required for cross mode." >&2
exit 1
fi
if ! command -v cross >/dev/null 2>&1; then
echo "cross is not installed. Install it with: cargo install cross --locked" >&2
exit 1
fi
cd "$SOURCE_DIR"
cross build --locked --release --target "$TARGET"
mkdir -p "$ROOT/dist/cross/$TARGET"
cp "target/$TARGET/release/rust-proxy" "$ROOT/dist/cross/$TARGET/rust-proxy"
echo "Cross-built binary: $ROOT/dist/cross/$TARGET/rust-proxy"
echo "Do not publish this as the production OPNsense package without validation on the target FreeBSD ABI."
+3 -3
View File
@@ -10,11 +10,11 @@ fi
source "$CONFIG_FILE"
: "${PACKAGING_REPO_URL:?PACKAGING_REPO_URL is required}"
: "${PACKAGING_REPO_REF:=main}"
: "${PACKAGING_WORKDIR:=/srv/rustproxy-opnsense/packaging}"
: "${PACKAGING_REPO_REF:=HEAD}"
: "${PACKAGING_WORKDIR:=/opt/opn-rustproxy/packaging}"
: "${APP_REPO_URL:=https://git.linuxiarz.pl/gru/RustProxy}"
: "${APP_REPO_REF:=HEAD}"
: "${APP_WORKDIR:=/srv/rustproxy-opnsense/source/RustProxy}"
: "${APP_WORKDIR:=/opt/opn-rustproxy/source/RustProxy}"
: "${STATE_DIR:=/var/lib/rustproxy-opnsense-builder}"
mkdir -p "$STATE_DIR" "$(dirname "$PACKAGING_WORKDIR")" "$(dirname "$APP_WORKDIR")"
-22
View File
@@ -1,22 +0,0 @@
#!/bin/bash
set -euo pipefail
if [[ ${EUID} -ne 0 ]]; then
echo "Run as root" >&2
exit 1
fi
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io rustup build-essential pkg-config
systemctl enable --now docker
TARGET_USER="${SUDO_USER:-root}"
if [[ "$TARGET_USER" == "root" ]]; then
rustup default stable
cargo install cross --locked
else
sudo -u "$TARGET_USER" -H sh -c 'rustup default stable && cargo install cross --locked'
fi
echo "Docker + rustup + cross installed for $TARGET_USER."
echo "Use BUILD_MODE=cross-binary only for development validation."
+14 -5
View File
@@ -9,21 +9,30 @@ fi
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates cron git openssh-client rsync util-linux curl jq
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates cron git openssh-client rsync util-linux curl jq \
docker.io zstd file python3
install -d -m 0755 /srv/rustproxy-opnsense
install -d -m 0755 /opt/opn-rustproxy
install -d -m 0755 /opt/opn-rustproxy/source
install -d -m 0755 /opt/opn-rustproxy/cache/cargo
install -d -m 0755 /var/lib/rustproxy-opnsense-builder
install -m 0755 "$REPO_ROOT/builder/debian/hourly-build.sh" /usr/local/sbin/rustproxy-opnsense-hourly
install -m 0644 "$REPO_ROOT/builder/debian/rustproxy-opnsense.cron" /etc/cron.d/rustproxy-opnsense
if [[ ! -e /etc/rustproxy-opnsense-builder.env ]]; then
install -m 0600 "$REPO_ROOT/builder.env.example" /etc/rustproxy-opnsense-builder.env
echo "Created /etc/rustproxy-opnsense-builder.env. Edit it before the first cron run."
echo "Created /etc/rustproxy-opnsense-builder.env"
else
echo "Keeping existing /etc/rustproxy-opnsense-builder.env"
echo "Compare it with: $REPO_ROOT/builder.env.example"
fi
touch /var/log/rustproxy-opnsense-builder.log
chmod 0640 /var/log/rustproxy-opnsense-builder.log
systemctl enable --now docker
systemctl enable --now cron
echo "Debian scheduler installed."
echo "Debian Docker builder installed."
echo "Edit /etc/rustproxy-opnsense-builder.env and run:"
echo " /usr/local/sbin/rustproxy-opnsense-hourly"
+150
View File
@@ -0,0 +1,150 @@
#!/bin/bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SOURCE_DIR="${RUSTPROXY_SOURCE_DIR:?RUSTPROXY_SOURCE_DIR is required}"
BINARY="${RUSTPROXY_BINARY:?RUSTPROXY_BINARY is required}"
OPNSENSE_ABI="${OPNSENSE_ABI:-26.7}"
FREEBSD_ABI="${FREEBSD_ABI:-FreeBSD:15:amd64}"
FREEBSD_ARCH="${FREEBSD_ARCH:-freebsd:15:x86:64}"
SOURCE_REVISION="${RUSTPROXY_SOURCE_REVISION:-0}"
SOURCE_COMMIT="${RUSTPROXY_SOURCE_COMMIT:-unknown}"
SOURCE_URL="${RUSTPROXY_SOURCE_URL:-https://git.linuxiarz.pl/gru/RustProxy}"
SOURCE_REF="${RUSTPROXY_SOURCE_REF:-HEAD}"
for tool in python3 sha256sum tar zstd file; do
command -v "$tool" >/dev/null 2>&1 || { echo "Missing tool: $tool" >&2; exit 1; }
done
[[ -f "$SOURCE_DIR/Cargo.toml" ]] || { echo "Missing Cargo.toml in $SOURCE_DIR" >&2; exit 1; }
[[ -f "$BINARY" ]] || { echo "Missing cross-built binary: $BINARY" >&2; exit 1; }
binary_desc="$(file -b "$BINARY")"
echo "$BINARY: $binary_desc"
if [[ "${ALLOW_NON_FREEBSD_BINARY:-0}" != "1" ]] && [[ "$binary_desc" != *FreeBSD* ]]; then
echo "Refusing to package a binary that is not identified as FreeBSD." >&2
exit 1
fi
app_version="$(awk -F '"' '/^version = "/ {print $2; exit}' "$SOURCE_DIR/Cargo.toml")"
[[ -n "$app_version" ]] || { echo "Cannot read app version" >&2; exit 1; }
if [[ "$SOURCE_REVISION" == "0" ]]; then
pkg_version="$app_version"
else
pkg_version="${app_version}_${SOURCE_REVISION}"
fi
work="$ROOT/dist/.package-work"
stage="$work/stage"
meta="$work/meta"
repo_dir="$ROOT/dist/packages/$OPNSENSE_ABI/amd64"
package_file="$repo_dir/os-rustproxy-${pkg_version}.pkg"
rm -rf "$work" "$ROOT/dist/packages"
mkdir -p "$stage/usr/local" "$meta" "$repo_dir"
# OPNsense plugin files map directly below /usr/local.
cp -a "$ROOT/opnsense-plugin/www/rustproxy/src/." "$stage/usr/local/"
mkdir -p "$stage/usr/local/sbin"
install -m 0555 "$BINARY" "$stage/usr/local/sbin/rust-proxy"
# Ensure executable hooks/scripts keep executable mode in the package.
chmod 0555 "$stage/usr/local/etc/rc.d/rustproxy"
chmod 0555 "$stage/usr/local/etc/rc.syshook.d/10-rustproxy.start"
chmod 0555 "$stage/usr/local/opnsense/scripts/rustproxy/service.sh"
POST_INSTALL_FILE="$ROOT/opnsense-plugin/www/rustproxy/+POST_INSTALL.pre"
PRE_DEINSTALL_FILE="$ROOT/opnsense-plugin/www/rustproxy/+PRE_DEINSTALL.pre"
python3 - "$stage" "$meta" "$pkg_version" "$FREEBSD_ABI" "$FREEBSD_ARCH" \
"$POST_INSTALL_FILE" "$PRE_DEINSTALL_FILE" <<'PY'
import hashlib
import json
import os
from pathlib import Path
import sys
stage = Path(sys.argv[1])
meta_dir = Path(sys.argv[2])
version = sys.argv[3]
abi = sys.argv[4]
arch = sys.argv[5]
post_install = Path(sys.argv[6]).read_text(encoding="utf-8")
pre_deinstall = Path(sys.argv[7]).read_text(encoding="utf-8")
files = {}
flatsize = 0
for p in sorted(stage.rglob("*")):
if p.is_symlink():
rel = "/" + p.relative_to(stage).as_posix()
files[rel] = "-"
elif p.is_file():
rel = "/" + p.relative_to(stage).as_posix()
files[rel] = hashlib.sha256(p.read_bytes()).hexdigest()
flatsize += p.stat().st_size
common = {
"name": "os-rustproxy",
"version": version,
"origin": "opnsense/os-rustproxy",
"comment": "RustProxy reverse proxy service",
"desc": "RustProxy packaged as an OPNsense plugin. The initial UI exposes Enabled/Disabled only.",
"maintainer": "local@localhost",
"www": "https://git.linuxiarz.pl/gru/RustProxy",
"prefix": "/usr/local",
"abi": abi,
"arch": arch,
"flatsize": flatsize,
"licenselogic": "single",
"licenses": ["UNKNOWN"],
"categories": ["www"],
"deps": {},
"options": {},
}
manifest = dict(common)
manifest["files"] = files
manifest["scripts"] = {
"post-install": post_install,
"pre-deinstall": pre_deinstall,
}
(meta_dir / "+COMPACT_MANIFEST").write_text(
json.dumps(common, separators=(",", ":"), ensure_ascii=False) + "\n",
encoding="utf-8",
)
(meta_dir / "+MANIFEST").write_text(
json.dumps(manifest, separators=(",", ":"), ensure_ascii=False) + "\n",
encoding="utf-8",
)
PY
archive="$work/os-rustproxy.tar"
rm -f "$archive" "$package_file"
tar --format=ustar --owner=0 --group=0 --numeric-owner \
-cf "$archive" -C "$meta" +COMPACT_MANIFEST +MANIFEST
tar --format=ustar --owner=0 --group=0 --numeric-owner \
-rf "$archive" -C "$stage" usr
zstd -q -f -19 -T0 "$archive" -o "$package_file"
# Convenience stable path for direct deployment.
cp -f "$package_file" "$ROOT/dist/os-rustproxy-latest.pkg"
(cd "$repo_dir" && sha256sum "$(basename "$package_file")") > "$ROOT/dist/SHA256"
cat > "$ROOT/dist/BUILD-INFO.txt" <<INFO
app_version=$app_version
package_version=$pkg_version
source_url=$SOURCE_URL
source_ref=$SOURCE_REF
source_commit=$SOURCE_COMMIT
opnsense_series=$OPNSENSE_ABI
freebsd_abi=$FREEBSD_ABI
freebsd_arch=$FREEBSD_ARCH
package=$(basename "$package_file")
INFO
# Structural verification available on Linux.
zstd -dc "$package_file" | tar -tf - >/dev/null
rm -rf "$work"
echo "Package: $package_file"
echo "Latest: $ROOT/dist/os-rustproxy-latest.pkg"