mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-30 15:48:34 +00:00
build-ipk.sh and build-apk.sh, run without --bin-dir, compiled with cargo zigbuild and then looked for the binaries under target/ in the checkout. A build with CARGO_TARGET_DIR, CARGO_BUILD_TARGET_DIR or a build.target-dir setting in any cargo config puts them elsewhere, so the builders failed after compiling, at `du: .../target/<triple>/release/fips: No such file or directory`. Both now ask `cargo metadata` for the target directory, read with sed so a build host needs no jq or Python, and stop with a clear error when it cannot be read rather than fall back to target/. The apk README describes packaging in an alpine:3.23 container, whose own apk-tools 3.0.8 builds packages OpenWrt 25.12 installs, instead of building apk-tools from source. It passes PKG_VERSION and APK_VERSION from the host, since without git in the container both fall back silently to versions that sort below an installed development build. Both READMEs give the flags that install or upgrade on a router with no internet access (`--no-network --repositories-file /dev/null`). package-test.sh builds both packages without --bin-dir against a cargo stub, once with each environment variable, and checks the packages hold the binaries cargo wrote there.
303 lines
11 KiB
Bash
Executable File
303 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# Build a FIPS .ipk package for OpenWrt without the OpenWrt SDK.
|
|
#
|
|
# Uses cargo-zigbuild for cross-compilation and assembles the .ipk directly.
|
|
# An .ipk is just an ar archive containing two tarballs — no SDK required.
|
|
#
|
|
# Usage:
|
|
# ./packaging/openwrt-ipk/build-ipk.sh [--arch <name>]
|
|
#
|
|
# Architectures (--arch):
|
|
# aarch64 GL.iNet MT3000/MT6000, RPi 3/4/5, most modern routers [default]
|
|
# mipsel Older MIPS routers (TP-Link, Netgear, GL.iNet AR750)
|
|
# mips MIPS big-endian routers (ath79)
|
|
# arm 32-bit ARM routers (Cortex-A7)
|
|
# x86_64 x86 routers / VMs
|
|
#
|
|
# Output: dist/fips_<PKG_VERSION>_<openwrt-arch>.ipk
|
|
#
|
|
# Environment:
|
|
# PKG_VERSION label for the file name (default: git describe)
|
|
# IPK_VERSION control file Version (default: PKG_VERSION). CI passes a
|
|
# release candidate as vX.Y.Z~rcN, which opkg sorts below the
|
|
# release, while PKG_VERSION keeps the tag's vX.Y.Z-rcN.
|
|
#
|
|
# Prerequisites:
|
|
# cargo install cargo-zigbuild
|
|
# rustup target add <rust-triple> (added automatically if missing)
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Arguments
|
|
# ---------------------------------------------------------------------------
|
|
|
|
ARCH="aarch64"
|
|
BIN_DIR="" # if set, use prebuilt binaries from here instead of compiling
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--arch) ARCH="$2"; shift 2 ;;
|
|
--arch=*) ARCH="${1#*=}"; shift ;;
|
|
--bin-dir) BIN_DIR="$2"; shift 2 ;;
|
|
--bin-dir=*) BIN_DIR="${1#*=}"; shift ;;
|
|
*) echo "Unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Architecture mapping
|
|
#
|
|
# RUST_TARGET — passed to cargo --target
|
|
# OPENWRT_ARCH — goes in the .ipk control file and filename
|
|
# ---------------------------------------------------------------------------
|
|
|
|
case "$ARCH" in
|
|
aarch64)
|
|
RUST_TARGET="aarch64-unknown-linux-musl"
|
|
OPENWRT_ARCH="aarch64_cortex-a53"
|
|
;;
|
|
mipsel)
|
|
RUST_TARGET="mipsel-unknown-linux-musl"
|
|
OPENWRT_ARCH="mipsel_24kc"
|
|
;;
|
|
mips)
|
|
RUST_TARGET="mips-unknown-linux-musl"
|
|
OPENWRT_ARCH="mips_24kc"
|
|
;;
|
|
arm)
|
|
RUST_TARGET="arm-unknown-linux-musleabihf"
|
|
OPENWRT_ARCH="arm_cortex-a7"
|
|
;;
|
|
x86_64)
|
|
RUST_TARGET="x86_64-unknown-linux-musl"
|
|
OPENWRT_ARCH="x86_64"
|
|
;;
|
|
*)
|
|
echo "Unknown arch: $ARCH" >&2
|
|
echo "Valid: aarch64, mipsel, mips, arm, x86_64" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Paths
|
|
# ---------------------------------------------------------------------------
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
FILES_DIR="$SCRIPT_DIR/files"
|
|
SCRIPTS_SRC="$SCRIPT_DIR/scripts" # maintainer scripts (metadata, not payload)
|
|
DIST_DIR="$PROJECT_ROOT/dist"
|
|
|
|
PKG_NAME="fips"
|
|
PKG_VERSION="${PKG_VERSION:-$(cd "$PROJECT_ROOT" && git describe --tags --always --dirty 2>/dev/null || echo "0.1.0")}"
|
|
IPK_VERSION="${IPK_VERSION:-$PKG_VERSION}"
|
|
|
|
if [ "$IPK_VERSION" = "$PKG_VERSION" ]; then
|
|
echo "==> Building $PKG_NAME $PKG_VERSION for $OPENWRT_ARCH ($RUST_TARGET)"
|
|
else
|
|
echo "==> Building $PKG_NAME $PKG_VERSION (control Version $IPK_VERSION) for $OPENWRT_ARCH ($RUST_TARGET)"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Obtain binaries
|
|
#
|
|
# Either use a directory of prebuilt binaries (--bin-dir; CI cross-compiles
|
|
# once in a shared job and hands them to both the .ipk and .apk packagers), or
|
|
# compile from source here for a self-contained local build.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
if [ -n "$BIN_DIR" ]; then
|
|
RELEASE_DIR="$BIN_DIR"
|
|
echo "==> Using prebuilt binaries from $RELEASE_DIR"
|
|
for bin in fips fipsctl fipstop fips-gateway; do
|
|
[ -f "$RELEASE_DIR/$bin" ] || {
|
|
echo "Error: prebuilt binary not found: $RELEASE_DIR/$bin" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
else
|
|
if ! command -v cargo-zigbuild &>/dev/null; then
|
|
echo "Error: cargo-zigbuild not found." >&2
|
|
echo " Install: cargo install cargo-zigbuild" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! rustup target list --installed | grep -q "^$RUST_TARGET$"; then
|
|
echo "==> Adding Rust target $RUST_TARGET..."
|
|
rustup target add "$RUST_TARGET"
|
|
fi
|
|
|
|
echo "==> Compiling..."
|
|
cd "$PROJECT_ROOT"
|
|
cargo zigbuild \
|
|
--release \
|
|
--target "$RUST_TARGET" \
|
|
--bin fips \
|
|
--bin fipsctl \
|
|
--bin fipstop \
|
|
--bin fips-gateway
|
|
|
|
# cargo writes under its target directory, which CARGO_TARGET_DIR,
|
|
# CARGO_BUILD_TARGET_DIR or build.target-dir in any config.toml can move
|
|
# away from target/, so ask cargo where it is. Read with sed, so a build
|
|
# host needs no jq or Python. Kept in sync with ../openwrt-apk/build-apk.sh.
|
|
TARGET_DIR="$(cargo metadata --format-version 1 --no-deps 2>/dev/null \
|
|
| sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p')" || TARGET_DIR=""
|
|
if [ -z "$TARGET_DIR" ] || [ ! -d "$TARGET_DIR" ]; then
|
|
echo "Error: could not read cargo's target directory from 'cargo metadata'" \
|
|
"(got '$TARGET_DIR')." >&2
|
|
exit 1
|
|
fi
|
|
RELEASE_DIR="$TARGET_DIR/$RUST_TARGET/release"
|
|
|
|
echo "==> Stripping binaries..."
|
|
STRIP="${LLVM_STRIP:-strip}"
|
|
for bin in fips fipsctl fipstop fips-gateway; do
|
|
"$STRIP" "$RELEASE_DIR/$bin" 2>/dev/null || true
|
|
done
|
|
fi
|
|
|
|
SIZE=$(du -sh "$RELEASE_DIR/fips" | cut -f1)
|
|
echo " fips: $SIZE"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Assemble .ipk
|
|
# ---------------------------------------------------------------------------
|
|
# An .ipk is an ar archive with three members:
|
|
# debian-binary — format version ("2.0\n")
|
|
# control.tar.gz — package metadata, conffiles, pre/post scripts
|
|
# data.tar.gz — the actual filesystem tree
|
|
|
|
WORK_DIR="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
|
|
|
CONTROL_DIR="$WORK_DIR/control"
|
|
DATA_DIR="$WORK_DIR/data"
|
|
mkdir -p "$CONTROL_DIR" "$DATA_DIR"
|
|
|
|
# ---- data tree ----
|
|
|
|
install -d "$DATA_DIR/usr/bin"
|
|
install -m 0755 "$RELEASE_DIR/fips" "$DATA_DIR/usr/bin/fips"
|
|
install -m 0755 "$RELEASE_DIR/fipsctl" "$DATA_DIR/usr/bin/fipsctl"
|
|
install -m 0755 "$RELEASE_DIR/fipstop" "$DATA_DIR/usr/bin/fipstop"
|
|
install -m 0755 "$RELEASE_DIR/fips-gateway" "$DATA_DIR/usr/bin/fips-gateway"
|
|
install -m 0755 "$FILES_DIR/usr/bin/fips-mesh-setup" "$DATA_DIR/usr/bin/fips-mesh-setup"
|
|
install -m 0755 "$FILES_DIR/usr/bin/fips-ap-setup" "$DATA_DIR/usr/bin/fips-ap-setup"
|
|
|
|
install -d "$DATA_DIR/etc/init.d"
|
|
install -m 0755 "$FILES_DIR/etc/init.d/fips" "$DATA_DIR/etc/init.d/fips"
|
|
install -m 0755 "$FILES_DIR/etc/init.d/fips-gateway" "$DATA_DIR/etc/init.d/fips-gateway"
|
|
|
|
install -d "$DATA_DIR/etc/fips"
|
|
install -m 0600 "$FILES_DIR/etc/fips/fips.yaml" "$DATA_DIR/etc/fips/fips.yaml"
|
|
|
|
install -d "$DATA_DIR/etc/sysctl.d"
|
|
install -m 0644 "$FILES_DIR/etc/sysctl.d/fips-bridge.conf" "$DATA_DIR/etc/sysctl.d/fips-bridge.conf"
|
|
install -m 0644 "$FILES_DIR/etc/sysctl.d/fips-gateway.conf" "$DATA_DIR/etc/sysctl.d/fips-gateway.conf"
|
|
|
|
# fw4 include: lets fips-gateway's port forwards through the fips zone. One
|
|
# source, installed for the zone's input and forward chains.
|
|
for chain in input_fips forward_fips; do
|
|
install -d "$DATA_DIR/usr/share/nftables.d/chain-post/$chain"
|
|
install -m 0644 "$FILES_DIR/usr/share/nftables.d/10-fips-port-forwards.nft" \
|
|
"$DATA_DIR/usr/share/nftables.d/chain-post/$chain/10-fips-port-forwards.nft"
|
|
done
|
|
# fw4 include: refuses ICMPv6 from wan into the mesh, ahead of the stock rule
|
|
# that accepts it into every zone.
|
|
install -d "$DATA_DIR/usr/share/nftables.d/chain-pre/forward_wan"
|
|
install -m 0644 "$FILES_DIR/usr/share/nftables.d/10-fips-wan-icmpv6.nft" \
|
|
"$DATA_DIR/usr/share/nftables.d/chain-pre/forward_wan/10-fips-wan-icmpv6.nft"
|
|
|
|
install -d "$DATA_DIR/etc/uci-defaults"
|
|
install -m 0755 "$FILES_DIR/etc/uci-defaults/90-fips-setup" "$DATA_DIR/etc/uci-defaults/90-fips-setup"
|
|
|
|
install -d "$DATA_DIR/lib/upgrade/keep.d"
|
|
install -m 0644 "$FILES_DIR/lib/upgrade/keep.d/fips" "$DATA_DIR/lib/upgrade/keep.d/fips"
|
|
|
|
# ---- control files ----
|
|
|
|
PKG_SIZE=$(du -sk "$DATA_DIR" | cut -f1)
|
|
|
|
cat > "$CONTROL_DIR/control" <<EOF
|
|
Package: $PKG_NAME
|
|
Version: $IPK_VERSION
|
|
Architecture: $OPENWRT_ARCH
|
|
Maintainer: FIPS Network
|
|
Section: net
|
|
Priority: optional
|
|
Depends: kmod-tun, kmod-br-netfilter, kmod-nft-nat, kmod-nf-conntrack, ip-full
|
|
Description: FIPS Mesh Network Daemon
|
|
Distributed, decentralized mesh networking over UDP, TCP, and raw Ethernet.
|
|
Provides a TUN interface (fips0) with ULA IPv6 addressing and a DNS
|
|
responder for .fips name resolution.
|
|
Installed-Size: $PKG_SIZE
|
|
EOF
|
|
|
|
# Mark fips.yaml as a conffile so opkg won't overwrite user edits on upgrade.
|
|
cat > "$CONTROL_DIR/conffiles" <<EOF
|
|
/etc/fips/fips.yaml
|
|
EOF
|
|
|
|
# The maintainer scripts live in files of their own rather than in heredocs
|
|
# here, so testing/openwrt/ can run the same bodies the package ships.
|
|
install -m 0755 "$SCRIPTS_SRC/postinst" "$CONTROL_DIR/postinst"
|
|
install -m 0755 "$SCRIPTS_SRC/prerm" "$CONTROL_DIR/prerm"
|
|
|
|
# ---- pack ----
|
|
|
|
PKG_FILENAME="${PKG_NAME}_${PKG_VERSION}_${OPENWRT_ARCH}.ipk"
|
|
IPK_WORK="$WORK_DIR/ipk"
|
|
mkdir -p "$IPK_WORK"
|
|
|
|
echo "2.0" > "$IPK_WORK/debian-binary"
|
|
|
|
# Detect a tar that supports --format=gnu.
|
|
# On macOS, Homebrew's GNU tar is installed as 'gtar'; the system tar is BSD.
|
|
# Our filenames are short so BSD tar (ustar) works too, but gnu is preferred
|
|
# to match ipkg-build exactly and to embed numeric UID/GID.
|
|
# COPYFILE_DISABLE=1 suppresses macOS resource-fork (._*) files; no-op on Linux.
|
|
if command -v gtar &>/dev/null; then
|
|
# Homebrew GNU tar on macOS
|
|
TAR_CMD="gtar"
|
|
TAR_EXTRA_FLAGS="--format=gnu --numeric-owner"
|
|
elif tar --version 2>/dev/null | grep -q 'GNU tar'; then
|
|
# System tar is GNU tar (Linux)
|
|
TAR_CMD="tar"
|
|
TAR_EXTRA_FLAGS="--format=gnu --numeric-owner"
|
|
else
|
|
# macOS BSD tar (libarchive). Its default format is PAX (typeflag 0x78),
|
|
# which OpenWrt's busybox tar cannot handle. Force ustar explicitly.
|
|
TAR_CMD="tar"
|
|
TAR_EXTRA_FLAGS="--format=ustar"
|
|
fi
|
|
|
|
ipk_tar() {
|
|
# ipk_tar <output.tar.gz> <source-dir> [paths...]
|
|
local out="$1" src="$2"; shift 2
|
|
local mtime_flags=""
|
|
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
|
|
mtime_flags="--mtime=@$SOURCE_DATE_EPOCH"
|
|
fi
|
|
COPYFILE_DISABLE=1 "$TAR_CMD" $TAR_EXTRA_FLAGS $mtime_flags -czf "$out" -C "$src" "$@"
|
|
}
|
|
|
|
ipk_tar "$IPK_WORK/control.tar.gz" "$CONTROL_DIR" .
|
|
ipk_tar "$IPK_WORK/data.tar.gz" "$DATA_DIR" .
|
|
|
|
# The outer .ipk container is a gzip-compressed tar — NOT an ar archive.
|
|
# (Debian .deb uses ar; OpenWrt .ipk uses tar.gz.)
|
|
# Entries must be named with ./ prefix, as ipkg-build produces.
|
|
mkdir -p "$DIST_DIR"
|
|
ipk_tar "$DIST_DIR/$PKG_FILENAME" "$IPK_WORK" ./debian-binary ./control.tar.gz ./data.tar.gz
|
|
|
|
echo ""
|
|
echo "==> Done: dist/$PKG_FILENAME"
|
|
echo " $(du -sh "$DIST_DIR/$PKG_FILENAME" | cut -f1)"
|
|
echo ""
|
|
echo "Install on router:"
|
|
echo " scp -O dist/$PKG_FILENAME root@192.168.1.1:/tmp/"
|
|
echo " ssh root@192.168.1.1 opkg install /tmp/$PKG_FILENAME"
|