feat(infra/domains): 域名池 + CDN 前置 + 签名端点分发 (tsk_NU9JuUweHWMt)
- domains.md: 四组域名隔离登记 + 冷备池 ≥5 + 启用流程(不含身份信息) - cdn/terraform: Cloudflare 配置即代码(WAF/bot/速率限制/代理DNS/回源鉴权注入)+ 30min 重放 Runbook - server/internal/originauth: 回源鉴权中间件,非 CDN 网段或鉴权头不符一律 403,支持双值轮换 - tools/endpoint-signer: 离线 Ed25519 签名 CLI(端点 + 公告文档,单调版本防回滚,key_id 双公钥轮换) - tools/publish-mirrors: ≥3 镜像发布 + hash 一致性校验 + 故障转移取回 - CLIENT-CONTRACT.md: schema/验签/防回滚/合并/兜底链/channel 客户端契约 - 出站独立出口要求写入部署文档;私钥/token/身份信息一律不入库 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
// Command publish-mirrors pushes a signed distribution document to ≥3 mirrors
|
||||
// and verifies every mirror serves byte-identical content (doc/05 §1/§5).
|
||||
//
|
||||
// Subcommands:
|
||||
//
|
||||
// publish -in doc.json -config mirrors.json [-name endpoints.v1.json] [-verify-only]
|
||||
// fetch -config mirrors.json -name endpoints.v1.json [-keys "id=b64"] [-type endpoints|notices]
|
||||
//
|
||||
// The mirrors config lists each destination's local content root (dir) and an
|
||||
// optional fetch_url used for read-back verification / failover fetch. The
|
||||
// per-mirror sync_cmd (e.g. wrangler / aws s3 cp) is recorded for the operator
|
||||
// but never executed here — publishing writes the object into dir, and the
|
||||
// deploy ships dir to its platform out of band.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/wangjia/pangolin/infra/domains/tools/internal/endpoint"
|
||||
"github.com/wangjia/pangolin/infra/domains/tools/internal/mirror"
|
||||
"github.com/wangjia/pangolin/infra/domains/tools/internal/sign"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
var err error
|
||||
switch os.Args[1] {
|
||||
case "publish":
|
||||
err = cmdPublish(os.Args[2:])
|
||||
case "fetch":
|
||||
err = cmdFetch(os.Args[2:])
|
||||
case "-h", "--help", "help":
|
||||
usage()
|
||||
return
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown subcommand %q\n", os.Args[1])
|
||||
usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprint(os.Stderr, `publish-mirrors — push signed docs to ≥3 mirrors and verify consistency
|
||||
|
||||
publish -in doc.json -config mirrors.json [-name endpoints.v1.json] [-verify-only]
|
||||
fetch -config mirrors.json -name endpoints.v1.json [-keys "id=b64"] [-type endpoints|notices]
|
||||
`)
|
||||
}
|
||||
|
||||
func loadConfig(path string) (mirror.Config, error) {
|
||||
var cfg mirror.Config
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if err := json.Unmarshal(raw, &cfg); err != nil {
|
||||
return cfg, fmt.Errorf("parse mirrors config: %w", err)
|
||||
}
|
||||
if len(cfg.Mirrors) < 3 {
|
||||
return cfg, fmt.Errorf("at least 3 mirrors required, got %d", len(cfg.Mirrors))
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func cmdPublish(args []string) error {
|
||||
fs := flag.NewFlagSet("publish", flag.ContinueOnError)
|
||||
in := fs.String("in", "", "signed document to publish")
|
||||
cfgPath := fs.String("config", "", "mirrors config JSON")
|
||||
name := fs.String("name", "", "published object name (default: basename of -in)")
|
||||
verifyOnly := fs.Bool("verify-only", false, "skip writing; only verify existing mirror content matches -in")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if *in == "" || *cfgPath == "" {
|
||||
return errors.New("-in and -config are required")
|
||||
}
|
||||
cfg, err := loadConfig(*cfgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content, err := os.ReadFile(*in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
objectName := *name
|
||||
if objectName == "" {
|
||||
objectName = filepath.Base(*in)
|
||||
}
|
||||
want := mirror.SHA256Hex(content)
|
||||
|
||||
if !*verifyOnly {
|
||||
results, perr := mirror.Publish(content, objectName, cfg.Mirrors)
|
||||
for _, r := range results {
|
||||
if r.Err != nil {
|
||||
fmt.Printf(" ✗ %-16s %v\n", r.Name, r.Err)
|
||||
} else {
|
||||
fmt.Printf(" ✓ %-16s %s sha256=%s\n", r.Name, r.Path, r.SHA256)
|
||||
}
|
||||
}
|
||||
if perr != nil {
|
||||
return fmt.Errorf("publish had failures: %w", perr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := mirror.VerifyConsistency(objectName, cfg.Mirrors, want); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("consistency OK: %d/%d mirrors serve sha256=%s\n", len(cfg.Mirrors), len(cfg.Mirrors), want)
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdFetch(args []string) error {
|
||||
fs := flag.NewFlagSet("fetch", flag.ContinueOnError)
|
||||
cfgPath := fs.String("config", "", "mirrors config JSON")
|
||||
name := fs.String("name", "", "object name to fetch")
|
||||
keys := fs.String("keys", "", "optional key ring id=base64pub[,...] to verify signature on fetch")
|
||||
docType := fs.String("type", "endpoints", "payload type when -keys given: endpoints|notices")
|
||||
out := fs.String("out", "", "write fetched content here (default: stdout)")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if *cfgPath == "" || *name == "" {
|
||||
return errors.New("-config and -name are required")
|
||||
}
|
||||
cfg, err := loadConfig(*cfgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var validate func([]byte) error
|
||||
if *keys != "" {
|
||||
ring, err := sign.ParseKeyRing([]string{*keys})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validate = func(b []byte) error {
|
||||
if *docType == "endpoints" {
|
||||
_, _, e := endpoint.VerifyDocument(b, ring, 0)
|
||||
return e
|
||||
}
|
||||
env, e := sign.Parse(b)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return sign.Verify(env, ring)
|
||||
}
|
||||
}
|
||||
|
||||
content, winner, err := mirror.Fetch(*name, cfg.Mirrors, validate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "fetched from mirror %q\n", winner)
|
||||
if *out == "" {
|
||||
_, err = os.Stdout.Write(content)
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(*out, content, 0o644)
|
||||
}
|
||||
Reference in New Issue
Block a user