diff --git a/.github/workflows/smoke-test-oci.yml b/.github/workflows/smoke-test-oci.yml new file mode 100644 index 0000000..5cf84c0 --- /dev/null +++ b/.github/workflows/smoke-test-oci.yml @@ -0,0 +1,122 @@ +name: Smoke-test veilor-os OCI + +# Pulls git.s8n.ru/veilor-org/veilor-os:43 and asserts that the image +# contains the veilor brand + the v0.5.x hardening overlay + the v0.7 +# CLI tools, and that cosign verifies it against bluebuild/cosign.pub. + +on: + workflow_run: + workflows: ["Build veilor-os OCI (BlueBuild)"] + types: [completed] + workflow_dispatch: + +permissions: + contents: read + +jobs: + smoke: + name: OCI smoke test + runs-on: nullstone + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_run' && + github.event.workflow_run.conclusion == 'success') + timeout-minutes: 20 + + env: + IMAGE: git.s8n.ru/veilor-org/veilor-os:43 + + steps: + - name: Checkout + uses: actions/checkout@v4.1.7 + + - name: Fix sudo perms + run: chown -R 0:0 /etc/sudo.conf /etc/sudoers /etc/sudoers.d 2>/dev/null || true + + - name: Install podman + cosign + run: | + set -euxo pipefail + command -v podman >/dev/null || dnf -y install --skip-unavailable podman + if ! command -v cosign >/dev/null 2>&1; then + curl -fsSL "https://github.com/sigstore/cosign/releases/download/v2.4.1/cosign-linux-amd64" \ + -o /usr/local/bin/cosign + chmod +x /usr/local/bin/cosign + fi + podman --version + cosign version + + - name: Login + pull OCI image + env: + FORGEJO_REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} + FORGEJO_REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} + run: | + set -euxo pipefail + if [ -n "${FORGEJO_REGISTRY_TOKEN:-}" ]; then + echo "$FORGEJO_REGISTRY_TOKEN" | podman login \ + --username "${FORGEJO_REGISTRY_USER:-veilor-org}" \ + --password-stdin git.s8n.ru + fi + podman pull "${IMAGE}" + + - name: Verify cosign signature + run: | + set -euo pipefail + [ -f bluebuild/cosign.pub ] || { echo "[ERR] bluebuild/cosign.pub missing"; exit 1; } + cosign verify --key bluebuild/cosign.pub "${IMAGE}" 2>&1 | tail -10 + + - name: Run OCI assertions + run: | + set -uo pipefail + PASS=0; FAIL=0; ERRORS="" + pass() { echo "[PASS] $1"; PASS=$((PASS+1)); } + fail() { echo "[FAIL] $1"; FAIL=$((FAIL+1)); ERRORS="${ERRORS} - $1\n"; } + img() { podman run --rm "${IMAGE}" /bin/bash -c "$1" 2>/dev/null; } + + OS=$(img 'cat /etc/os-release 2>/dev/null') + echo "$OS" | grep -q 'ID=veilor' && pass "ID=veilor" || fail "ID=veilor missing" + echo "$OS" | grep -q 'NAME="veilor-os"' && pass 'NAME="veilor-os"' || fail 'NAME="veilor-os" missing' + + img 'which sudo' >/dev/null && pass "sudo present" || fail "sudo missing" + img 'rpm -q mullvad-browser' >/dev/null && pass "mullvad-browser present" || fail "mullvad-browser missing" + img 'rpm -q tailscale' >/dev/null && pass "tailscale present" || fail "tailscale missing" + img 'rpm -q yggdrasil' >/dev/null && pass "yggdrasil present" || fail "yggdrasil missing" + + if img 'grep -qi "^SELINUX=enforcing" /etc/selinux/config'; then + pass "SELinux config = enforcing" + else + fail "SELinux not enforcing" + fi + + img 'test -e /etc/systemd/system/multi-user.target.wants/veilor-firstboot.service \ + -o -e /etc/systemd/system/graphical.target.wants/veilor-firstboot.service' >/dev/null \ + && pass "veilor-firstboot enabled" || fail "veilor-firstboot not enabled" + img 'test -e /etc/systemd/system/multi-user.target.wants/veilor-postinstall.service \ + -o -e /etc/systemd/system/graphical.target.wants/veilor-postinstall.service' >/dev/null \ + && pass "veilor-postinstall enabled" || fail "veilor-postinstall not enabled" + + for b in veilor-power veilor-update veilor-doctor veilor-postinstall; do + img "test -x /usr/local/bin/${b}" >/dev/null \ + && pass "${b} executable" || fail "${b} missing" + done + + if img 'ls /usr/share/veilor-os/scripts/ 2>/dev/null' | grep -qE '(10-harden|20-harden|30-apply)'; then + pass "/usr/share/veilor-os/scripts populated" + else + fail "/usr/share/veilor-os/scripts missing" + fi + + LEAKS=$(img "grep -rIni 'onyx\|192\.168\.0\.\|fedora\.local\|xynki\.dev' /etc/veilor* /usr/share/veilor-os 2>/dev/null") + if [ -z "$LEAKS" ]; then + pass "no brand leaks" + else + fail "brand leaks found" + echo "$LEAKS" + fi + + echo + echo "═══ ${PASS} passed, ${FAIL} failed ═══" + if [ "$FAIL" -gt 0 ]; then + printf "%b" "$ERRORS" + exit 1 + fi + echo "✓ veilor-os:43 smoke test passed"