93 Commits
Author SHA1 Message Date
justme 675a2bc5b4 Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 16:26:33 +08:00
justme a4f3d1892a ci: all-platform node binaries in the signed release manifest; signed SHA256SUMS for the archives; web build once before the parallel backend builds; release 0.3.3 [CI SKIP]
ci/woodpecker/tag/windows-release Pipeline was successful
ci/woodpecker/tag/linux-release Pipeline was successful
ci/woodpecker/tag/gitea-release Pipeline was successful
2026-10-08 16:26:29 +08:00
justme 76efc15e9a Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline failed
ci/woodpecker/push/web-build unknown status
2026-10-08 16:13:02 +08:00
justme 3ee8b6bb3d Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 16:05:42 +08:00
justme 0fa2f2a26a Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 15:59:32 +08:00
justme 0e7a8c14fb Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 15:30:16 +08:00
justme 4a15dba8de Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline failed
ci/woodpecker/push/web-build unknown status
2026-10-08 06:47:15 +08:00
justme 8410735377 Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline failed
ci/woodpecker/push/web-build unknown status
2026-10-08 05:44:20 +08:00
justme 6310358490 Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 05:41:02 +08:00
justme 0dc8c6fe34 Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 04:23:16 +08:00
justme 1f70c8d31e Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-08 04:10:29 +08:00
justme 0e0b925c8b Merge branch 'main' into dev
ci/woodpecker/push/verify Pipeline failed
ci/woodpecker/push/web-build unknown status
2026-10-08 03:22:20 +08:00
justme f01bb8fd63 ci: sync main into dev [CI SKIP] 2026-10-07 20:03:38 +08:00
justme 96990c6f4e ci: sync main into dev [CI SKIP] 2026-10-07 19:14:58 +08:00
justme bd35a1a966 ci: sync main into dev [CI SKIP] 2026-10-07 18:13:20 +08:00
justme b47e01d944 ci: sync main into dev [CI SKIP] 2026-10-07 17:21:25 +08:00
justme 7ee860cb27 ci: sync main into dev [CI SKIP] 2026-10-07 16:58:59 +08:00
justme 699ed8c411 ci: sync main into dev [CI SKIP] 2026-10-07 16:20:25 +08:00
justme 0012df7b91 ci: sync main into dev [CI SKIP] 2026-10-07 15:08:26 +08:00
justme d5967b6110 ci: sync main into dev [CI SKIP] 2026-10-07 14:57:08 +08:00
justme 450748f3bc ci: sync main into dev [CI SKIP] 2026-10-07 14:18:03 +08:00
justme 5bd53f8880 ci: sync main into dev [CI SKIP] 2026-10-07 03:36:55 +08:00
justme 643f9e1ea3 ci: sync main into dev [CI SKIP] 2026-10-07 03:30:19 +08:00
justme ac0bcd8e1f docs: merge the Fleet docs into backend/fleet/README.md, add TODO.md, fix stale references [CI SKIP] 2026-10-07 03:30:03 +08:00
justme f4aa3f2482 docs: audit the docs, merge the Fleet docs into backend/fleet/README.md and add TODO.md [CI SKIP]
Drop the unimplemented fleet updater design, the stale project-layout sections and broken references, fix the API table, and rename the four Monitor*.cs files left over from the rename.
2026-10-07 03:29:05 +08:00
justme 180e54cb3c ci: sync main into dev [CI SKIP] 2026-10-07 03:12:48 +08:00
justme a19517a57d docs: rewrite the README: scope, trust model, architecture, protocol and build [SKIP TESTS]
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-07 01:51:54 +08:00
justme 83e5120377 refactor!: rename the project from Monitor to MoShiro
Namespaces, assemblies, projects and directories, the solution, binaries,
images, systemd units, config paths, environment variables, protocol method
names, the media type and the raw chunk magic all move to MoShiro/moshiro.
Existing data and deployments are not compatible and must be recreated.
Also fix the architecture tests after the solution layout change and make
the update tests independent of their order.
2026-10-07 01:49:04 +08:00
justme 17b35223aa ci: sync main into dev [CI SKIP] 2026-10-07 01:46:35 +08:00
justme 4d329f5e16 ci: sync main into dev [CI SKIP] 2026-10-07 01:30:46 +08:00
justme ad17170664 docs: redraw the architecture diagrams and update the README [SKIP TESTS]
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/web-build Pipeline was successful
2026-10-07 01:30:28 +08:00
justme 710aa9e99f ci: sync main into dev [CI SKIP] 2026-10-07 01:28:32 +08:00
justme 71d2866b9c docs: update the architecture [SKIP TESTS] 2026-10-07 01:21:49 +08:00
justme 48664c0ada docs: remove the roadmap and bring the docs in line with the current code [SKIP TESTS]
Probes link to the panel directly, container nodes update through signed
releases, the frontend exists and the ports changed. The roadmap is gone; its
open items live in the README.
2026-10-06 23:27:26 +08:00
justme 198ee556d6 ci: sync main into dev [CI SKIP] 2026-10-06 23:22:52 +08:00
justme da465866d5 fix(web): lift the hovered overview card above its neighbours so the popover is not hidden 2026-10-06 23:22:36 +08:00
justme 3a751a0f89 feat: choose which probes link to which agents from the admin
The panel decides the probe/agent pairs and probes always dial out. A new
fleet.auto_link setting connects every probe to every agent (default); with it
off only the pairs chosen by hand connect, and any pair can be forced on or
off. The Probes page has a link matrix with the switch and per-pair toggles.
2026-10-06 23:19:32 +08:00
justme d69581dcb5 docs: mount the host os-release and fix the agent public host after tunnel enrollment [SKIP TESTS] 2026-10-06 23:11:26 +08:00
justme 8f44e924a4 docs: describe container node deployment and the update flow [SKIP TESTS] 2026-10-06 23:02:16 +08:00
justme 0c39485a78 ci: sync main into dev [CI SKIP] 2026-10-06 22:57:11 +08:00
justme 7a71bf57f9 feat: update controls in the admin, release token scope, build info in CI images
The versions page shows each node's update state with update buttons, API
tokens get a release scope that can only register releases (used by CI), and
the CI image and release builds now pass the tag, commit and build time.
2026-10-06 22:56:56 +08:00
justme 22a98f2eb0 ci: sync main into dev [CI SKIP] 2026-10-06 22:42:20 +08:00
justme 0a66f5e07a feat: signed releases and a host-side updater for container nodes
CI signs the image digest of each agent and probe release; the panel only
stores and forwards it. Nodes write the manifest to their data directory and
a host updater verifies the signature with a local public key, pulls by
digest, restarts the unit and rolls back when it is unhealthy. The panel can
list releases, ask a node to update and show the reported update state.
2026-10-06 22:37:26 +08:00
justme 48072737c9 feat!: probes connect to the backend directly
A probe now links to the backend like an agent (probe.json carries the
server address). The backend sends the agent list as desired state and the
probe reports its platform and build in its own hello. The agent no longer
relays the list or the probe platform. Existing probes must be re-created.
2026-10-06 22:25:16 +08:00
justme 604cbe3123 fix(ci): restore with PublishAot so node images are native, add compose files
Agent and probe images were built with a restore step that lacked
PublishAot, so the publish silently produced a trimmed launcher that looks
for a .dll. Restore now passes PublishAot and the build fails if the binary
is smaller than 2 MB. Also add agent and probe compose files and document
the [CI] marker in AGENTS.md.
2026-10-06 21:43:46 +08:00
justme b242701e9e ci: sync main into dev [CI SKIP] 2026-10-06 21:24:47 +08:00
justme 83f54978d2 feat(web): acrylic theme, node flags and icons, build info
Acrylic cards and topbar over a blurred background, country flags, ISP and
OS icons, per-node version tags, a build info footer and an admin versions
tab. Overview cards drop the sparkline and widen the hover area.
2026-10-06 21:22:04 +08:00
justme 1e5c660d05 feat(backend): report node platform, geo and build info
Agents and probes report OS, arch and build (version, tag, commit, time).
The backend looks up country and ISP of public addresses only, stores them
in node_meta (schema v5) and exposes display info via /api/public/nodes,
/api/public/about and /api/admin/build-info behind site.show_build.
2026-10-06 21:22:04 +08:00
justme 2e470607fa docs: add project agent instructions [SKIP TESTS] 2026-10-06 17:11:07 +08:00
justme a70b89b95c feat(web): show UDP and TCP strips on overview cards
Add 3/6/12 hour ranges with status strips, latency sparklines and a side popover for the hovered interval.
2026-10-06 16:59:17 +08:00
justme a9538eae4a feat(web): add backend switcher and connection status
Split the login page into backend list and credentials, show connection status in the sidebar and top bar, and render dialogs in a portal.
2026-10-06 16:59:17 +08:00
justme 91c63d0cf2 feat(api): add return_session login for cross-origin clients
Return the session token in the login response instead of a cookie, and accept it as a Bearer token without CSRF.
2026-10-06 16:59:16 +08:00
justme 193fc3a95c feat: improve agent management and speed up tests 2026-10-06 15:48:46 +08:00
justme 134c06d605 merge: bring line detail timeline into dev [CI SKIP] 2026-10-06 14:52:48 +08:00
justme d1be70cda2 feat(web): 线路详情页的时间轴、EWMA 平滑和加载进度
- 先选日期,再在连续 48 小时(或 24 小时)的丢包缩略图上拖选区;选区可以跨零点,
  点轴或刻度时离得近的端点贴过去,悬停预览会动的端点;没有数据的时段画成斜纹
