* feat: add static musl binary builds for Linux (#103) * feat: add aarch64 static builds, clean up docs
This commit is contained in:
@@ -185,10 +185,69 @@ jobs:
|
||||
path: rustnet-${{ github.ref_name }}-x86_64-unknown-freebsd.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
build-static:
|
||||
name: build-static-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
runner: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
- arch: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-musl
|
||||
container:
|
||||
image: rust:alpine
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apk add --no-cache \
|
||||
musl-dev libpcap-dev pkgconfig build-base perl \
|
||||
elfutils-dev zlib-dev zlib-static zstd-dev zstd-static \
|
||||
clang llvm linux-headers git
|
||||
rustup component add rustfmt
|
||||
|
||||
- name: Build static binary
|
||||
env:
|
||||
# -C strip=symbols: Strip debug symbols for smaller binary
|
||||
# -C link-arg=-l:libzstd.a: Fix elfutils 0.189+ zstd dependency (libbpf/bpftool#152)
|
||||
RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a"
|
||||
run: cargo build --release
|
||||
|
||||
- name: Verify static linking
|
||||
run: |
|
||||
file target/release/rustnet
|
||||
# Use file command to verify (ldd behaves differently inside Alpine)
|
||||
file target/release/rustnet | grep -q "static.* linked" || \
|
||||
(echo "ERROR: Binary is not statically linked" && exit 1)
|
||||
|
||||
- name: Create release archive
|
||||
run: |
|
||||
staging="rustnet-${{ github.ref_name }}-${{ matrix.target }}"
|
||||
mkdir -p "$staging/assets"
|
||||
|
||||
cp target/release/rustnet "$staging/"
|
||||
cp assets/services "$staging/assets/" 2>/dev/null || true
|
||||
cp README.md "$staging/"
|
||||
cp LICENSE "$staging/" 2>/dev/null || true
|
||||
|
||||
tar czf "$staging.tar.gz" "$staging"
|
||||
|
||||
- name: Upload static build artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: build-${{ matrix.target }}
|
||||
path: rustnet-${{ github.ref_name }}-${{ matrix.target }}.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
create-release:
|
||||
name: create-release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-release, build-freebsd]
|
||||
needs: [build-release, build-freebsd, build-static]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Dockerfile for building static musl-linked RustNet binary
|
||||
#
|
||||
# Usage (with eBPF - default, recommended):
|
||||
# docker build -f Dockerfile.static -t rustnet-static .
|
||||
# docker run --rm -v $(pwd)/dist:/dist rustnet-static cp /build/target/release/rustnet /dist/
|
||||
#
|
||||
# Usage (without eBPF - smaller binary, ~5.2MB vs ~6.5MB):
|
||||
# docker build -f Dockerfile.static --build-arg FEATURES="--no-default-features" -t rustnet-static .
|
||||
|
||||
FROM rust:alpine
|
||||
|
||||
ARG FEATURES=""
|
||||
|
||||
# Install build dependencies
|
||||
# - musl-dev: musl C library headers
|
||||
# - libpcap-dev: libpcap headers and static library (/usr/lib/libpcap.a)
|
||||
# - pkgconfig: for finding library paths
|
||||
# - build-base: basic build tools (make, gcc, etc.)
|
||||
# - perl: required by some build scripts (ring crate)
|
||||
# - elfutils-dev: libelf for eBPF (includes static library)
|
||||
# - zlib-dev/zlib-static: compression library
|
||||
# - zstd-dev/zstd-static: Zstandard compression (required by elfutils 0.189+)
|
||||
# - clang/llvm: for eBPF compilation
|
||||
# - linux-headers: kernel headers for eBPF
|
||||
RUN apk add --no-cache \
|
||||
musl-dev \
|
||||
libpcap-dev \
|
||||
pkgconfig \
|
||||
build-base \
|
||||
perl \
|
||||
elfutils-dev \
|
||||
zlib-dev \
|
||||
zlib-static \
|
||||
zstd-dev \
|
||||
zstd-static \
|
||||
clang \
|
||||
llvm \
|
||||
linux-headers
|
||||
|
||||
# Add rustfmt for eBPF skeleton generation
|
||||
RUN rustup component add rustfmt
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Configure static linking for zstd
|
||||
# This fixes the elfutils 0.189+ undeclared dependency on zstd
|
||||
# See: https://github.com/libbpf/bpftool/issues/152
|
||||
RUN mkdir -p .cargo && printf '[target.x86_64-unknown-linux-musl]\nrustflags = ["-C", "link-arg=-l:libzstd.a"]\n' > .cargo/config.toml
|
||||
|
||||
# Build configuration:
|
||||
# - In Alpine/musl, binaries are statically linked by default
|
||||
# - FEATURES arg controls whether eBPF is included (default) or disabled
|
||||
# - --release: Optimized build
|
||||
RUN cargo build --release ${FEATURES}
|
||||
|
||||
# Verify the binary is statically linked
|
||||
RUN file target/release/rustnet && \
|
||||
ldd target/release/rustnet 2>&1 || echo "Binary is statically linked"
|
||||
+28
@@ -216,6 +216,34 @@ sudo setcap 'cap_net_raw,cap_bpf,cap_perfmon=eip' $(brew --prefix)/bin/rustnet
|
||||
rustnet
|
||||
```
|
||||
|
||||
#### Static Binary (Portable - Any Linux Distribution)
|
||||
|
||||
For maximum portability, static binaries are available that work on **any Linux distribution** regardless of GLIBC version. These are fully self-contained and require no system dependencies.
|
||||
|
||||
```bash
|
||||
# Download the static binary for your architecture:
|
||||
# - rustnet-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz (x86_64)
|
||||
# - rustnet-vX.Y.Z-aarch64-unknown-linux-musl.tar.gz (ARM64)
|
||||
|
||||
# Extract the archive
|
||||
tar xzf rustnet-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz
|
||||
|
||||
# Move binary to PATH
|
||||
sudo mv rustnet-vX.Y.Z-x86_64-unknown-linux-musl/rustnet /usr/local/bin/
|
||||
|
||||
# Grant capabilities (modern kernel 5.8+)
|
||||
sudo setcap 'cap_net_raw,cap_bpf,cap_perfmon=eip' /usr/local/bin/rustnet
|
||||
|
||||
# Run without sudo
|
||||
rustnet
|
||||
```
|
||||
|
||||
**When to use static binaries:**
|
||||
- Older distributions with outdated GLIBC (e.g., CentOS 7, older Ubuntu)
|
||||
- Minimal/containerized environments
|
||||
- Air-gapped systems where installing dependencies is difficult
|
||||
- When you want a single portable binary
|
||||
|
||||
### FreeBSD Installation
|
||||
|
||||
FreeBSD support is available starting from version 0.15.0.
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
# musl Static Build Challenges
|
||||
|
||||
This document explains why RustNet currently does not provide musl static builds and the technical challenges encountered during implementation attempts.
|
||||
|
||||
## Background
|
||||
|
||||
musl is a lightweight C standard library designed for static linking. It would allow RustNet to produce fully static binaries that work on any Linux distribution regardless of GLIBC version.
|
||||
|
||||
## Why We Attempted musl Builds
|
||||
|
||||
GitHub issue #40 reported that pre-built packages required GLIBC 2.38/2.39, which wasn't available on PopOS 22.04 (GLIBC 2.35). musl builds would theoretically solve this by creating fully static binaries.
|
||||
|
||||
## Challenges Encountered
|
||||
|
||||
### libpcap Linking Issues
|
||||
|
||||
The primary challenge appears to be related to **libpcap** static linking with musl:
|
||||
|
||||
- Installing `libpcap-dev` in Ubuntu-based cross-rs containers provides glibc-linked libraries
|
||||
- Attempting to statically link these with musl resulted in linker errors
|
||||
- Errors included undefined references to pthread, math (exp), and dynamic loading functions (dladdr)
|
||||
|
||||
It's unclear whether this is due to:
|
||||
- Fundamental glibc/musl incompatibility when statically linking
|
||||
- Missing library specifications in the linker flags
|
||||
- Issues with how cross-rs musl images are configured
|
||||
- Something specific to our build configuration
|
||||
|
||||
### eBPF Complications
|
||||
|
||||
We initially attempted to include eBPF support, which required vendoring libelf and zlib. This was abandoned to simplify the problem, but even without eBPF the libpcap linking issues persisted.
|
||||
|
||||
## Current Solution
|
||||
|
||||
**We solved the original issue by pinning builds to ubuntu-22.04** (GLIBC 2.35), which ensures compatibility with PopOS 22.04 and similar distributions.
|
||||
|
||||
For users on older distributions, the `cargo install` workaround is documented:
|
||||
```bash
|
||||
cargo install rustnet-monitor
|
||||
sudo setcap 'cap_net_raw,cap_bpf,cap_perfmon=eip' ~/.cargo/bin/rustnet
|
||||
```
|
||||
|
||||
## Potential Future Approaches
|
||||
|
||||
If someone wants to tackle musl builds in the future, areas to investigate:
|
||||
|
||||
1. **Building libpcap from source** targeting musl in the pre-build step
|
||||
2. **Using Alpine Linux-based images** which have native musl packages
|
||||
3. **Custom linker flags** to properly link required libraries
|
||||
4. **Alternative pure-Rust packet capture** libraries (if they exist)
|
||||
|
||||
We're uncertain which approach would work best, or if there are other issues we haven't discovered yet.
|
||||
|
||||
## Why We're Not Pursuing This Now
|
||||
|
||||
- The ubuntu-22.04 solution already addresses the reported issue
|
||||
- The complexity-to-benefit ratio seems high
|
||||
- `cargo install` provides a universal fallback for edge cases
|
||||
- More investigation would be needed to understand the root causes
|
||||
|
||||
If you have experience with musl static linking and want to contribute, we'd welcome the help!
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-10-09*
|
||||
*Status: Not currently pursuing due to linking complexity*
|
||||
Reference in New Issue
Block a user