Add release verification workflow
This commit is contained in:
parent
61471f033d
commit
6ca8ac8469
4 changed files with 275 additions and 0 deletions
69
.github/workflows/release-verification.yml
vendored
Normal file
69
.github/workflows/release-verification.yml
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
name: Release Verification
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release tag to verify'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout tests
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
tests/release-verification
|
||||||
|
tests/contracts/monitor-snapshot-v1.txt
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
|
- name: Setup cosign
|
||||||
|
uses: sigstore/cosign-installer@v3.5.0
|
||||||
|
|
||||||
|
- name: Determine Tag
|
||||||
|
id: tag
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "tag=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Verify Assets List
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
bash tests/release-verification/verify-assets.sh "${{ steps.tag.outputs.tag }}"
|
||||||
|
|
||||||
|
- name: Isolate Workspace
|
||||||
|
run: |
|
||||||
|
# Move tests to a safe location and clean the workspace
|
||||||
|
# This ensures the installation does not accidentally use source files
|
||||||
|
cp -r tests /tmp/isolated_tests
|
||||||
|
rm -rf * .git
|
||||||
|
|
||||||
|
- name: Run Positive Installation
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
mkdir -p workspace
|
||||||
|
cd workspace
|
||||||
|
# Bring contract back for verification script
|
||||||
|
mkdir -p tests/contracts
|
||||||
|
cp /tmp/isolated_tests/contracts/monitor-snapshot-v1.txt tests/contracts/
|
||||||
|
bash /tmp/isolated_tests/release-verification/run-positive-installation.sh "${{ steps.tag.outputs.tag }}"
|
||||||
|
|
||||||
|
- name: Run Negative Tests
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
mkdir -p workspace-neg
|
||||||
|
cd workspace-neg
|
||||||
|
bash /tmp/isolated_tests/release-verification/run-negative-tests.sh "${{ steps.tag.outputs.tag }}"
|
||||||
66
tests/release-verification/run-negative-tests.sh
Normal file
66
tests/release-verification/run-negative-tests.sh
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAG="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$TAG" ]]; then
|
||||||
|
echo "Usage: $0 <tag>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Running negative tests against release $TAG..."
|
||||||
|
|
||||||
|
# We will need smm-setup.sh or ochenstarik-server-monitor-manager.sh
|
||||||
|
# We'll download ochenstarik-server-monitor-manager.sh directly to test verify-release
|
||||||
|
gh release download "$TAG" -p 'ochenstarik-server-monitor-manager.sh'
|
||||||
|
chmod +x ochenstarik-server-monitor-manager.sh
|
||||||
|
|
||||||
|
ARCHIVE="server-monitor-manager-linux-$(uname -m | sed -e 's/x86_64/x64/' -e 's/aarch64/arm64/').tar.gz"
|
||||||
|
gh release download "$TAG" -p "$ARCHIVE"
|
||||||
|
gh release download "$TAG" -p "server-monitor-manager-manifest.json"
|
||||||
|
gh release download "$TAG" -p "server-monitor-manager-manifest.sig"
|
||||||
|
|
||||||
|
echo "Test 1: Altered byte in archive"
|
||||||
|
cp "$ARCHIVE" "corrupted-$ARCHIVE"
|
||||||
|
echo "corrupted" >> "corrupted-$ARCHIVE"
|
||||||
|
if ./ochenstarik-server-monitor-manager.sh verify-release "corrupted-$ARCHIVE" >/dev/null 2>&1; then
|
||||||
|
echo "FAIL: Altered archive was accepted!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: Altered archive rejected."
|
||||||
|
rm "corrupted-$ARCHIVE"
|
||||||
|
|
||||||
|
echo "Test 2: Substituted hash in manifest without resigning"
|
||||||
|
cp server-monitor-manager-manifest.json corrupted-manifest.json
|
||||||
|
# Replace all hashes with zeros
|
||||||
|
sed -i 's/"[a-f0-9]\{64\}"/"0000000000000000000000000000000000000000000000000000000000000000"/g' corrupted-manifest.json
|
||||||
|
# Test verify-manifest directly
|
||||||
|
if ./ochenstarik-server-monitor-manager.sh verify-manifest corrupted-manifest.json server-monitor-manager-manifest.sig >/dev/null 2>&1; then
|
||||||
|
echo "FAIL: Manifest with substituted hash accepted!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: Substituted hash rejected."
|
||||||
|
rm corrupted-manifest.json
|
||||||
|
|
||||||
|
echo "Test 3: Manifest without signature"
|
||||||
|
# We just pass an empty string for the signature file argument
|
||||||
|
if ./ochenstarik-server-monitor-manager.sh verify-manifest server-monitor-manager-manifest.json "" >/dev/null 2>&1; then
|
||||||
|
echo "FAIL: Manifest without signature accepted!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: Missing signature rejected."
|
||||||
|
|
||||||
|
echo "Test 4: Signature made by another identity"
|
||||||
|
# Generate a local keypair and sign the manifest
|
||||||
|
export COSIGN_PASSWORD=""
|
||||||
|
cosign generate-key-pair
|
||||||
|
cosign sign-blob --yes --key cosign.key --output-signature fake.sig server-monitor-manager-manifest.json
|
||||||
|
# Verification must fail because ochenstarik-server-monitor-manager.sh enforces keyless OIDC identity!
|
||||||
|
if ./ochenstarik-server-monitor-manager.sh verify-manifest server-monitor-manager-manifest.json fake.sig >/dev/null 2>&1; then
|
||||||
|
echo "FAIL: Signature from wrong identity accepted!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: Wrong identity signature rejected."
|
||||||
|
rm cosign.key cosign.pub fake.sig
|
||||||
|
|
||||||
|
echo "All negative tests passed!"
|
||||||
89
tests/release-verification/run-positive-installation.sh
Normal file
89
tests/release-verification/run-positive-installation.sh
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAG="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$TAG" ]]; then
|
||||||
|
echo "Usage: $0 <tag>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Running positive installation test for $TAG..."
|
||||||
|
|
||||||
|
# Fetch smm-setup.sh
|
||||||
|
gh release download "$TAG" -p 'smm-setup.sh*'
|
||||||
|
|
||||||
|
# Verify checksum
|
||||||
|
sha256sum -c smm-setup.sh.sha256
|
||||||
|
|
||||||
|
# The archive is downloaded by verify-release or we must download it?
|
||||||
|
# In smm-setup.sh, the owner manually downloads the archive?
|
||||||
|
# Wait, let's look at docs: "загрузка bootstrap и архива из релиза, проверка контрольных сумм, проверка подписи manifest"
|
||||||
|
# Actually, the user does:
|
||||||
|
ARCHIVE="server-monitor-manager-linux-$(uname -m | sed -e 's/x86_64/x64/' -e 's/aarch64/arm64/').tar.gz"
|
||||||
|
gh release download "$TAG" -p "$ARCHIVE*"
|
||||||
|
gh release download "$TAG" -p "server-monitor-manager-manifest.*"
|
||||||
|
|
||||||
|
sha256sum -c "$ARCHIVE.sha256"
|
||||||
|
|
||||||
|
# Run setup steps through smm-setup.sh
|
||||||
|
# "preflight, verify-release, установка Control, mesh-init"
|
||||||
|
sudo bash smm-setup.sh preflight
|
||||||
|
sudo bash smm-setup.sh verify-manifest server-monitor-manager-manifest.json server-monitor-manager-manifest.sig
|
||||||
|
sudo bash smm-setup.sh verify-release "$ARCHIVE"
|
||||||
|
sudo bash smm-setup.sh install-control "$ARCHIVE" 127.0.0.1 17443
|
||||||
|
sudo bash smm-setup.sh mesh-init 127.0.0.1 51820
|
||||||
|
|
||||||
|
echo "Checking Control healthz..."
|
||||||
|
for _ in {1..30}; do
|
||||||
|
if sudo curl --fail --silent \
|
||||||
|
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
|
||||||
|
"https://127.0.0.1:17443/healthz" >/dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
sudo curl --fail --silent --show-error \
|
||||||
|
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
|
||||||
|
"https://127.0.0.1:17443/healthz"
|
||||||
|
|
||||||
|
echo "Extracting node code and installing agent..."
|
||||||
|
NODE_CODE=$(sudo bash smm-setup.sh node-code test-node)
|
||||||
|
export SMM_ENROLL_CODE="$NODE_CODE"
|
||||||
|
export SMM_ACCEPT_CA_FINGERPRINT=1
|
||||||
|
sudo --preserve-env=SMM_ENROLL_CODE,SMM_ACCEPT_CA_FINGERPRINT bash smm-setup.sh install-node "$ARCHIVE"
|
||||||
|
|
||||||
|
sudo systemctl is-active --quiet ochenstarik-smm-agent.service
|
||||||
|
sudo systemctl is-active --quiet ochenstarik-smm-control.service
|
||||||
|
|
||||||
|
# Verify install-monitor
|
||||||
|
echo "Installing monitor..."
|
||||||
|
# Generate a dummy SSH key for the test
|
||||||
|
ssh-keygen -t ed25519 -N "" -f /tmp/monitor_key
|
||||||
|
MONITOR_PUB=$(cat /tmp/monitor_key.pub)
|
||||||
|
sudo bash smm-setup.sh install-monitor "$MONITOR_PUB"
|
||||||
|
|
||||||
|
echo "Verifying monitor user and forced command..."
|
||||||
|
# Run SSH locally as the monitor user (assuming ssh is configured, but actually we can just su into the user or run the forced command directly)
|
||||||
|
# The forced command is likely defined in ~smm-monitor/.ssh/authorized_keys
|
||||||
|
MONITOR_CMD=$(sudo cat /var/lib/ochenstarik-server-monitor-manager/monitor/.ssh/authorized_keys | grep -o 'command="[^"]*"' | cut -d'"' -f2)
|
||||||
|
SNAPSHOT=$(sudo -u ochenstarik-smm-monitor $MONITOR_CMD)
|
||||||
|
|
||||||
|
# Simple validation of snapshot fields (since actual values vary, we just check keys)
|
||||||
|
EXPECTED_KEYS=$(cat tests/contracts/monitor-snapshot-v1.txt | cut -d'=' -f1 | sort)
|
||||||
|
ACTUAL_KEYS=$(echo "$SNAPSHOT" | cut -d'=' -f1 | sort)
|
||||||
|
|
||||||
|
if [[ "$EXPECTED_KEYS" == "$ACTUAL_KEYS" ]]; then
|
||||||
|
echo "Monitor snapshot keys match contract."
|
||||||
|
else
|
||||||
|
echo "Monitor snapshot keys mismatch!"
|
||||||
|
diff <(echo "$EXPECTED_KEYS") <(echo "$ACTUAL_KEYS") || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify uninstall
|
||||||
|
sudo bash smm-setup.sh uninstall-monitor
|
||||||
|
sudo bash smm-setup.sh uninstall-agent --purge
|
||||||
|
sudo bash smm-setup.sh uninstall-control --confirm-destroy-control
|
||||||
|
|
||||||
|
echo "Positive installation test passed!"
|
||||||
51
tests/release-verification/verify-assets.sh
Normal file
51
tests/release-verification/verify-assets.sh
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAG="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$TAG" ]]; then
|
||||||
|
echo "Usage: $0 <tag>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Verifying assets for release $TAG..."
|
||||||
|
|
||||||
|
# Fetch the list of assets from the release
|
||||||
|
ACTUAL_ASSETS=$(gh release view "$TAG" --json assets -q '.assets[].name' | sort)
|
||||||
|
|
||||||
|
EXPECTED_ASSETS=$(cat <<EOF | sort
|
||||||
|
ochenstarik-server-monitor-manager.sh
|
||||||
|
ochenstarik-server-monitor-manager.sh.sha256
|
||||||
|
server-monitor-manager-linux-x64.tar.gz
|
||||||
|
server-monitor-manager-linux-x64.tar.gz.sha256
|
||||||
|
server-monitor-manager-linux-arm64.tar.gz
|
||||||
|
server-monitor-manager-linux-arm64.tar.gz.sha256
|
||||||
|
server-monitor-manager-linux-x64-sbom.json
|
||||||
|
server-monitor-manager-linux-arm64-sbom.json
|
||||||
|
server-monitor-manager-win-x64-sbom.json
|
||||||
|
ServerMonitorManager-win-x64.msix
|
||||||
|
ServerMonitorManager-test-signing.cer
|
||||||
|
SHA256SUMS
|
||||||
|
smm-setup.sh
|
||||||
|
smm-setup.sh.sha256
|
||||||
|
server-monitor-manager-manifest.json
|
||||||
|
server-monitor-manager-manifest.sig
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$ACTUAL_ASSETS" == "$EXPECTED_ASSETS" ]]; then
|
||||||
|
echo "All expected assets are present."
|
||||||
|
else
|
||||||
|
echo "Asset mismatch!"
|
||||||
|
echo "Expected:"
|
||||||
|
echo "$EXPECTED_ASSETS"
|
||||||
|
echo "---"
|
||||||
|
echo "Actual:"
|
||||||
|
echo "$ACTUAL_ASSETS"
|
||||||
|
echo "---"
|
||||||
|
echo "Missing in Actual:"
|
||||||
|
comm -23 <(echo "$EXPECTED_ASSETS") <(echo "$ACTUAL_ASSETS")
|
||||||
|
echo "Unexpected in Actual:"
|
||||||
|
comm -13 <(echo "$EXPECTED_ASSETS") <(echo "$ACTUAL_ASSETS")
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Loading…
Reference in a new issue