mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-27 12:53:05 +00:00
feat(deploy): 部署前校验 master.key 可解封配置 + llmsproxy -show-secrets
密钥校验(deploy.sh) - 新增 verify_master_key,在 build/替换任何文件之前执行。失败则二进制与 配置分毫未动、服务不受影响(已负向验证:缺钥匙、错钥匙两种情况都挡住) - 走真实的 -show-secrets 解密路径,而不是只检查钥匙文件格式——格式合法 但内容不匹配(重新生成、恢复了错的备份、换机器)同样会被拒 - 钥匙来源与 config 包一致:LLMS_PROXY_MASTER_KEY 优先,否则 dirname(runtime_file)/master.key - 配置里没有密文时跳过并提示(首次加密场景) -show-secrets - llmsproxy -show-secrets -config <path>:把凭据打到 stdout 后退出 - 不启动任何东西、不写任何文件(已验证 mtime 不变) - 加密往返无损:封存前后输出逐字节一致 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -15,6 +15,7 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -225,3 +226,47 @@ func (c *Config) migratePlaintextSecrets() error {
|
||||
log.Printf("[config] sealed %d plaintext credential(s) in %s", n, c.Path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintSecrets writes the config's credentials to stdout in the clear and
|
||||
// returns an error only when the file cannot be read or the master key does not
|
||||
// match. It is the operator counterpart to sealing-at-rest: with keys stored as
|
||||
// ciphertext, "which key is this source using" must still be answerable.
|
||||
//
|
||||
// It deliberately takes a path rather than a *Config so the caller cannot
|
||||
// accidentally hand it a config that has already been unsealed in memory, and it
|
||||
// never writes anything.
|
||||
func PrintSecrets(path string) error {
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
box, err := NewSecretBox(cfg.RuntimeFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w (is master.key present and intact?)", err)
|
||||
}
|
||||
if err := cfg.normalizeSecrets(box); err != nil {
|
||||
return err
|
||||
}
|
||||
w := os.Stdout
|
||||
fmt.Fprintf(w, "# %s — %d source(s), %d gateway key(s)\n", path, len(cfg.Sources), len(cfg.Keys))
|
||||
for _, s := range cfg.Sources {
|
||||
fmt.Fprintf(w, "source %-16s api_key=%s\n", s.Name, orNone(s.APIKey))
|
||||
for k, v := range s.Headers {
|
||||
if v != "" {
|
||||
fmt.Fprintf(w, "source %-16s header[%s]=%s\n", s.Name, k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, k := range cfg.Keys {
|
||||
fmt.Fprintf(w, "key %-16s role=%-5s %s\n", k.Name, k.Role, k.Key)
|
||||
}
|
||||
fmt.Fprintf(w, "gateway_keys (legacy): %s\n", strings.Join(cfg.GatewayKeys, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func orNone(s string) string {
|
||||
if s == "" {
|
||||
return "(unset)"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user