- 刻度下方的时长快捷档:30 分钟、1 小时、3 小时、6 小时、12 小时、整天
- 指标卡片平铺,丢包等标明 UDP/TCP;图表按 UDP、TCP、评分分组
- 图表数据刷新时原地更新(不再整张重建),毫秒到 1 秒以上显示成秒
- EWMA 平滑开关(关/轻/中/重),原始数据以淡线垫底
- 数据加载时卡片上显示进度条和占位;useQuery 区分定时刷新和用户触发的加载
- 前端可以在页面里指定后端地址和令牌(配合后端 Monitor__CorsOrigins)
2026-10-06 14:48:58 +08:00
justme 672f6e3dcb fix(ci): emit native apphosts in fastlane images [CI SKIP] 2026-10-05 10:42:34 -07:00
justme 9a375ffd9e fix(build): select AotAnywhere explicitly [CI SKIP] 2026-10-05 10:37:10 -07:00
justme 407c09f6a5 fix(ci): use host networking for native builds [CI SKIP] 2026-10-05 10:30:35 -07:00
justme fffee8937c ci: sync main into dev [CI SKIP] 2026-10-05 10:26:53 -07:00
justme 91485db500 fix(ci): fall back to git diff for empty path lists [CI SKIP] 2026-10-05 10:25:59 -07:00
justme fa34869f18 ci: sync main into dev [CI SKIP] 2026-10-05 10:21:22 -07:00
justme deb5a97802 ci: add dependency-aware main fastlane [CI SKIP] 2026-10-05 10:18:22 -07:00
justme 479f8c25b0 feat(ci): publish agent and probe OCI images [CI SKIP] 2026-10-05 09:33:22 -07:00
justme 2b80bb66d1 ci: use local Gitea endpoint for release uploads 2026-10-05 08:44:19 -07:00
justme 078eacf1cb ci: persist Buildah cache for Linux releases 2026-10-05 08:29:08 -07:00
justme 3db6d5b159 ci: authenticate Zot with API key for justme [CI SKIP] 2026-10-05 22:24:16 +08:00
justme a7a8212c08 ci: push images to public Zot over HTTPS [CI SKIP] 2026-10-05 16:28:15 +08:00
justme 68406843de ci: retry transient Zot registry errors [CI SKIP] 2026-10-04 13:26:09 -07:00
justme fae8c45c90 ci: use bounded runspaces for artifact uploads [CI SKIP] 2026-10-04 13:11:01 -07:00
justme 3bf12ec73d ci: upload release artifacts concurrently [CI SKIP] 2026-10-04 13:05:17 -07:00
justme a9ee4ec34c build: disable PDB debug symbols in releases [CI SKIP] 2026-10-04 12:54:39 -07:00
justme 6b12049df0 ci: drop 32-bit release targets [CI SKIP] 2026-10-04 12:44:32 -07:00
justme d4b4869501 fix(ci): package cross-platform tarballs on Windows [CI SKIP] 2026-10-04 12:38:13 -07:00
justme 92b72d4443 ci: release without blocking on test workflow [CI SKIP] 2026-10-04 12:31:27 -07:00
justme 67b428991f ci: parallelize NativeAOT target builds 2026-10-04 12:29:35 -07:00
justme 55756f7695 ci: cross-publish full NativeAOT matrix from Windows 2026-10-04 12:10:06 -07:00
justme 17a69c1c7a ci: parallelize release components 2026-10-04 11:44:41 -07:00
justme 50d8c07939 ci: gate parallel releases on verified commits 2026-10-05 02:39:21 +08:00
justme 2e549b230d build: use ASP.NET runtime for backend images 2026-10-05 02:24:15 +08:00
justme ace42e5da7 fix(ci): use POST for Gitea release assets 2026-10-05 02:16:14 +08:00
justme ad32afd038 ci: run release builds in parallel 2026-10-05 02:11:29 +08:00
justme 286c9ff7cd ci: publish release assets to Gitea 2026-10-05 02:06:36 +08:00
justme 8aaa574485 fix(ci): make release retryable 2026-10-05 01:21:37 +08:00
justme ce30828ed3 ci: use runner-local Gitea package endpoints 2026-10-05 01:10:17 +08:00
justme a46f45cce3 ci: order multiarch index after builds 2026-10-05 01:05:24 +08:00
justme ce3ba51c02 test: normalize project reference paths across platforms 2026-10-05 00:49:20 +08:00
justme faf1203f0a ci: disable git lfs in clone steps 2026-10-05 00:43:46 +08:00
justme ee422ca366 ci: run verification for push, pull requests and tags 2026-10-05 00:41:29 +08:00
justme c33003c279 ci: restore public Gitea package endpoint 2026-10-04 23:11:37 +08:00
justme 31af31b96f ci: use internal Gitea endpoint 2026-10-04 23:01:59 +08:00
justme 9b6c4e0fd8 ci: skip host OpenAPI generation for arm64 2026-10-04 22:38:26 +08:00
justme ceef543854 ci: verify development branch 2026-10-04 22:20:58 +08:00
justme 299f86dbee ci: configure Woodpecker release pipelines 2026-10-04 22:12:21 +08:00
9 changed files with 134 additions and 10 deletions

