diff --git a/.github/workflows/linux-control-agent.yml b/.github/workflows/linux-control-agent.yml index 709fa4b..425d55e 100644 --- a/.github/workflows/linux-control-agent.yml +++ b/.github/workflows/linux-control-agent.yml @@ -42,9 +42,13 @@ jobs: bash -n deploy/ochenstarik-server-monitor-manager.sh bash -n deploy/ochenstarik-smm-policy-apply bash -n deploy/ochenstarik-smm-emergency + bash -n tests/bootstrap/run-native-systemd-smoke.sh + bash -n tests/bootstrap/run-systemd-container-smoke.sh shellcheck --severity=error deploy/ochenstarik-server-monitor-manager.sh shellcheck --severity=error deploy/ochenstarik-smm-policy-apply shellcheck --severity=error deploy/ochenstarik-smm-emergency + shellcheck --severity=error tests/bootstrap/run-native-systemd-smoke.sh + shellcheck --severity=error tests/bootstrap/run-systemd-container-smoke.sh bash tests/bootstrap/test-bootstrap-contract.sh - name: Publish agent amd64 diff --git a/.github/workflows/linux-platform-matrix.yml b/.github/workflows/linux-platform-matrix.yml new file mode 100644 index 0000000..502224a --- /dev/null +++ b/.github/workflows/linux-platform-matrix.yml @@ -0,0 +1,149 @@ +name: Linux platform matrix + +on: + workflow_dispatch: + pull_request: + paths: + - '.github/workflows/linux-platform-matrix.yml' + - 'deploy/**' + - 'src/ServerMonitorManager.Agent/**' + - 'src/ServerMonitorManager.Control/**' + - 'src/ServerMonitorManager.Core/**' + - 'tests/bootstrap/**' + push: + branches: [main] + paths: + - '.github/workflows/linux-platform-matrix.yml' + - 'deploy/**' + - 'src/ServerMonitorManager.Agent/**' + - 'src/ServerMonitorManager.Control/**' + - 'src/ServerMonitorManager.Core/**' + - 'tests/bootstrap/**' + +permissions: + contents: read + +concurrency: + group: linux-platform-matrix-${{ github.ref }} + cancel-in-progress: true + +jobs: + release-archive: + name: Release archive (${{ matrix.runtime }}) + runs-on: ubuntu-24.04 + strategy: + matrix: + runtime: [linux-x64, linux-arm64] + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up .NET 10 + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.0.x + + - name: Publish compatible release + run: | + set -Eeuo pipefail + dotnet publish src/ServerMonitorManager.Agent/ServerMonitorManager.Agent.csproj \ + --configuration Release --runtime '${{ matrix.runtime }}' --self-contained true \ + -p:PublishSingleFile=true -p:PublishTrimmed=true -o out/agent + dotnet publish src/ServerMonitorManager.Control/ServerMonitorManager.Control.csproj \ + --configuration Release --runtime '${{ matrix.runtime }}' --self-contained true \ + -p:PublishSingleFile=true -p:PublishTrimmed=true -o out/control + install -d out/deploy out/bootstrap + install -m 0644 deploy/ochenstarik-smm-control.service out/deploy/ + install -m 0644 deploy/ochenstarik-smm-agent.service out/deploy/ + install -m 0644 deploy/ochenstarik-smm-firewall.service out/deploy/ + install -m 0755 deploy/ochenstarik-smm-policy-apply out/deploy/ + install -m 0755 deploy/ochenstarik-smm-emergency out/deploy/ + install -m 0755 deploy/ochenstarik-server-monitor-manager.sh out/bootstrap/ + archive='server-monitor-manager-${{ matrix.runtime }}.tar.gz' + tar -C out -czf "$archive" agent control deploy bootstrap + sha256sum "$archive" >"$archive.sha256" + + - name: Upload release archive + uses: actions/upload-artifact@v6 + with: + name: server-monitor-manager-${{ matrix.runtime }}-matrix + path: | + server-monitor-manager-${{ matrix.runtime }}.tar.gz + server-monitor-manager-${{ matrix.runtime }}.tar.gz.sha256 + + ubuntu-vm: + name: ${{ matrix.name }} native VM + needs: release-archive + strategy: + fail-fast: false + matrix: + include: + - name: Ubuntu 22.04 x64 + runner: ubuntu-22.04 + runtime: linux-x64 + - name: Ubuntu 24.04 x64 + runner: ubuntu-24.04 + runtime: linux-x64 + - name: Ubuntu 22.04 arm64 + runner: ubuntu-22.04-arm + runtime: linux-arm64 + - name: Ubuntu 24.04 arm64 + runner: ubuntu-24.04-arm + runtime: linux-arm64 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Download release archive + uses: actions/download-artifact@v8 + with: + name: server-monitor-manager-${{ matrix.runtime }}-matrix + path: artifacts + + - name: Install twice and restart services + run: | + bash tests/bootstrap/run-native-systemd-smoke.sh \ + 'artifacts/server-monitor-manager-${{ matrix.runtime }}.tar.gz' \ + deploy/ochenstarik-server-monitor-manager.sh + + debian-systemd: + name: ${{ matrix.name }} systemd restart + needs: release-archive + strategy: + fail-fast: false + matrix: + include: + - name: Debian 12 x64 + runner: ubuntu-24.04 + runtime: linux-x64 + image: debian:12 + - name: Debian 13 x64 + runner: ubuntu-24.04 + runtime: linux-x64 + image: debian:13 + - name: Debian 12 arm64 + runner: ubuntu-24.04-arm + runtime: linux-arm64 + image: debian:12 + - name: Debian 13 arm64 + runner: ubuntu-24.04-arm + runtime: linux-arm64 + image: debian:13 + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Download release archive + uses: actions/download-artifact@v8 + with: + name: server-monitor-manager-${{ matrix.runtime }}-matrix + path: artifacts + + - name: Install twice and restart systemd container + run: | + bash tests/bootstrap/run-systemd-container-smoke.sh \ + '${{ matrix.image }}' \ + 'artifacts/server-monitor-manager-${{ matrix.runtime }}.tar.gz' \ + deploy/ochenstarik-server-monitor-manager.sh diff --git a/.github/workflows/linux-release.yml b/.github/workflows/linux-release.yml index c2f3e87..8df7022 100644 --- a/.github/workflows/linux-release.yml +++ b/.github/workflows/linux-release.yml @@ -21,9 +21,13 @@ jobs: bash -n deploy/ochenstarik-server-monitor-manager.sh bash -n deploy/ochenstarik-smm-policy-apply bash -n deploy/ochenstarik-smm-emergency + bash -n tests/bootstrap/run-native-systemd-smoke.sh + bash -n tests/bootstrap/run-systemd-container-smoke.sh shellcheck --severity=error deploy/ochenstarik-server-monitor-manager.sh shellcheck --severity=error deploy/ochenstarik-smm-policy-apply shellcheck --severity=error deploy/ochenstarik-smm-emergency + shellcheck --severity=error tests/bootstrap/run-native-systemd-smoke.sh + shellcheck --severity=error tests/bootstrap/run-systemd-container-smoke.sh bash tests/bootstrap/test-bootstrap-contract.sh - name: Package bootstrap diff --git a/docs/linux-platform-matrix.md b/docs/linux-platform-matrix.md new file mode 100644 index 0000000..9f1db6d --- /dev/null +++ b/docs/linux-platform-matrix.md @@ -0,0 +1,20 @@ +# Linux platform matrix + +Workflow `linux-platform-matrix.yml` собирает те же self-contained release-архивы, которые устанавливает production bootstrap, отдельно для `linux-x64` и `linux-arm64`. + +## Реальные Ubuntu VM + +На GitHub-hosted VM проверяются: + +- Ubuntu 22.04 x64; +- Ubuntu 24.04 x64; +- Ubuntu 22.04 arm64; +- Ubuntu 24.04 arm64. + +Каждая VM выполняет `preflight`, проверяет checksum и содержимое архива, дважды устанавливает Control, проверяет emergency-команду, перезапускает systemd service и обращается к HTTPS `/healthz` через созданный CA. + +## Debian systemd containers + +Debian 12/13 проверяются для x64 и arm64 в privileged systemd-контейнерах на соответствующей архитектуре runner. Тест выполняет чистую и повторную установку, затем полностью перезапускает контейнер и проверяет автоматический запуск Control и HTTPS healthcheck после нового systemd boot. + +Systemd-контейнер проверяет дистрибутив, users, permissions, units, certificates, SQLite state и service lifecycle, но не считается полноценной Debian VM. Отдельным незакрытым критерием остаётся reboot настоящих Debian VM с собственным kernel. Физическая проверка WireGuard/nftables также выполняется по `three-server-acceptance.md`, поскольку GitHub runners не моделируют внешний NAT и cloud firewall. diff --git a/docs/roadmap.md b/docs/roadmap.md index d661f84..b918e17 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -2,6 +2,8 @@ Подробные требования к собственному bootstrap, управляемой настройке Linux и Xray находятся в [ТЗ Provisioning и Xray VPN](provisioning-vpn-requirements.md). Все компоненты Server Monitor Manager разрабатываются, версионируются и выпускаются только в этом репозитории. +Текущее автоматическое покрытие поддерживаемых Linux-платформ описано в [Linux platform matrix](linux-platform-matrix.md). + ## Этап 0 — граница и базовая архитектура - [x] переименовать проект в Server Monitor Manager; @@ -99,7 +101,9 @@ - [x] локально создавать Agent key/CSR, выполнять mTLS enrollment и не сохранять token; - [x] добавить собственную установку WireGuard Hub/Node и выдачу внутренних адресов; - [x] реализовать nftables policy helper вместо временного deny-by-default helper; -- [ ] добавить VM CI matrix, повторную установку и reboot; +- [x] добавить native Ubuntu 22.04/24.04 x64/arm64 VM CI и повторную установку; +- [x] добавить Debian 12/13 x64/arm64 systemd-container restart matrix; +- [ ] добавить полный reboot настоящих Debian VM; - [x] добавить локальную emergency recovery command для текущих Mesh/firewall-компонентов. ## Этап 8 — Provisioning control plane diff --git a/tests/bootstrap/run-native-systemd-smoke.sh b/tests/bootstrap/run-native-systemd-smoke.sh new file mode 100755 index 0000000..8789577 --- /dev/null +++ b/tests/bootstrap/run-native-systemd-smoke.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +IFS=$'\n\t' + +archive="${1:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}" +bootstrap="${2:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}" +port="${SMM_SMOKE_PORT:-17443}" + +cleanup() { + sudo "$bootstrap" uninstall-control --confirm-destroy-control >/dev/null 2>&1 || true +} +trap cleanup EXIT + +sudo "$bootstrap" preflight +sudo "$bootstrap" verify-release "$archive" +sudo "$bootstrap" install-control "$archive" 127.0.0.1 "$port" +sudo test -x /usr/local/sbin/ochenstarik-smm-emergency +sudo /usr/local/sbin/ochenstarik-smm-emergency status + +for _ in {1..30}; do + if sudo curl --fail --silent \ + --cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \ + "https://127.0.0.1:$port/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:$port/healthz" + +sudo "$bootstrap" install-control "$archive" 127.0.0.1 "$port" +sudo systemctl restart ochenstarik-smm-control.service +sudo systemctl is-active --quiet ochenstarik-smm-control.service +sudo curl --fail --silent --show-error --retry 15 --retry-all-errors --retry-delay 1 \ + --cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \ + "https://127.0.0.1:$port/healthz" + +printf '%s\n' "NATIVE_SYSTEMD_SMOKE=PASS" diff --git a/tests/bootstrap/run-systemd-container-smoke.sh b/tests/bootstrap/run-systemd-container-smoke.sh new file mode 100755 index 0000000..89eee9c --- /dev/null +++ b/tests/bootstrap/run-systemd-container-smoke.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +IFS=$'\n\t' + +root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +base_image="${1:?usage: run-systemd-container-smoke.sh BASE_IMAGE ARCHIVE BOOTSTRAP}" +archive="$(realpath "${2:?archive is required}")" +bootstrap="$(realpath "${3:?bootstrap is required}")" +name="smm-systemd-${RANDOM}-${RANDOM}" +image="smm-systemd-smoke:${base_image//[:\/]/-}" +port="17443" + +cleanup() { + docker rm -f "$name" >/dev/null 2>&1 || true +} +trap cleanup EXIT + +docker build \ + --build-arg "BASE_IMAGE=$base_image" \ + -f "$root/tests/bootstrap/systemd-container.Dockerfile" \ + -t "$image" \ + "$root" +docker run --detach --privileged --cgroupns=private \ + --tmpfs /run --tmpfs /run/lock --name "$name" "$image" >/dev/null + +for _ in {1..30}; do + if docker exec "$name" systemctl is-system-running >/dev/null 2>&1; then + break + fi + state="$(docker exec "$name" systemctl is-system-running 2>/dev/null || true)" + [[ "$state" == "degraded" ]] && break + sleep 1 +done +state="$(docker exec "$name" systemctl is-system-running 2>/dev/null || true)" +[[ "$state" == "running" || "$state" == "degraded" ]] || { + docker exec "$name" systemctl --failed --no-pager || true + printf '%s\n' "container systemd did not finish booting: $state" >&2 + exit 1 +} + +docker cp "$archive" "$name:/tmp/release.tar.gz" +docker cp "${archive}.sha256" "$name:/tmp/release.tar.gz.sha256" +docker cp "$bootstrap" "$name:/tmp/ochenstarik-server-monitor-manager.sh" +docker exec "$name" chmod 0700 /tmp/ochenstarik-server-monitor-manager.sh +docker exec "$name" /tmp/ochenstarik-server-monitor-manager.sh preflight +docker exec "$name" /tmp/ochenstarik-server-monitor-manager.sh install-control \ + /tmp/release.tar.gz 127.0.0.1 "$port" +docker exec "$name" curl --fail --silent --show-error --retry 15 --retry-all-errors --retry-delay 1 \ + --cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \ + "https://127.0.0.1:$port/healthz" +docker exec "$name" /tmp/ochenstarik-server-monitor-manager.sh install-control \ + /tmp/release.tar.gz 127.0.0.1 "$port" + +docker restart "$name" >/dev/null +for _ in {1..60}; do + if docker exec "$name" systemctl is-active --quiet ochenstarik-smm-control.service; then + break + fi + sleep 1 +done +docker exec "$name" systemctl is-active --quiet ochenstarik-smm-control.service +docker exec "$name" curl --fail --silent --show-error --retry 15 --retry-all-errors --retry-delay 1 \ + --cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \ + "https://127.0.0.1:$port/healthz" +docker exec "$name" /usr/local/sbin/ochenstarik-smm-emergency status + +printf '%s\n' "SYSTEMD_CONTAINER_SMOKE=PASS image=$base_image" diff --git a/tests/bootstrap/systemd-container.Dockerfile b/tests/bootstrap/systemd-container.Dockerfile new file mode 100644 index 0000000..3790429 --- /dev/null +++ b/tests/bootstrap/systemd-container.Dockerfile @@ -0,0 +1,14 @@ +ARG BASE_IMAGE +FROM ${BASE_IMAGE} + +ENV container=docker +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates curl iproute2 openssl procps sudo systemd systemd-sysv \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +STOPSIGNAL SIGRTMIN+3 +CMD ["/sbin/init"]