feat(store): 更新插件商店生成脚本和安全签名功能
All checks were successful
Sign Plugins / sign (push) Successful in 31s
All checks were successful
Sign Plugins / sign (push) Successful in 31s
- 修改 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 模板文件
This commit is contained in:
15
.github/workflows/sign.yml
vendored
15
.github/workflows/sign.yml
vendored
@@ -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
|
||||
|
||||
@@ -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 "]"
|
||||
|
||||
36
scripts/sign-security.sh
Executable file
36
scripts/sign-security.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
KEY_FILE="$1"
|
||||
|
||||
if [ -z "$KEY_FILE" ]; then
|
||||
echo "Usage: $0 <private-key-file>"
|
||||
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!"
|
||||
6
security/keys.json
Normal file
6
security/keys.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"updated_at": 1735560000,
|
||||
"keys": [],
|
||||
"signature": ""
|
||||
}
|
||||
6
security/revocation.json
Normal file
6
security/revocation.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"updated_at": 1735560000,
|
||||
"entries": [],
|
||||
"signature": ""
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user