From 9b4a12b51a2c6a50bee7f850bc4059610330b4b7 Mon Sep 17 00:00:00 2001 From: Flik Date: Tue, 30 Dec 2025 22:06:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(store):=20=E6=9B=B4=E6=96=B0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=95=86=E5=BA=97=E7=94=9F=E6=88=90=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=92=8C=E5=AE=89=E5=85=A8=E7=AD=BE=E5=90=8D=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 generate-store.sh 脚本添加 Gitea raw 文件基础 URL 配置 - 在 generate-store.sh 中为每个插件添加 download_url 和 signature_url 字段 - 扩展 signtool 工具添加 sign-json 命令用于签名 JSON 配置文件 - 更新 GitHub Actions 工作流添加对 security/*.json 文件的监控 - 新增 sign-security.sh 脚本用于批量签名安全相关 JSON 文件 - 添加 security/keys.json 和 security/revocation.json 模板文件 --- .github/workflows/sign.yml | 15 ++++++++- scripts/generate-store.sh | 16 ++++++++-- scripts/sign-security.sh | 36 +++++++++++++++++++++ security/keys.json | 6 ++++ security/revocation.json | 6 ++++ tools/signtool/main.go | 65 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 141 insertions(+), 3 deletions(-) create mode 100755 scripts/sign-security.sh create mode 100644 security/keys.json create mode 100644 security/revocation.json diff --git a/.github/workflows/sign.yml b/.github/workflows/sign.yml index 92389da..e9a17fc 100644 --- a/.github/workflows/sign.yml +++ b/.github/workflows/sign.yml @@ -6,6 +6,7 @@ on: paths: - 'plugins/**/*.js' - 'plugins/**/manifest.json' + - 'security/*.json' workflow_dispatch: jobs: @@ -31,13 +32,25 @@ jobs: bash scripts/sign-all.sh /tmp/private.key rm -f /tmp/private.key + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + - name: Generate store.json run: bash scripts/generate-store.sh > store.json + - name: Sign security files + env: + SIGNING_KEY: ${{ secrets.PLUGIN_SIGNING_KEY }} + run: | + echo "$SIGNING_KEY" > /tmp/private.key + chmod 600 /tmp/private.key + bash scripts/sign-security.sh /tmp/private.key + rm -f /tmp/private.key + - name: Commit changes run: | git config user.name "GitHub Actions" git config user.email "actions@github.com" - git add -A "plugins/**/*.sig" store.json + git add -A "plugins/**/*.sig" store.json "security/*.json" git diff --staged --quiet || git commit -m "chore: update signatures and store" git push diff --git a/scripts/generate-store.sh b/scripts/generate-store.sh index e407d60..be7dd9c 100755 --- a/scripts/generate-store.sh +++ b/scripts/generate-store.sh @@ -2,7 +2,9 @@ set -e REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -STORE_FILE="$REPO_ROOT/store.json" + +# Gitea raw 文件基础 URL +BASE_URL="${STORE_BASE_URL:-https://git.92coco.cn:8443/flik/GoTunnel-Plugins/raw/branch/main}" echo "[" @@ -10,13 +12,23 @@ first=true for manifest in "$REPO_ROOT"/plugins/*/manifest.json; do [ -f "$manifest" ] || continue + # 获取插件目录名 + plugin_dir=$(dirname "$manifest") + plugin_name=$(basename "$plugin_dir") + + # 构建下载 URL 和签名 URL + download_url="$BASE_URL/plugins/$plugin_name/plugin.js" + signature_url="$BASE_URL/plugins/$plugin_name/plugin.js.sig" + if [ "$first" = true ]; then first=false else echo "," fi - cat "$manifest" + # 使用 jq 添加 download_url 和 signature_url 字段 + jq --arg dl "$download_url" --arg sig "$signature_url" \ + '. + {download_url: $dl, signature_url: $sig}' "$manifest" done echo "]" diff --git a/scripts/sign-security.sh b/scripts/sign-security.sh new file mode 100755 index 0000000..e30d65b --- /dev/null +++ b/scripts/sign-security.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +KEY_FILE="$1" + +if [ -z "$KEY_FILE" ]; then + echo "Usage: $0 " + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(dirname "$SCRIPT_DIR")" +SIGNTOOL="$REPO_ROOT/signtool" + +# 构建 signtool +if [ ! -f "$SIGNTOOL" ]; then + echo "Building signtool..." + cd "$REPO_ROOT" + go build -o signtool ./tools/signtool +fi + +cd "$REPO_ROOT" + +# 签名撤销列表 +if [ -f "security/revocation.json" ]; then + echo "Signing revocation.json..." + "$SIGNTOOL" sign-json -key "$KEY_FILE" security/revocation.json +fi + +# 签名公钥列表 +if [ -f "security/keys.json" ]; then + echo "Signing keys.json..." + "$SIGNTOOL" sign-json -key "$KEY_FILE" security/keys.json +fi + +echo "Done!" diff --git a/security/keys.json b/security/keys.json new file mode 100644 index 0000000..a6cd9e4 --- /dev/null +++ b/security/keys.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "updated_at": 1735560000, + "keys": [], + "signature": "" +} diff --git a/security/revocation.json b/security/revocation.json new file mode 100644 index 0000000..48b7425 --- /dev/null +++ b/security/revocation.json @@ -0,0 +1,6 @@ +{ + "version": 1, + "updated_at": 1735560000, + "entries": [], + "signature": "" +} diff --git a/tools/signtool/main.go b/tools/signtool/main.go index 0bc6f6a..3ba6264 100644 --- a/tools/signtool/main.go +++ b/tools/signtool/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "flag" "fmt" "os" @@ -20,6 +21,8 @@ func main() { cmdKeygen() case "sign": cmdSign(os.Args[2:]) + case "sign-json": + cmdSignJSON(os.Args[2:]) default: printUsage() os.Exit(1) @@ -32,6 +35,7 @@ func printUsage() { fmt.Println("Usage:") fmt.Println(" signtool keygen") fmt.Println(" signtool sign -key KEY -name NAME -version VER FILE") + fmt.Println(" signtool sign-json -key KEY FILE") } func cmdKeygen() { @@ -104,3 +108,64 @@ func cmdSign(args []string) { fmt.Printf(" 插件: %s v%s\n", *name, *version) fmt.Printf(" 哈希: %s\n", payload.SourceHash) } + +// cmdSignJSON 签名 JSON 配置文件(撤销列表、公钥列表等) +func cmdSignJSON(args []string) { + fs := flag.NewFlagSet("sign-json", flag.ExitOnError) + keyFile := fs.String("key", "", "私钥文件路径") + fs.Parse(args) + + if *keyFile == "" || fs.NArg() < 1 { + fmt.Println("用法: signtool sign-json -key KEY FILE") + os.Exit(1) + } + + // 读取私钥 + keyData, err := os.ReadFile(*keyFile) + if err != nil { + fmt.Fprintf(os.Stderr, "读取私钥失败: %v\n", err) + os.Exit(1) + } + + privKey, err := sign.DecodePrivateKey(strings.TrimSpace(string(keyData))) + if err != nil { + fmt.Fprintf(os.Stderr, "解析私钥失败: %v\n", err) + os.Exit(1) + } + + // 读取 JSON 文件 + jsonFile := fs.Arg(0) + data, err := os.ReadFile(jsonFile) + if err != nil { + fmt.Fprintf(os.Stderr, "读取文件失败: %v\n", err) + os.Exit(1) + } + + // 解析 JSON + var obj map[string]interface{} + if err := json.Unmarshal(data, &obj); err != nil { + fmt.Fprintf(os.Stderr, "解析 JSON 失败: %v\n", err) + os.Exit(1) + } + + // 移除现有签名字段 + delete(obj, "signature") + + // 序列化待签名数据 + signData, _ := json.Marshal(obj) + + // 签名 + signature := sign.SignBase64(privKey, signData) + + // 添加签名字段 + obj["signature"] = signature + + // 写回文件 + output, _ := json.MarshalIndent(obj, "", " ") + if err := os.WriteFile(jsonFile, output, 0644); err != nil { + fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err) + os.Exit(1) + } + + fmt.Printf("签名成功: %s\n", jsonFile) +}