From d206c2219e6fdc76936c7f043dffec3f1bf1cf4e Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Sat, 15 Aug 2026 22:04:25 +0700 Subject: [PATCH 1/2] feat: add one-command alpha14 server installation --- deploy/smm-setup.sh | 95 ++++++++++++++++++++++- docs/linux-bootstrap.md | 29 ++++++- docs/release-policy.md | 5 +- tests/bootstrap/test-release-contract.sh | 98 +++++++++++++++++++++++- 4 files changed, 218 insertions(+), 9 deletions(-) diff --git a/deploy/smm-setup.sh b/deploy/smm-setup.sh index 8e95c01..2abad49 100644 --- a/deploy/smm-setup.sh +++ b/deploy/smm-setup.sh @@ -16,8 +16,13 @@ usage() { Usage: smm-setup.sh [--tag TAG] [--repository OWNER/REPO] COMMAND [ARG...] -Commands are passed to the verified ochenstarik-server-monitor-manager.sh -asset from the selected immutable GitHub release. Common commands: +Convenience installation commands: + install-hub PUBLIC_HOST [HTTPS_PORT] [WG_PORT] + install-node + +Other commands are passed to the verified ochenstarik-server-monitor-manager.sh +asset from the selected immutable GitHub release. Use -- before a command to +force pass-through. Common bootstrap commands: install-agent | install-control | uninstall-agent | uninstall-control backup-create | backup-restore | version @@ -37,6 +42,7 @@ require_command() { command -v "$1" >/dev/null 2>&1 || die "required command is unavailable: $1" } +pass_through=0 while [[ $# -gt 0 ]]; do case "$1" in --tag) @@ -54,6 +60,7 @@ while [[ $# -gt 0 ]]; do exit 0 ;; --) + pass_through=1 shift break ;; @@ -75,6 +82,20 @@ done [[ "$REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] \ || die "invalid repository: $REPOSITORY" +action="$1" +shift +if (( pass_through == 0 )); then + case "$action" in + install-hub) + [[ $# -ge 1 && $# -le 3 ]] \ + || die "install-hub requires PUBLIC_HOST [HTTPS_PORT] [WG_PORT]" + ;; + install-node) + [[ $# -eq 0 ]] || die "install-node takes no arguments" + ;; + esac +fi + require_command curl require_command sha256sum require_command mktemp @@ -99,4 +120,72 @@ curl -fsSL "$release_base/$INNER_ASSET.sha256" -o "$temporary_directory/$INNER_A install -m 0755 "$temporary_directory/$INNER_ASSET" "$cached_script" install -m 0644 "$temporary_directory/$INNER_ASSET.sha256" "$cached_checksum" -exec "$cached_script" "$@" +download_required_asset() { + local asset="$1" + if ! curl -fsSL "$release_base/$asset" -o "$temporary_directory/$asset"; then + case "$asset" in + server-monitor-manager-manifest.json|server-monitor-manager-manifest.sig|server-monitor-manager-manifest.pem) + die "required signed-release asset is unavailable: $asset" + ;; + *) + die "required release asset is unavailable: $asset" + ;; + esac + fi +} + +download_platform_release() { + local platform archive_asset asset + case "$(uname -m)" in + x86_64) platform="linux-x64" ;; + aarch64|arm64) platform="linux-arm64" ;; + *) die "unsupported architecture: $(uname -m)" ;; + esac + + archive_asset="server-monitor-manager-$platform.tar.gz" + for asset in \ + "$archive_asset" \ + "$archive_asset.sha256" \ + server-monitor-manager-manifest.json \ + server-monitor-manager-manifest.sig \ + server-monitor-manager-manifest.pem; do + download_required_asset "$asset" + done + + ( + cd "$temporary_directory" + sha256sum -c "$archive_asset.sha256" >/dev/null + ) || die "checksum verification failed for $RELEASE_TAG/$archive_asset" + downloaded_archive="$temporary_directory/$archive_asset" +} + +if (( pass_through == 1 )); then + exec "$cached_script" "$action" "$@" +fi + +case "$action" in + install-hub) + download_platform_release + archive="$downloaded_archive" + public_host="$1" + https_port="${2:-}" + wg_port="${3:-}" + if [[ -n "$https_port" ]]; then + "$cached_script" install-control "$archive" "$public_host" "$https_port" + else + "$cached_script" install-control "$archive" "$public_host" + fi + if [[ -n "$wg_port" ]]; then + exec "$cached_script" mesh-init "$public_host" "$wg_port" + fi + exec "$cached_script" mesh-init "$public_host" + ;; + install-node) + download_platform_release + archive="$downloaded_archive" + exec "$cached_script" install-node "$archive" + ;; + *) + exec "$cached_script" "$action" "$@" + ;; +esac diff --git a/docs/linux-bootstrap.md b/docs/linux-bootstrap.md index 9403745..a68b711 100644 --- a/docs/linux-bootstrap.md +++ b/docs/linux-bootstrap.md @@ -4,7 +4,32 @@ Server Monitor Manager устанавливает Control (Hub) и Agent (Node) Текущая версия предназначена для alpha-тестирования на Ubuntu Server 22.04/24.04 и Debian 12/13 (`amd64`, `arm64`, systemd). Hub должен иметь публичный IPv4-адрес или DNS-имя и доступный UDP-порт. Node может находиться за NAT без белого IP. -## Файлы релиза +## Быстрая установка + +Скачайте и проверьте convenience installer из `v0.1.0-alpha.14`: + +```bash +curl -fsSLO https://github.com/ochenstarik-ui/server-monitor-manager/releases/download/v0.1.0-alpha.14/smm-setup.sh +curl -fsSLO https://github.com/ochenstarik-ui/server-monitor-manager/releases/download/v0.1.0-alpha.14/smm-setup.sh.sha256 +sha256sum -c smm-setup.sh.sha256 +chmod 700 smm-setup.sh +``` + +На Hub установка Control и инициализация Mesh выполняются одной командой. Порты можно не указывать: по умолчанию используются HTTPS `7443` и WireGuard `51820`. + +```bash +sudo ./smm-setup.sh install-hub hub.example.com 7443 51820 +``` + +На Node команда сама выбирает `linux-x64` или `linux-arm64`, скачивает и проверяет релиз, затем запрашивает код `SMMNODE2`: + +```bash +sudo ./smm-setup.sh install-node +``` + +Обе команды публично скачивают архив, его checksum, manifest, подпись и Fulcio-сертификат из одного неизменяемого тега. Отсутствие любого файла подписи прерывает установку; автоматического перехода на unsigned-режим нет. Опции `--tag` и `--repository` предназначены для явного выбора другого источника. Остальные команды bootstrap по-прежнему можно передавать напрямую; `-- COMMAND` принудительно включает сквозной режим. + +## Ручная установка и файлы релиза Скачайте из одного GitHub Release: @@ -19,7 +44,7 @@ Server Monitor Manager устанавливает Control (Hub) и Agent (Node) Bootstrap проверяет подпись manifest, затем SHA-256 архива по manifest, и принимает в архиве только каталоги `agent`, `control`, `deploy` и `bootstrap`. -## 1. Установка главного сервера (Hub) +## 1. Ручная установка главного сервера (Hub) ```bash sha256sum -c ochenstarik-server-monitor-manager.sh.sha256 diff --git a/docs/release-policy.md b/docs/release-policy.md index fd71245..ab6f487 100644 --- a/docs/release-policy.md +++ b/docs/release-policy.md @@ -12,7 +12,8 @@ Known release history: - `v0.1.0-alpha.8` contains the v1 `server-monitor-manager-bootstrap-manifest.json` layout and an orphaned `server-monitor-manager-manifest.sig` without the corresponding manifest v2. Published assets remain immutable; the anomaly is documented rather than repaired in place. - Tags `v0.1.0-alpha.10` and `v0.1.0-alpha.11` exist, but their Release pipelines failed before a GitHub Release was published. Those version numbers are burned and must not be moved, deleted, recreated, or reused. -- `v0.1.0-alpha.12` was published, but its Windows `SHA256SUMS` asset used CRLF and was not consumable by GNU `sha256sum -c`. The immutable release remains published as historical evidence; the correction is released under a higher version. -- `v0.1.0-alpha.13` corrected the checksum portability defect, but its keyless manifest signature lacked the published signing certificate required by production consumers. The immutable release remains published as historical evidence; the producer/consumer certificate contract is corrected under a higher version. +- `v0.1.0-alpha.12` was published with a keyless manifest signature but without the Fulcio signing certificate, so consumers cannot verify that signature. Its Windows `SHA256SUMS` asset also used CRLF and was not consumable by GNU `sha256sum -c`. The immutable release remains published as historical evidence; neither defect is repaired in place. +- `v0.1.0-alpha.13` corrected the checksum portability defect, but it was also published with a keyless manifest signature and without the Fulcio signing certificate required by production consumers. The immutable release remains published as historical evidence; the producer/consumer certificate contract is corrected under a higher version. +- `v0.1.0-alpha.14` is the first release whose contract requires the manifest, keyless signature, and Fulcio certificate to be published and verified together. Its tag may be created only from `main` after the release pull request, required CI, and a branch `workflow_dispatch` dry run of the Release pipeline are green. Every release candidate must pass a branch `workflow_dispatch` run of the Release pipeline before its immutable version tag is created. The release owner has sole write ownership of version sources, `deploy/**`, `tests/bootstrap/**`, release workflows, the root README release status, and translated README release statuses. Other contributors request changes to those paths in their report; they do not edit or bump them directly. One pull request covers one release topic and may merge only after required CI is green. diff --git a/tests/bootstrap/test-release-contract.sh b/tests/bootstrap/test-release-contract.sh index 98c326c..3912b57 100644 --- a/tests/bootstrap/test-release-contract.sh +++ b/tests/bootstrap/test-release-contract.sh @@ -17,6 +17,8 @@ v1_fixture="$root/tests/fixtures/alpha8-v1-release" } bash -n "$setup" grep -Fq 'readonly DEFAULT_RELEASE_TAG="v0.1.0-alpha.14"' "$setup" +grep -Fq 'install-hub PUBLIC_HOST [HTTPS_PORT] [WG_PORT]' "$setup" +grep -Fxq ' install-node' "$setup" if grep -Fq 'validate_control_url' "$setup" || grep -Fq '${CONTROL_URL%/}/control' "$setup"; then printf '%s\n' 'temporary control URL workaround must not be present in smm-setup.sh' >&2 exit 1 @@ -54,6 +56,8 @@ grep -Fq 'sha256sum -c SHA256SUMS' "$workflow" grep -Fq -- '--output-certificate server-monitor-manager-manifest.pem' "$workflow" grep -Fq 'server-monitor-manager-manifest.pem' "$workflow" grep -Fq 'v0.1.0-alpha.13' "$policy" +grep -Fq 'v0.1.0-alpha.14' "$policy" +grep -Fq 'first release whose contract requires the manifest, keyless signature, and Fulcio certificate' "$policy" grep -Fq 'Published release tags and their assets are immutable.' "$installer_contract" grep -Fq 'publish a new, higher version tag' "$installer_contract" @@ -86,10 +90,34 @@ trap 'rm -rf -- "$work"' EXIT mkdir -p "$work/bin" "$work/home" cat >"$work/inner.sh" <<'INNER' #!/usr/bin/env bash -printf 'INNER_COMMAND=%s\n' "$1" +set -Eeuo pipefail +printf 'INNER_ARGS=' +printf '%s ' "$@" +printf '\n' +case "${1:-}" in + install-control|install-node) + archive="$2" + [[ "$archive" == */server-monitor-manager-linux-*.tar.gz ]] || exit 0 + directory="$(dirname "$archive")" + for required in \ + server-monitor-manager-manifest.json \ + server-monitor-manager-manifest.sig \ + server-monitor-manager-manifest.pem; do + [[ -s "$directory/$required" ]] || { + printf 'missing signed-release asset: %s\n' "$required" >&2 + exit 1 + } + done + ;; +esac INNER chmod +x "$work/inner.sh" inner_hash="$(sha256sum "$work/inner.sh" | cut -d' ' -f1)" +printf '%s\n' 'release archive fixture' >"$work/archive.tar.gz" +archive_hash="$(sha256sum "$work/archive.tar.gz" | cut -d' ' -f1)" +printf '%s\n' '{"schema":"smm-manifest/v2"}' >"$work/server-monitor-manager-manifest.json" +printf '%s\n' 'fixture-signature' >"$work/server-monitor-manager-manifest.sig" +printf '%s\n' 'fixture-certificate' >"$work/server-monitor-manager-manifest.pem" cat >"$work/bin/curl" <>'$work/urls' +asset="\${url##*/}" +if [[ "\${SMM_TEST_MISSING_ASSET:-}" == "\$asset" ]]; then + printf 'fixture asset unavailable: %s\n' "\$asset" >&2 + exit 22 +fi case "\$url" in */ochenstarik-server-monitor-manager.sh) cp '$work/inner.sh' "\$out" @@ -110,6 +143,15 @@ case "\$url" in */ochenstarik-server-monitor-manager.sh.sha256) printf '%s %s\n' '$inner_hash' 'ochenstarik-server-monitor-manager.sh' >"\$out" ;; + */server-monitor-manager-linux-x64.tar.gz|*/server-monitor-manager-linux-arm64.tar.gz) + cp '$work/archive.tar.gz' "\$out" + ;; + */server-monitor-manager-linux-x64.tar.gz.sha256|*/server-monitor-manager-linux-arm64.tar.gz.sha256) + printf '%s %s\n' '$archive_hash' "\${asset%.sha256}" >"\$out" + ;; + */server-monitor-manager-manifest.json|*/server-monitor-manager-manifest.sig|*/server-monitor-manager-manifest.pem) + cp '$work/'"\$asset" "\$out" + ;; *) printf 'unexpected URL: %s\n' "\$url" >&2 exit 1 @@ -117,10 +159,62 @@ case "\$url" in esac EOF_CURL chmod +x "$work/bin/curl" +cat >"$work/bin/uname" <<'EOF_UNAME' +#!/usr/bin/env bash +printf '%s\n' "${SMM_TEST_ARCH:-x86_64}" +EOF_UNAME +chmod +x "$work/bin/uname" HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" version >"$work/output" -grep -Fq 'INNER_COMMAND=version' "$work/output" +grep -Fq 'INNER_ARGS=version ' "$work/output" grep -Fq '/releases/download/v0.1.0-alpha.14/ochenstarik-server-monitor-manager.sh' "$work/urls" grep -Fq '/releases/download/v0.1.0-alpha.14/ochenstarik-server-monitor-manager.sh.sha256' "$work/urls" +if HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" install-hub >"$work/invalid.out" 2>&1; then + printf '%s\n' 'install-hub accepted a missing PUBLIC_HOST' >&2 + exit 1 +fi +grep -Fq 'install-hub requires PUBLIC_HOST [HTTPS_PORT] [WG_PORT]' "$work/invalid.out" +if HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" install-hub host 7443 51820 extra >"$work/invalid.out" 2>&1; then + printf '%s\n' 'install-hub accepted an extra argument' >&2 + exit 1 +fi +if HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" install-node extra >"$work/invalid.out" 2>&1; then + printf '%s\n' 'install-node accepted an argument' >&2 + exit 1 +fi +grep -Fq 'install-node takes no arguments' "$work/invalid.out" + +HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" install-hub hub.example.com 7443 51820 >"$work/hub.out" +grep -Eq 'INNER_ARGS=install-control .*/server-monitor-manager-linux-x64.tar.gz hub.example.com 7443 ' "$work/hub.out" +grep -Fq 'INNER_ARGS=mesh-init hub.example.com 51820 ' "$work/hub.out" + +HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" install-node >"$work/node.out" +grep -Eq 'INNER_ARGS=install-node .*/server-monitor-manager-linux-x64.tar.gz ' "$work/node.out" + +SMM_TEST_ARCH=aarch64 HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" install-node >"$work/arm-node.out" +grep -Fq '/server-monitor-manager-linux-arm64.tar.gz' "$work/urls" + +HOME="$work/home" PATH="$work/bin:$PATH" bash "$setup" -- install-node legacy.tar.gz >"$work/pass-through.out" +grep -Fq 'INNER_ARGS=install-node legacy.tar.gz ' "$work/pass-through.out" + +for required in \ + server-monitor-manager-manifest.json \ + server-monitor-manager-manifest.sig \ + server-monitor-manager-manifest.pem; do + for mode in node hub; do + if [[ "$mode" == node ]]; then + install_arguments=(install-node) + else + install_arguments=(install-hub hub.example.com) + fi + if SMM_TEST_MISSING_ASSET="$required" HOME="$work/home" PATH="$work/bin:$PATH" \ + bash "$setup" "${install_arguments[@]}" >"$work/missing.out" 2>&1; then + printf '%s continued without %s\n' "$mode" "$required" >&2 + exit 1 + fi + grep -Fq "required signed-release asset is unavailable: $required" "$work/missing.out" + done +done + printf '%s\n' 'RELEASE_CONTRACT=PASS' -- 2.45.2 From c83fd1ea92692458af2917f783ab441e5a49762b Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Sat, 15 Aug 2026 22:30:12 +0700 Subject: [PATCH 2/2] docs: record unpublished alpha10 and alpha11 releases --- docs/release-policy.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/release-policy.md b/docs/release-policy.md index ab6f487..fdf9c0d 100644 --- a/docs/release-policy.md +++ b/docs/release-policy.md @@ -11,7 +11,8 @@ For `v0.1.0-alpha.9`, this makes `smm-setup.sh`, `smm-setup.sh.sha256`, the boot Known release history: - `v0.1.0-alpha.8` contains the v1 `server-monitor-manager-bootstrap-manifest.json` layout and an orphaned `server-monitor-manager-manifest.sig` without the corresponding manifest v2. Published assets remain immutable; the anomaly is documented rather than repaired in place. -- Tags `v0.1.0-alpha.10` and `v0.1.0-alpha.11` exist, but their Release pipelines failed before a GitHub Release was published. Those version numbers are burned and must not be moved, deleted, recreated, or reused. +- `v0.1.0-alpha.10` exists as a tag, but its Release pipeline failed before a GitHub Release was published. The version number is burned and must not be moved, deleted, recreated, or reused. +- `v0.1.0-alpha.11` exists as a tag, but its Release pipeline failed before a GitHub Release was published. The version number is burned and must not be moved, deleted, recreated, or reused. - `v0.1.0-alpha.12` was published with a keyless manifest signature but without the Fulcio signing certificate, so consumers cannot verify that signature. Its Windows `SHA256SUMS` asset also used CRLF and was not consumable by GNU `sha256sum -c`. The immutable release remains published as historical evidence; neither defect is repaired in place. - `v0.1.0-alpha.13` corrected the checksum portability defect, but it was also published with a keyless manifest signature and without the Fulcio signing certificate required by production consumers. The immutable release remains published as historical evidence; the producer/consumer certificate contract is corrected under a higher version. - `v0.1.0-alpha.14` is the first release whose contract requires the manifest, keyless signature, and Fulcio certificate to be published and verified together. Its tag may be created only from `main` after the release pull request, required CI, and a branch `workflow_dispatch` dry run of the Release pipeline are green. -- 2.45.2