ci: cross-compile FreeRDP3 from source via MinGW + sign all binaries
Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Has been cancelled

- Downloads FreeRDP 3.24.0 source, cross-compiles to Windows DLLs via
  MinGW toolchain on Linux CI runner
- Signs wraith.exe AND all FreeRDP DLLs with Azure Key Vault EV cert
- DLL SHA256 hashes included in version.json manifest
- Fixed repo clone URL (vstockwell/wraith not vigilcyber/wraith)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-17 07:54:41 -04:00
parent 521ce2fa21
commit 531b3e1480

View File

@ -1,9 +1,9 @@
# ============================================================================= # =============================================================================
# Wraith — Build & Sign Release # Wraith — Build & Sign Release
# ============================================================================= # =============================================================================
# Builds the Wails v3 desktop app for Windows amd64, signs it with an # Builds the Wails v3 desktop app for Windows amd64, cross-compiles FreeRDP3
# Azure Key Vault code-signing certificate via jsign, then uploads the # from source via MinGW, signs everything with Azure Key Vault EV cert,
# signed binary and version manifest as release artifacts. # then uploads to SeaweedFS.
# #
# Trigger: push a tag matching v* (e.g. v1.0.0) or run manually. # Trigger: push a tag matching v* (e.g. v1.0.0) or run manually.
# #
@ -35,7 +35,7 @@ jobs:
- name: Checkout code - name: Checkout code
run: | run: |
git clone --depth 1 --branch ${{ github.ref_name }} \ git clone --depth 1 --branch ${{ github.ref_name }} \
https://${{ secrets.GIT_TOKEN }}@git.command.vigilcyber.com/vigilcyber/wraith.git . https://${{ secrets.GIT_TOKEN }}@git.command.vigilcyber.com/vstockwell/wraith.git .
# --------------------------------------------------------------- # ---------------------------------------------------------------
# Extract version from tag # Extract version from tag
@ -43,27 +43,107 @@ jobs:
- name: Get version from tag - name: Get version from tag
id: version id: version
run: | run: |
# Strip leading "v" from the tag (v1.2.3 -> 1.2.3)
TAG=$(echo "${{ github.ref_name }}" | sed 's/^v//') TAG=$(echo "${{ github.ref_name }}" | sed 's/^v//')
echo "version=${TAG}" >> $GITHUB_OUTPUT echo "version=${TAG}" >> $GITHUB_OUTPUT
echo "Building version: ${TAG}" echo "Building version: ${TAG}"
# --------------------------------------------------------------- # ---------------------------------------------------------------
# Install toolchain: Go, Node, npm # Install toolchain
# --------------------------------------------------------------- # ---------------------------------------------------------------
- name: Install Node.js - name: Install build dependencies
run: | run: |
# Install Node.js LTS if not present apt-get update -qq
apt-get install -y -qq \
mingw-w64 mingw-w64-tools binutils-mingw-w64 \
cmake ninja-build nasm meson \
default-jre-headless \
python3 awscli
# Node.js
if ! command -v node >/dev/null 2>&1; then if ! command -v node >/dev/null 2>&1; then
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y -qq nodejs apt-get install -y -qq nodejs
fi fi
node --version
npm --version
# --------------------------------------------------------------- echo "=== Toolchain versions ==="
# Build frontend assets go version
# --------------------------------------------------------------- node --version
x86_64-w64-mingw32-gcc --version | head -1
cmake --version | head -1
# ===============================================================
# FreeRDP3 — Cross-compile from source via MinGW
# ===============================================================
- name: Build FreeRDP3 for Windows (MinGW cross-compile)
run: |
FREERDP_VERSION="3.24.0"
echo "=== Building FreeRDP ${FREERDP_VERSION} for Windows amd64 via MinGW ==="
# Download FreeRDP source
curl -sSL -o /tmp/freerdp.tar.gz \
"https://github.com/FreeRDP/FreeRDP/archive/refs/tags/${FREERDP_VERSION}.tar.gz"
tar -xzf /tmp/freerdp.tar.gz -C /tmp
cd /tmp/FreeRDP-${FREERDP_VERSION}
# Create MinGW toolchain file
cat > /tmp/mingw-toolchain.cmake << 'TCEOF'
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
TCEOF
# Configure — minimal client-only build (no server, no extras)
cmake -B build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=/tmp/mingw-toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/tmp/freerdp-install \
-DBUILD_SHARED_LIBS=ON \
-DWITH_CLIENT=ON \
-DWITH_SERVER=OFF \
-DWITH_SHADOW=OFF \
-DWITH_PROXY=OFF \
-DWITH_SAMPLE=OFF \
-DWITH_PLATFORM_SERVER=OFF \
-DWITH_WINPR_TOOLS=OFF \
-DWITH_FFMPEG=OFF \
-DWITH_SWSCALE=OFF \
-DWITH_CAIRO=OFF \
-DWITH_CUPS=OFF \
-DWITH_PULSE=OFF \
-DWITH_ALSA=OFF \
-DWITH_OSS=OFF \
-DWITH_WAYLAND=OFF \
-DWITH_X11=OFF \
-DCHANNEL_URBDRC=OFF \
-DWITH_OPENH264=OFF
# Build
cmake --build build --parallel $(nproc)
cmake --install build
echo "=== FreeRDP3 DLLs built ==="
ls -la /tmp/freerdp-install/bin/*.dll 2>/dev/null || ls -la /tmp/freerdp-install/lib/*.dll 2>/dev/null || echo "Checking build output..."
find /tmp/freerdp-install -name "*.dll" -type f
- name: Stage FreeRDP3 DLLs
run: |
mkdir -p dist
# Copy all FreeRDP DLLs (MinGW produces lib-prefixed names)
find /tmp/freerdp-install -name "*.dll" -type f -exec cp {} dist/ \;
echo "=== Staged DLLs ==="
ls -la dist/*.dll 2>/dev/null || echo "No DLLs found — FreeRDP build may have failed"
# ===============================================================
# Build Wraith
# ===============================================================
- name: Build frontend - name: Build frontend
run: | run: |
cd frontend cd frontend
@ -72,22 +152,11 @@ jobs:
echo "Frontend build complete:" echo "Frontend build complete:"
ls -la dist/ ls -la dist/
# ---------------------------------------------------------------
# Build Windows amd64 binary
# ---------------------------------------------------------------
# Wails v3 cross-compilation from Linux to Windows requires CGO
# for the webview2 bindings. Rather than pull in a full MinGW
# cross-compiler, we build the Go binary directly — the frontend
# assets are embedded via //go:embed in main.go, so the result is
# a single self-contained .exe.
# ---------------------------------------------------------------
- name: Build wraith.exe (Windows amd64) - name: Build wraith.exe (Windows amd64)
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
echo "=== Cross-compiling wraith.exe for Windows amd64 ===" echo "=== Cross-compiling wraith.exe for Windows amd64 ==="
mkdir -p dist
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \ GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \
go build \ go build \
-ldflags="-s -w -X main.version=${VERSION}" \ -ldflags="-s -w -X main.version=${VERSION}" \
@ -96,64 +165,18 @@ jobs:
ls -la dist/wraith.exe ls -la dist/wraith.exe
# --------------------------------------------------------------- # ===============================================================
# FreeRDP3 DLLs — runtime dependency for the real RDP backend # Code signing — jsign + Azure Key Vault (EV cert)
# --------------------------------------------------------------- # ===============================================================
- name: Download FreeRDP3 DLLs
run: |
echo "=== Downloading FreeRDP3 pre-built DLLs for Windows amd64 ==="
FREERDP_VERSION="3.12.0"
# FreeRDP releases pre-built Windows binaries on GitHub.
# We need: libfreerdp3.dll, libwinpr3.dll, libfreerdp-client3.dll
#
# Option 1: Download from FreeRDP GitHub releases
# curl -sSL -o /tmp/freerdp.zip \
# "https://github.com/FreeRDP/FreeRDP/releases/download/${FREERDP_VERSION}/FreeRDP-${FREERDP_VERSION}-win64.zip"
# unzip /tmp/freerdp.zip -d /tmp/freerdp
# cp /tmp/freerdp/bin/libfreerdp3.dll dist/
# cp /tmp/freerdp/bin/libwinpr3.dll dist/
# cp /tmp/freerdp/bin/libfreerdp-client3.dll dist/
#
# Option 2: Build from source with cmake + MSVC cross-compiler
# For the initial CI run, the app will fall back to MockBackend
# until the DLLs are provided alongside wraith.exe.
echo "FreeRDP3 DLLs not yet configured — app will use mock RDP backend"
echo "To enable real RDP: place libfreerdp3.dll, libwinpr3.dll, libfreerdp-client3.dll alongside wraith.exe"
# When DLLs are available, sign them alongside wraith.exe:
# for dll in dist/lib*.dll; do
# java -jar /usr/local/bin/jsign.jar \
# --storetype AZUREKEYVAULT \
# --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" \
# --storepass "${{ steps.azure-token.outputs.token }}" \
# --alias "${{ secrets.AZURE_CERT_NAME }}" \
# --tsaurl http://timestamp.digicert.com \
# --tsmode RFC3161 \
# "$dll"
# done
# ---------------------------------------------------------------
# Code signing — jsign + Azure Key Vault
# ---------------------------------------------------------------
- name: Install jsign - name: Install jsign
run: | run: |
# jsign: Java-based Authenticode signing tool that runs on
# Linux and supports Azure Key Vault as a keystore.
JSIGN_VERSION="7.0" JSIGN_VERSION="7.0"
curl -sSL -o /usr/local/bin/jsign.jar \ curl -sSL -o /usr/local/bin/jsign.jar \
"https://github.com/ebourg/jsign/releases/download/${JSIGN_VERSION}/jsign-${JSIGN_VERSION}.jar" "https://github.com/ebourg/jsign/releases/download/${JSIGN_VERSION}/jsign-${JSIGN_VERSION}.jar"
# Ensure a JRE is available (jsign needs Java)
command -v java >/dev/null 2>&1 || {
apt-get update -qq && apt-get install -y -qq default-jre-headless
}
- name: Get Azure Key Vault access token - name: Get Azure Key Vault access token
id: azure-token id: azure-token
run: | run: |
# OAuth2 client credentials flow for Azure Key Vault access
TOKEN=$(curl -s -X POST \ TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/${{ secrets.AZURE_TENANT_ID }}/oauth2/v2.0/token" \ "https://login.microsoftonline.com/${{ secrets.AZURE_TENANT_ID }}/oauth2/v2.0/token" \
-d "client_id=${{ secrets.AZURE_CLIENT_ID }}" \ -d "client_id=${{ secrets.AZURE_CLIENT_ID }}" \
@ -164,9 +187,12 @@ jobs:
echo "::add-mask::${TOKEN}" echo "::add-mask::${TOKEN}"
echo "token=${TOKEN}" >> $GITHUB_OUTPUT echo "token=${TOKEN}" >> $GITHUB_OUTPUT
- name: Sign wraith.exe - name: Sign all Windows binaries
run: | run: |
echo "=== Signing wraith.exe with Azure Key Vault certificate ===" echo "=== Signing all .exe and .dll files with EV certificate ==="
for binary in dist/*.exe dist/*.dll; do
[ -f "$binary" ] || continue
echo "Signing: $binary"
java -jar /usr/local/bin/jsign.jar \ java -jar /usr/local/bin/jsign.jar \
--storetype AZUREKEYVAULT \ --storetype AZUREKEYVAULT \
--keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" \ --keystore "${{ secrets.AZURE_KEY_VAULT_URL }}" \
@ -174,35 +200,49 @@ jobs:
--alias "${{ secrets.AZURE_CERT_NAME }}" \ --alias "${{ secrets.AZURE_CERT_NAME }}" \
--tsaurl http://timestamp.digicert.com \ --tsaurl http://timestamp.digicert.com \
--tsmode RFC3161 \ --tsmode RFC3161 \
dist/wraith.exe "$binary"
echo "Signing complete." echo "Signed: $binary"
done
# --------------------------------------------------------------- # ===============================================================
# Version manifest # Version manifest
# --------------------------------------------------------------- # ===============================================================
- name: Create version.json - name: Create version.json
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
SHA256=$(sha256sum dist/wraith.exe | awk '{print $1}') EXE_SHA=$(sha256sum dist/wraith.exe | awk '{print $1}')
# Build DLL manifest
DLL_ENTRIES=""
for dll in dist/*.dll; do
[ -f "$dll" ] || continue
DLL_NAME=$(basename "$dll")
DLL_SHA=$(sha256sum "$dll" | awk '{print $1}')
DLL_ENTRIES="${DLL_ENTRIES} \"${DLL_NAME}\": \"${DLL_SHA}\",
"
done
cat > dist/version.json << EOF cat > dist/version.json << EOF
{ {
"version": "${VERSION}", "version": "${VERSION}",
"filename": "wraith.exe", "filename": "wraith.exe",
"sha256": "${SHA256}", "sha256": "${EXE_SHA}",
"platform": "windows", "platform": "windows",
"architecture": "amd64", "architecture": "amd64",
"released": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")", "released": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"signed": true "signed": true,
"dlls": {
${DLL_ENTRIES} "_note": "All DLLs are EV code-signed"
}
} }
EOF EOF
echo "=== version.json ===" echo "=== version.json ==="
cat dist/version.json cat dist/version.json
# --------------------------------------------------------------- # ===============================================================
# Upload release artifacts # Upload release artifacts
# --------------------------------------------------------------- # ===============================================================
- name: Upload release artifacts - name: Upload release artifacts
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
@ -214,10 +254,13 @@ jobs:
aws s3 cp dist/ "s3://agents/wraith/${VERSION}/windows/amd64/" \ aws s3 cp dist/ "s3://agents/wraith/${VERSION}/windows/amd64/" \
--recursive --endpoint-url "$ENDPOINT" --no-sign-request --recursive --endpoint-url "$ENDPOINT" --no-sign-request
# Latest path (overwritten on each release) # Latest path
aws s3 sync dist/ "s3://agents/wraith/latest/windows/amd64/" \ aws s3 sync dist/ "s3://agents/wraith/latest/windows/amd64/" \
--delete --endpoint-url "$ENDPOINT" --no-sign-request --delete --endpoint-url "$ENDPOINT" --no-sign-request
echo "=== Upload complete ===" echo "=== Upload complete ==="
echo "Versioned: ${ENDPOINT}/agents/wraith/${VERSION}/windows/amd64/" echo "Versioned: ${ENDPOINT}/agents/wraith/${VERSION}/windows/amd64/"
echo "Latest: ${ENDPOINT}/agents/wraith/latest/windows/amd64/" echo "Latest: ${ENDPOINT}/agents/wraith/latest/windows/amd64/"
echo ""
echo "=== Contents ==="
ls -la dist/