No files matched your search

+2
View File
@@ -40,6 +40,8 @@ steps:
GITEA_BASE: http://127.0.0.1:3100
GITEA_TOKEN:
from_secret: gitea_token
RELEASE_SIGNING_KEY:
from_secret: release_signing_key
commands:
- bash ci/publish-gitea-release.sh
depends_on:
+14 -3
View File
@@ -15,6 +15,17 @@ when:
concurrency: 1
steps:
# 前端先打包好(几十秒),两个架构的 Backend 构建再并行:它们把 web/*/dist 内嵌进程序,只读这份产物,不再各自 npm ci
# (并行的两个 npm ci 会同时删 node_modules,互相搞坏)。步骤加了超时:卡住就几分钟内失败,不是耗着。
- name: web-build
image: bash
environment:
NPM_CONFIG_CACHE: /home/justme/.npm
commands:
- npm ci --prefix web --prefer-offline
- timeout 600 npm run build --prefix web
depends_on: []
- name: linux-amd64-backend
image: bash
environment:
@@ -25,7 +36,8 @@ steps:
from_secret: zot_secret
commands:
- bash ci/release-linux.sh amd64 backend
depends_on: []
depends_on:
- web-build
- name: linux-arm64-backend
image: bash
@@ -36,10 +48,9 @@ steps:
ZOT_SECRET:
from_secret: zot_secret
commands:
# 两个架构共用一个工作目录:amd64 先构建并生成前端产物,arm64 在它之后(并行会同时 npm ci,互相删 node_modules)
- bash ci/release-linux.sh arm64 backend
depends_on:
- linux-amd64-backend
- web-build
- name: backend-multiarch-index
image: bash
+1 -1
View File
@@ -13,7 +13,7 @@
<UseArtifactsOutput>true</UseArtifactsOutput>
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
<Deterministic>true</Deterministic>
<Version>0.3.2</Version>
<Version>0.3.3</Version>
<NuGetAudit>false</NuGetAudit>
</PropertyGroup>
<ItemGroup>
+36
View File
@@ -114,4 +114,40 @@ for pid in "${pids[@]}"; do
wait "$pid"
done
# 所有压缩包的校验和汇成一份 SHA256SUMS,用发布私钥签名(base64 的 DER ECDSA,和签名的发布清单是同一把密钥、同一种格式):
# 不走容器的安装(直接下载压缩包)先验这份签名再核对压缩包的哈希,发布页被改也骗不到;没有配置签名密钥(例如 fork)就跳过。
upload_local_asset() {
local name="$1" file="$2" upload_response="$tmp/$1.upload.json" status
if grep -Eq "\"name\"[[:space:]]*:[[:space:]]*\"$name\"" "$assets_json"; then
echo "release asset $name already exists; keeping the existing asset"
return 0
fi
status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \
--request POST --header 'Content-Type: application/octet-stream' --data-binary "@$file" \
--output "$upload_response" --write-out '%{http_code}' "$api/releases/$release_id/assets?name=$name")"
[[ "$status" == 2* ]] || { echo "Gitea Release asset upload failed for $name (HTTP $status)" >&2; cat "$upload_response" >&2; return 1; }
echo "attached $name"
}
if [[ -n "${RELEASE_SIGNING_KEY:-}" ]]; then
sums="$tmp/SHA256SUMS"
: > "$sums"
for archive in "${assets[@]}"; do
curl --fail --silent --show-error --retry 3 --netrc-file "$netrc" \
"$gitea_base/api/packages/justme/generic/moshiro-node/$version/$archive.sha256" -o "$tmp/$archive.sha256"
cat "$tmp/$archive.sha256" >> "$sums"
done
[[ "$(wc -l < "$sums")" -eq "${#assets[@]}" ]] || { echo "SHA256SUMS does not have one line per archive" >&2; exit 1; }
signing_key="$(mktemp)"
chmod 600 "$signing_key"
printf '%s\n' "$RELEASE_SIGNING_KEY" > "$signing_key"
openssl dgst -sha256 -sign "$signing_key" -out "$tmp/SHA256SUMS.sig.bin" "$sums"
rm -f "$signing_key"
base64 -w0 "$tmp/SHA256SUMS.sig.bin" > "$tmp/SHA256SUMS.sig" 2>/dev/null || base64 "$tmp/SHA256SUMS.sig.bin" | tr -d '\n' > "$tmp/SHA256SUMS.sig"
upload_local_asset SHA256SUMS "$sums"
upload_local_asset SHA256SUMS.sig "$tmp/SHA256SUMS.sig"
else
echo "release signing is not configured; SHA256SUMS is not signed"
fi
echo "published Gitea Release $tag: https://git.oeo.one/justme/MoShiro/releases/tag/$tag"
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# 一个组件在所有平台、所有架构的发布压缩包里的可执行文件的 SHA-256(逗号分隔,输出到标准输出)。
# ci/release-binary-hashes.sh <agent|probe> <版本,不带 v> 环境变量:GITEA_BASE、NETRC(curl 的 netrc 文件)
# 签名的发布清单里的 binaries 就是它:节点握手时上报正在运行的可执行文件的哈希,Backend 凭 CI 签名的清单认「官方二进制」——
# 容器、Linux glibc/musl 压缩包、macOS、Windows 的 x64/arm64 都一样,不只是容器镜像里的那两个。
set -euo pipefail
component="${1:?usage: release-binary-hashes.sh <agent|probe> <version>}"; version="${2:?version}"
[[ "$component" =~ ^(agent|probe)$ ]] || { echo "component must be agent or probe" >&2; exit 2; }
gitea_base="${GITEA_BASE:?GITEA_BASE is required}"; gitea_base="${gitea_base%/}"
netrc=(); [[ -n "${NETRC:-}" ]] && netrc=(--netrc-file "$NETRC")
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
# 和 ci/publish-gitea-release.sh 里发布的那 8 种一样(每个组件)
targets=(linux-amd64 linux-arm64 linux-musl-amd64 linux-musl-arm64 macos-x64 macos-arm64 windows-amd64 windows-arm64)
hashes=()
for target in "${targets[@]}"; do
if [[ "$target" == windows-* ]]; then archive="moshiro-$component-v$version-$target.zip"; member="moshiro-$component.exe"
else archive="moshiro-$component-v$version-$target.tar.gz"; member="moshiro-$component"; fi
curl --fail --silent --show-error --retry 3 ${netrc[@]+"${netrc[@]}"} "$gitea_base/api/packages/justme/generic/moshiro-node/$version/$archive" -o "$work/$archive"
if [[ "$archive" == *.zip ]]; then
hash="$(cd "$work" && python3 -c 'import hashlib,sys,zipfile; print(hashlib.sha256(zipfile.ZipFile(sys.argv[1]).read(sys.argv[2])).hexdigest())' "$archive" "$member")"
else
hash="$(tar -xzOf "$work/$archive" "$member" | sha256sum | cut -d' ' -f1)"
fi
[[ "$hash" =~ ^[0-9a-f]{64}$ ]] || { echo "could not hash $member in $archive" >&2; exit 1; }
hashes+=("$hash")
rm -f "$work/$archive"
done
# 不同平台的二进制哈希不会相同;去重只是防御
printf '%s\n' "${hashes[@]}" | sort -u | paste -sd,
+3 -4
View File
@@ -67,8 +67,6 @@ build_arch_image() {
"$gitea_base/api/packages/justme/generic/moshiro-node/$version/$filename" -o "$archive"
mkdir -p "$context/app" "$context/config"
tar -xzf "$archive" -C "$work" "moshiro-$component" fleet-enroll
# 镜像里装的就是这个文件:它的哈希写进签名的发布清单(binaries),节点握手时上报正在运行的二进制的哈希,面板据此认「官方二进制」
sha256sum "$work/moshiro-$component" | cut -d' ' -f1 >> "$work/$component.hashes"
install -m 0755 "$work/fleet-enroll" "$context/app/fleet-enroll"
install -m 0755 "$work/moshiro-$component" "$context/app/moshiro-$component"
: > "$context/config/.keep"
@@ -112,7 +110,6 @@ publish_index() {
}
for component in agent probe; do
rm -f "$work/$component.hashes"
build_arch_image "$component" amd64 musl-amd64
build_arch_image "$component" arm64 musl-arm64
publish_index "$component"
@@ -120,6 +117,8 @@ for component in agent probe; do
# 没有配置签名密钥(例如 fork)就只发镜像、不发清单。
if [[ -n "${RELEASE_SIGNING_KEY:-}" ]]; then
digest="$(cat "$work/$component.digest")"
RELEASE_BINARIES="$(paste -sd, "$work/$component.hashes")" bash "$root/ci/publish-release-offer.sh" "$component" "$image_root/moshiro-$component" "$digest" "$tag" release "$work/$component-release"
# 清单里的 binaries:所有平台、所有架构的发布压缩包里的可执行文件(不只是镜像里的 musl 那两个),节点握手时上报的哈希对得上其中任何一个
binaries="$(NETRC="$netrc" GITEA_BASE="$gitea_base" bash "$root/ci/release-binary-hashes.sh" "$component" "$version")"
RELEASE_BINARIES="$binaries" bash "$root/ci/publish-release-offer.sh" "$component" "$image_root/moshiro-$component" "$digest" "$tag" release "$work/$component-release"
fi
done
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# release-binary-hashes.sh 的测试:用本地的假 Gitea 包仓库(python http.server),造 8 个平台的压缩包(tar.gz 和 zip),
# 核对脚本算出来的哈希就是压缩包里可执行文件的 SHA-256。运行:bash ci/tests/binary-hashes-test.sh
set -eu
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
T="$(mktemp -d)"
SERVER=
trap '[ -z "$SERVER" ] || kill "$SERVER" 2>/dev/null || true; rm -rf "$T" 2>/dev/null || true' EXIT
V=9.9.9
PKG="$T/srv/api/packages/justme/generic/moshiro-node/$V"
mkdir -p "$PKG"
fail=0
expected=()
for component in agent probe; do
for target in linux-amd64 linux-arm64 linux-musl-amd64 linux-musl-arm64 macos-x64 macos-arm64 windows-amd64 windows-arm64; do
stage="$T/stage-$component-$target"; mkdir -p "$stage"
if [[ "$target" == windows-* ]]; then member="moshiro-$component.exe"; else member="moshiro-$component"; fi
printf 'binary of %s for %s\n' "$component" "$target" > "$stage/$member"
printf 'noise' > "$stage/fleet-enroll"; printf 'readme' > "$stage/README.md"
if [[ "$target" == windows-* ]]; then
(cd "$stage" && python3 -c "import zipfile,sys; z=zipfile.ZipFile('out.zip','w'); [z.write(n) for n in sys.argv[1:]]" "$member" fleet-enroll README.md && mv out.zip "$PKG/moshiro-$component-v$V-$target.zip")
else
tar -czf "$PKG/moshiro-$component-v$V-$target.tar.gz" -C "$stage" "$member" fleet-enroll README.md
fi
[[ "$component" == agent ]] && expected+=("$(sha256sum "$stage/$member" | cut -d' ' -f1)")
done
done
want="$(printf '%s\n' "${expected[@]}" | sort -u | paste -sd,)"
(cd "$T/srv" && python3 -u -m http.server 0 --bind 127.0.0.1 >"$T/server.log" 2>&1 & echo $! > "$T/pid")
SERVER="$(cat "$T/pid")"
for _ in $(seq 1 50); do port="$(grep -o 'port [0-9]*' "$T/server.log" | head -1 | cut -d' ' -f2 || true)"; [[ -n "$port" ]] && break; sleep 0.2; done
[[ -n "${port:-}" ]] || { echo "FAIL the fake package server did not start"; cat "$T/server.log"; exit 1; }
got="$(GITEA_BASE="http://127.0.0.1:$port" bash "$ROOT/ci/release-binary-hashes.sh" agent "$V")"
if [[ "$got" == "$want" ]]; then echo "ok agent: all 8 platform binaries hashed ($(echo "$got" | tr ',' '\n' | wc -l) distinct)"; else echo "FAIL agent hashes differ"; echo " want $want"; echo " got $got"; fail=1; fi
[[ "$(echo "$got" | tr ',' '\n' | wc -l)" -eq 8 ]] && echo "ok eight different binaries, none dropped" || { echo "FAIL expected 8 hashes"; fail=1; }
# 缺一个平台的压缩包:整个脚本失败,不能悄悄少签一个
rm "$PKG/moshiro-probe-v$V-macos-arm64.tar.gz"
if GITEA_BASE="http://127.0.0.1:$port" bash "$ROOT/ci/release-binary-hashes.sh" probe "$V" >/dev/null 2>&1; then echo "FAIL a missing archive was ignored"; fail=1; else echo "ok a missing archive fails the script instead of signing fewer binaries"; fi
if bash "$ROOT/ci/release-binary-hashes.sh" other "$V" >/dev/null 2>&1; then echo "FAIL unknown component accepted"; fail=1; else echo "ok unknown component is refused"; fi
exit "$fail"
+3 -1
View File
@@ -337,7 +337,9 @@ CI 给每个镜像的 digest 签名之后,除了(面板在线时)登记给
- **更新器**:配置里 `FEED=auto`(默认)时先看仓库里的清单,验签通过才用;取不到或验不过就用面板放进信箱的那份。`MANIFEST_REF` 可以指定清单镜像,`FEED=off` 关掉;通道取配置里的 `CHANNEL`,没有就按仓库路径推断(`moshiro-dev` → dev,`moshiro` → release)。仓库里的清单验签失败而信箱里又没有东西时,状态会报「无效清单」而不是悄悄当作没有更新。
- **面板自己取**:安装脚本向面板要某个组件的签名清单时,面板里没有(清过库、CI 登记那一步网络失败)或者已经放了十分钟以上,面板就按 OCI 分发协议**匿名**读一次仓库里的清单镜像(只读清单和它的配置两个小 JSON,不拉镜像层),用内嵌的 CI 发布公钥验过签才登记;验不过(仓库或路上被改)就当没有。所以清库之后不用手工补登记,也不会被一份伪造的清单骗到。仓库要匿名可读(`moshiro/**`、`moshiro-dev/**`)。
- **没有宿主机更新器的节点**(一键容器安装、二进制安装)握手时带不来清单:面板用自己手里(登记时验过 CI 签名)的那份把节点的二进制标成「已签名」;不在清单里的只是不标「已签名」,不会因此被拒绝。
- **发版**:打 `vMAJOR.MINOR.PATCH` tag 时,节点镜像(Agent、Probe 的多架构镜像)也和 Backend 一样签清单,清单里带两个架构的二进制哈希,放到 `moshiro/…-manifest:release`。
- **发版**:打 `vMAJOR.MINOR.PATCH` tag 时,节点镜像(Agent、Probe 的多架构镜像)也和 Backend 一样签清单,放到 `moshiro/…-manifest:release`。清单里的 `binaries` 是**所有平台、所有架构**的发布压缩包里可执行文件的 SHA-256(Linux glibc 和 musl、macOS、Windows,x64 和 arm64,每个组件 8 个,`ci/release-binary-hashes.sh`),不只是容器镜像里的那两个:节点握手时上报正在运行的可执行文件的哈希,Backend 凭 CI 签名的清单认「官方二进制」,所以容器和直接下载压缩包安装的节点是一回事。
- **压缩包的校验和也签名**:Release 里除了每个压缩包的 `.sha256`,还有汇总的 `SHA256SUMS` 和 `SHA256SUMS.sig`(用发布私钥签,base64 的 DER ECDSA P-256,和清单同一把密钥)。验证:`base64 -d SHA256SUMS.sig > s.bin && openssl dgst -sha256 -verify packaging/update/release-key.pub.pem -signature s.bin SHA256SUMS`,通过之后再 `sha256sum -c SHA256SUMS`。macOS 的 Developer ID 签名和公证、Windows 的 Authenticode 需要苹果和微软的证书,目前没有做。
- 测试:`bash ci/tests/binary-hashes-test.sh`(本地的假包仓库,8 种平台的 tar.gz 和 zip,缺任何一个压缩包都会失败,不会悄悄少签)。
- **首次部署**:`deploy-node.sh` 默认就走这条路,不需要面板,也不用手动指定镜像(Backend 第一次部署时本来就没有面板)。`--tag` 指定标签时不验签并有警告;`--image` 直接用你给的引用。
- **别人自己部署后端,什么都不用改**:仓库对匿名读开放(`moshiro/*` 和各 `*-manifest` 仓库),然后 `sudo sh deploy-node.sh backend --channel release --public-url https://<自己的域名>`,只是配置,不改代码、不需要自己的 CI;passkey 绑定在自己的域名上。自己 fork 并自建镜像的人要换自己的发布公钥和仓库地址(`--image-repo`)并重新构建,这是 fork 的正常代价。
- 测试:`bash packaging/update/tests/feed-test.sh`(假的 podman + 真的 openssl,覆盖篡改、别的密钥、旧 seq、换钥、仓库不可用回退、首次部署的验签)。
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "moshiro-web",
"private": true,
"version": "0.3.2",
"version": "0.3.3",
"workspaces": [
"display",
"admin"