Files
corrosion-admin-panel/.gitea/workflows/build-host-agent.yml
Vantz Stockwell 6b3e805ac2
Some checks failed
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 16s
CI / agent-tests (push) Successful in 1m27s
CI / integration (push) Successful in 21s
Build Host Agent (Rust) / build (push) Failing after 1m33s
feat(host-agent): Phase 3a signed self-update (minisign) + CI signing gate
Agent only ever runs a binary whose minisign signature verifies against
the EMBEDDED public key. NATS host.cmd func 'update' {url}: download
binary + .minisig from the CDN -> verify against embedded pubkey ->
atomic swap (.old rollback) -> relaunch. URL allowlist (https + cdn.
corrosionmgmt.com only, rejects userinfo-bypass), 100MiB cap. Closes the
supply-chain hole: even a malicious CDN upload can't run unsigned.

CI: build-host-agent.yml signs every artifact with MINISIGN_SECRET_KEY
(Gitea secret) and publishes .minisig alongside; the step FAILS the
build if the secret is absent (refuses to ship unsigned). Bumped to
alpha.6.

6 deterministic tests (accept valid / reject tampered+garbage+empty sig,
URL allowlist incl userinfo-bypass, atomic swap+rollback). Fixtures
signed with the real release key so tests need no key at runtime. Full
suite 50/50 green; musl + native build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:00:36 -04:00

143 lines
6.4 KiB
YAML

name: Build Host Agent (Rust)
# Rust agent ships on its own tag namespace (agent-v*) so it never collides
# with the legacy Go pipeline (v*.*.*). Artifacts publish to the CDN /alpha/
# channel — /host-agent/latest/ stays on the Go build until cutover.
on:
push:
tags:
- 'agent-v*'
jobs:
build:
runs-on: ubuntu-latest
env:
# Override the macOS toolchain names in corrosion-host-agent/.cargo/config.toml
# (real env beats the config [env] table).
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
CC_x86_64_unknown_linux_musl: musl-gcc
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/agent-v}" >> $GITHUB_OUTPUT
- name: Verify tag matches Cargo.toml
run: |
CARGO_VERSION=$(grep '^version' corrosion-host-agent/Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
if [ "${{ steps.version.outputs.VERSION }}" != "$CARGO_VERSION" ]; then
echo "Tag agent-v${{ steps.version.outputs.VERSION }} does not match Cargo.toml version $CARGO_VERSION"
exit 1
fi
# The Asgard runner executes jobs in a bare node:20-bullseye container
# (no Rust, no sudo, runs as root) — bootstrap the toolchain per-run,
# same pattern as actions/setup-go in the Go pipeline.
- name: Install Rust + cross toolchains
run: |
apt-get update -qq
apt-get install -y -qq build-essential musl-tools gcc-mingw-w64-x86-64 curl
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
"$HOME/.cargo/bin/rustup" target add x86_64-unknown-linux-musl x86_64-pc-windows-gnu
- name: Build Linux AMD64 (static musl)
run: |
cd corrosion-host-agent
cargo build --release --target x86_64-unknown-linux-musl
mkdir -p bin
cp target/x86_64-unknown-linux-musl/release/corrosion-host-agent bin/corrosion-host-agent-linux-amd64
chmod +x bin/corrosion-host-agent-linux-amd64
- name: Build Windows AMD64 (mingw)
run: |
cd corrosion-host-agent
cargo build --release --target x86_64-pc-windows-gnu
cp target/x86_64-pc-windows-gnu/release/corrosion-host-agent.exe bin/corrosion-host-agent-windows-amd64.exe
- name: Generate checksums
run: |
cd corrosion-host-agent/bin
sha256sum corrosion-host-agent-linux-amd64 > checksums.txt
sha256sum corrosion-host-agent-windows-amd64.exe >> checksums.txt
cat checksums.txt
- name: Sign artifacts (minisign)
env:
MINISIGN_SECRET_KEY: ${{ secrets.MINISIGN_SECRET_KEY }}
run: |
if [ -z "$MINISIGN_SECRET_KEY" ]; then
echo "::error::MINISIGN_SECRET_KEY secret is not set — refusing to publish unsigned agent artifacts."
exit 1
fi
apt-get install -y -qq minisign
printf '%s\n' "$MINISIGN_SECRET_KEY" > /tmp/sign.key
cd corrosion-host-agent/bin
# Passwordless key (-W generated); feed empty stdin so it never blocks.
for f in corrosion-host-agent-linux-amd64 corrosion-host-agent-windows-amd64.exe checksums.txt; do
minisign -S -s /tmp/sign.key -m "$f" -x "$f.minisig" < /dev/null
done
rm -f /tmp/sign.key
echo "signed: $(ls *.minisig)"
- name: Create Release
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
API_URL="${{ github.server_url }}/api/v1"
REPO="${{ github.repository }}"
VERSION="agent-v${{ steps.version.outputs.VERSION }}"
RESPONSE=$(curl -s -X POST \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${VERSION}\", \"name\": \"Corrosion Host Agent ${VERSION}\", \"body\": \"Rust host agent release ${VERSION}\", \"draft\": false, \"prerelease\": true}" \
"${API_URL}/repos/${REPO}/releases")
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*')
for f in corrosion-host-agent-linux-amd64 corrosion-host-agent-linux-amd64.minisig \
corrosion-host-agent-windows-amd64.exe corrosion-host-agent-windows-amd64.exe.minisig \
checksums.txt checksums.txt.minisig; do
curl -s -X POST \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @corrosion-host-agent/bin/$f \
"${API_URL}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=$f"
done
- name: Upload to CDN (alpha channel)
run: |
CDN_URL="https://cdn.corrosionmgmt.com"
VERSION="${{ steps.version.outputs.VERSION }}"
for f in corrosion-host-agent-linux-amd64 corrosion-host-agent-linux-amd64.minisig \
corrosion-host-agent-windows-amd64.exe corrosion-host-agent-windows-amd64.exe.minisig \
checksums.txt checksums.txt.minisig; do
curl -s -X POST \
-F "file=@corrosion-host-agent/bin/$f" \
"${CDN_URL}/host-agent/alpha/$f"
curl -s -X POST \
-F "file=@corrosion-host-agent/bin/$f" \
"${CDN_URL}/host-agent/${VERSION}/$f"
done
echo "CDN upload complete: ${CDN_URL}/host-agent/alpha/"
- name: Build Summary
run: |
echo "## Corrosion Host Agent (Rust) Build Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "**Commit:** ${GITHUB_SHA:0:7}" >> $GITHUB_STEP_SUMMARY
echo "**Channel:** alpha (latest/ untouched until cutover)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Built Artifacts:" >> $GITHUB_STEP_SUMMARY
echo "- Linux AMD64 static musl ($(stat -c%s corrosion-host-agent/bin/corrosion-host-agent-linux-amd64) bytes)" >> $GITHUB_STEP_SUMMARY
echo "- Windows AMD64 mingw ($(stat -c%s corrosion-host-agent/bin/corrosion-host-agent-windows-amd64.exe) bytes)" >> $GITHUB_STEP_SUMMARY
echo "- SHA256 checksums" >> $GITHUB_STEP_SUMMARY