feat!: probes generate their own keys and enroll with a join token; node and peer keys are separate [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful

This commit is contained in:
justme committed 2026-10-07 16:57:34 +08:00
1 parent 1ff847e45a
commit a9f562441f
51 files changed
+505 -749

No files matched your search

+1
View File
@@ -7,6 +7,7 @@ LABEL org.opencontainers.image.title="MoShiro Probe" \
org.opencontainers.image.version="$APP_VERSION"
COPY --chown=1654:1654 app/moshiro-probe /usr/local/bin/moshiro-probe
COPY --chown=1654:1654 app/fleet-enroll /usr/local/bin/fleet-enroll
COPY --chown=1654:1654 config/ /etc/moshiro/
USER 1654:1654
@@ -3,7 +3,8 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine-aot AS build
WORKDIR /src/backend
COPY restore/backend/ ./
# restore 也要带 PublishAot:不然不会还原 ILCompiler,之后 --no-restore 的发布就悄悄变成「裁剪过的框架依赖程序」
RUN dotnet restore probe/src/MoShiro.Probe -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts
RUN dotnet restore probe/src/MoShiro.Probe -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts \
&& dotnet restore fleet/tools/fleet-enroll -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts
COPY source/backend/ ./
# 构建信息:CI 通过 --build-arg 传入;不传就是空(放在 restore 之后,每次构建都变的时间不会让依赖还原的缓存失效)
@@ -14,10 +15,14 @@ ENV MOSHIRO_TAG=$MOSHIRO_TAG MOSHIRO_COMMIT=$MOSHIRO_COMMIT MOSHIRO_BUILT_AT=$MO
RUN dotnet publish probe/src/MoShiro.Probe -m:3 -c Release -r linux-musl-x64 \
--self-contained true --no-restore -p:PublishAot=true -p:UseAotAnywhere=false \
-p:UseAppHost=true -p:StripSymbols=true \
-p:DebugType=none -p:DebugSymbols=false --artifacts-path /artifacts -o /out/probe
-p:DebugType=none -p:DebugSymbols=false --artifacts-path /artifacts -o /out/probe \
&& dotnet publish fleet/tools/fleet-enroll -m:3 -c Release -r linux-musl-x64 \
--self-contained true --no-restore -p:PublishAot=true -p:UseAotAnywhere=false \
-p:UseAppHost=true -p:StripSymbols=true \
-p:DebugType=none -p:DebugSymbols=false --artifacts-path /artifacts -o /out/enroll
# 原生程序有几 MB;几十 KB 的是只会去找 .dll 的启动器
RUN test -x /out/probe/moshiro-probe \
&& test "$(stat -c%s /out/probe/moshiro-probe)" -gt 2000000
RUN test -x /out/probe/moshiro-probe && test -x /out/enroll/fleet-enroll \
&& test "$(stat -c%s /out/probe/moshiro-probe)" -gt 2000000 && test "$(stat -c%s /out/enroll/fleet-enroll)" -gt 2000000
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine
@@ -28,6 +33,7 @@ LABEL org.opencontainers.image.title="MoShiro Probe" \
org.opencontainers.image.version="$APP_VERSION"
COPY --chown=1654:1654 --from=build /out/probe/moshiro-probe /usr/local/bin/moshiro-probe
COPY --chown=1654:1654 --from=build /out/enroll/fleet-enroll /usr/local/bin/fleet-enroll
COPY --chown=1654:1654 config/ /etc/moshiro/
USER 1654:1654
+15 -15
View File
@@ -3,7 +3,7 @@
# 重新安装单元和更新器,然后重启节点;已有的身份和配置不会被覆盖。
#
# 首次部署 Agent:sudo sh deploy-node.sh agent --image-repo REPO --server https://panel --join jt_xxx --name hk-1
# 首次部署 Probe:sudo sh deploy-node.sh probe --image-repo REPO --probe-json ./probe.json
# 首次部署 Probe:sudo sh deploy-node.sh probe --image-repo REPO --server https://panel --join jt_xxx --name home-1
# 更新(配置迁移、单元和更新器、镜像):sudo sh deploy-node.sh agent (其余参数沿用上次的更新器配置)
#
# 需要 root、systemd,以及 Podman 或 Docker;脚本要和 packaging/ 里的 systemd/、update/ 放在一起。
@@ -12,13 +12,13 @@ set -eu
usage() {
echo "usage: deploy-node.sh <agent|probe> [--engine podman|docker] [--image-repo R] [--tag T] [--image REF]" >&2
echo " [--server URL --join TOKEN --name NAME] [--probe-json FILE] [--policy auto|manual] [--authfile F]" >&2
echo " [--server URL --join TOKEN --name NAME] [--policy auto|manual] [--authfile F]" >&2
exit 2
}
ROLE="${1:-}"; shift || true
case "$ROLE" in agent|probe) ;; *) usage ;; esac
ENGINE=""; REPO=""; TAG=latest; IMAGE=""; SERVER=""; JOIN=""; NAME=""; PROBE_JSON=""; POLICY=""; AUTHFILE=""
ENGINE=""; REPO=""; TAG=latest; IMAGE=""; SERVER=""; JOIN=""; NAME=""; POLICY=""; AUTHFILE=""
while [ $# -gt 0 ]; do
case "$1" in
--engine) ENGINE="$2"; shift 2 ;;
@@ -28,7 +28,6 @@ while [ $# -gt 0 ]; do
--server) SERVER="$2"; shift 2 ;;
--join) JOIN="$2"; shift 2 ;;
--name) NAME="$2"; shift 2 ;;
--probe-json) PROBE_JSON="$2"; shift 2 ;;
--policy) POLICY="$2"; shift 2 ;;
--authfile) AUTHFILE="$2"; shift 2 ;;
*) usage ;;
@@ -82,13 +81,14 @@ in_volume() {
}
in_volume 'mkdir -p /etc/moshiro/update' </dev/null
# 3. 配置
# 3. 配置:身份由节点自己生成(Agent 和 Probe 一样),注册之后面板只认这把公钥
ID_DIR="/etc/moshiro/$ROLE-id"
if ! in_volume "test -f $ID_DIR/node.json" </dev/null; then
[ -n "$SERVER" ] && [ -n "$JOIN" ] && [ -n "$NAME" ] || { echo "first deployment needs --server, --join and --name" >&2; exit 1; }
"$ENGINE" run --rm --pull=never -v "$V_CFG:/etc/moshiro" --entrypoint /usr/local/bin/fleet-enroll "$IMAGE" \
--server "$SERVER" --join "$JOIN" --name "$NAME" --identity-dir "$ID_DIR"
fi
if [ "$ROLE" = agent ]; then
if ! in_volume 'test -f /etc/moshiro/agent-id/node.json' </dev/null; then
[ -n "$SERVER" ] && [ -n "$JOIN" ] && [ -n "$NAME" ] || { echo "first deployment needs --server, --join and --name" >&2; exit 1; }
"$ENGINE" run --rm --pull=never -v "$V_CFG:/etc/moshiro" --entrypoint /usr/local/bin/fleet-enroll "$IMAGE" \
--server "$SERVER" --join "$JOIN" --name "$NAME" --identity-dir /etc/moshiro/agent-id
fi
if ! in_volume 'test -f /etc/moshiro/agent.json' </dev/null; then
[ -n "$SERVER" ] || { echo "agent.json is missing; pass --server to create it" >&2; exit 1; }
printf '{"fleet":{"server":"%s","identity_dir":"/etc/moshiro/agent-id"},"listen":{"bind":"::"}}\n' "$SERVER" \
@@ -100,11 +100,11 @@ if [ "$ROLE" = agent ]; then
echo "config: listen.bind 0.0.0.0 -> ::"
fi
else
if [ -n "$PROBE_JSON" ]; then
in_volume 'umask 077; cat > /etc/moshiro/probe.json' < "$PROBE_JSON"
echo "config: imported probe.json"
elif ! in_volume 'test -f /etc/moshiro/probe.json' </dev/null; then
echo "first deployment needs --probe-json (download it from the panel)" >&2; exit 1
if ! in_volume 'test -f /etc/moshiro/probe.json' </dev/null; then
[ -n "$SERVER" ] || { echo "probe.json is missing; pass --server to create it" >&2; exit 1; }
printf '{"fleet":{"server":"%s","identity_dir":"/etc/moshiro/probe-id"}}\n' "$SERVER" \
| in_volume 'umask 077; cat > /etc/moshiro/probe.json'
echo "config: created probe.json"
fi
fi
+29 -34
View File
@@ -3,7 +3,7 @@
# POSIX sh: runs on Debian/Ubuntu/Alpine/OpenWrt (busybox).
#
# ./install.sh agent --server https://panel.example.com --join <join-token> --name hk-1
# ./install.sh probe [--probe-json /path/to/probe.json]
# ./install.sh probe --server https://panel --join jt_xxx --name home-1
# ./install.sh uninstall agent|probe [--purge]
#
# Binaries go to /usr/lib/moshiro (root-owned: the collector cannot rewrite its own executable);
@@ -62,27 +62,34 @@ install_binary() {
put 0755 "$HERE/$src" "$PREFIX/$src"
}
write_agent_config() {
cat >"$CONFIG/agent.json" <<JSON
write_config() {
if [ "$1" = agent ]; then
cat >"$CONFIG/agent.json" <<JSON
{
"fleet": { "server": "$1", "identity_dir": "$CONFIG" },
"fleet": { "server": "$2", "identity_dir": "$CONFIG" },
"listen": { "bind": "::", "control_port": 7443, "udp_ports": [7443], "tcp_ports": [7444] },
"limits": { "max_total_mbps": 200, "max_total_pps": 20000, "allowed_target_cidrs": [], "max_concurrent_tests": 1 }
}
JSON
chmod 0640 "$CONFIG/agent.json"
if [ "$have_user" -eq 1 ]; then chown "root:$(id -gn "$USER_NAME")" "$CONFIG/agent.json"; fi
else
cat >"$CONFIG/probe.json" <<JSON
{
"fleet": { "server": "$2", "identity_dir": "$CONFIG" }
}
JSON
fi
chmod 0640 "$CONFIG/$1.json"
if [ "$have_user" -eq 1 ]; then chown "root:$(id -gn "$USER_NAME")" "$CONFIG/$1.json"; fi
}
install_role() {
role="$1"; shift
server=""; join=""; name=""; probe_json=""; no_start=0
server=""; join=""; name=""; no_start=0
while [ $# -gt 0 ]; do
case "$1" in
--server) server="$2"; shift 2 ;;
--join) join="$2"; shift 2 ;;
--name) name="$2"; shift 2 ;;
--probe-json) probe_json="$2"; shift 2 ;;
--no-start) no_start=1; shift ;;
*) die "unknown option: $1" ;;
esac
@@ -96,7 +103,7 @@ install_role() {
chmod 0750 "$CONFIG"
install_binary "moshiro-$role"
if [ "$role" = agent ]; then install_binary fleet-enroll; fi
install_binary fleet-enroll
say "installed binaries to $PREFIX"
init="$(init_system)"
@@ -114,25 +121,17 @@ install_role() {
esac
ready=0
if [ "$role" = agent ]; then
if [ -n "$server" ] && [ ! -f "$CONFIG/node.json" ]; then
[ -n "$join" ] && [ -n "$name" ] || die "--server needs --join and --name to enroll"
say "enrolling as $name"
if [ "$have_user" -eq 1 ]; then
su -s /bin/sh "$USER_NAME" -c "'$PREFIX/fleet-enroll' --server '$server' --join '$join' --name '$name' --identity-dir '$CONFIG'"
else
"$PREFIX/fleet-enroll" --server "$server" --join "$join" --name "$name" --identity-dir "$CONFIG"
fi
if [ -n "$server" ] && [ ! -f "$CONFIG/node.json" ]; then
[ -n "$join" ] && [ -n "$name" ] || die "--server needs --join and --name to enroll"
say "enrolling as $name"
if [ "$have_user" -eq 1 ]; then
su -s /bin/sh "$USER_NAME" -c "'$PREFIX/fleet-enroll' --server '$server' --join '$join' --name '$name' --identity-dir '$CONFIG'"
else
"$PREFIX/fleet-enroll" --server "$server" --join "$join" --name "$name" --identity-dir "$CONFIG"
fi
if [ ! -f "$CONFIG/agent.json" ] && [ -n "$server" ]; then write_agent_config "$server"; fi
if [ -f "$CONFIG/agent.json" ] && [ -f "$CONFIG/node.json" ]; then ready=1; fi
else
if [ -n "$probe_json" ]; then
put 0600 "$probe_json" "$CONFIG/probe.json"
if [ "$have_user" -eq 1 ]; then chown "$USER_NAME" "$CONFIG/probe.json"; fi
fi
if [ -f "$CONFIG/probe.json" ]; then ready=1; fi
fi
if [ ! -f "$CONFIG/$role.json" ] && [ -n "$server" ]; then write_config "$role" "$server"; fi
if [ -f "$CONFIG/$role.json" ] && [ -f "$CONFIG/node.json" ]; then ready=1; fi
if [ "$ready" -eq 1 ] && [ "$no_start" -eq 0 ]; then
case "$init" in
@@ -142,13 +141,9 @@ install_role() {
say "moshiro-$role is installed"
else
say "installed, not started yet. Next:"
if [ "$role" = agent ]; then
echo " $PREFIX/fleet-enroll --server <panel-url> --join <token> --name <name> --identity-dir $CONFIG (as the '$USER_NAME' user)"
echo " write $CONFIG/agent.json (see README.md), then: systemctl enable --now moshiro-agent"
else
echo " save the panel's probe identity (POST /api/admin/probe/identity) as $CONFIG/probe.json (mode 0600, owner $USER_NAME)"
echo " then: systemctl enable --now moshiro-probe (OpenWrt: /etc/init.d/moshiro-probe enable && /etc/init.d/moshiro-probe start)"
fi
echo " $PREFIX/fleet-enroll --server <panel-url> --join <token> --name <name> --identity-dir $CONFIG (as the '$USER_NAME' user)"
echo " write $CONFIG/$role.json (see README.md), then: systemctl enable --now moshiro-$role"
[ "$role" = agent ] || echo " (OpenWrt: /etc/init.d/moshiro-probe enable && /etc/init.d/moshiro-probe start)"
fi
}
@@ -166,7 +161,7 @@ uninstall() {
rm -f /etc/init.d/moshiro-probe /lib/upgrade/keep.d/moshiro ;;
esac
rm -f "$PREFIX/moshiro-$role"
if [ "$role" = agent ]; then rm -f "$PREFIX/fleet-enroll"; fi
rm -f "$PREFIX/fleet-enroll"
rmdir "$PREFIX" 2>/dev/null || true
if [ "$purge" -eq 1 ]; then
rm -rf "$CONFIG"
+1 -1
View File
@@ -10,7 +10,7 @@ CONF=/etc/moshiro/probe.json
start_service() {
[ -f "$CONF" ] || {
logger -t moshiro-probe "$CONF not found: download it from the panel (POST /api/admin/probe/identity)"
logger -t moshiro-probe "$CONF not found: enroll first (install.sh probe --server URL --join TOKEN --name NAME)"
return 1
}
procd_open_instance