feat(agent-go): consume panel-hosted Reality keys (persist keyMaterial, backfill unused-provided keys)

This commit is contained in:
aaaamoooo
2026-10-20 07:20:59 +00:00
parent 483b891306
commit f2b7d8e181
4 changed files with 219 additions and 5 deletions
+123
View File
@@ -355,6 +355,129 @@ func TestReadSingBoxObservations(t *testing.T) {
}
}
// ---- Panel-hosted Reality keys --------------------------------------------------
func TestPersistKeyMaterialPanelWins(t *testing.T) {
dir := t.TempDir()
old := cfg.KeyFile
defer func() { cfg.KeyFile = old }()
// Produce a genuine second keypair in a scratch file to act as the
// panel-hosted pair (realityKey always generates valid x25519 keys).
cfg.KeyFile = filepath.Join(dir, "scratch.json")
panel, err := realityKey("reality-node-host")
if err != nil {
t.Fatal(err)
}
// The node has a divergent pre-hosting local pair under the same id.
cfg.KeyFile = filepath.Join(dir, "keys.json")
local, err := realityKey("reality-node-host")
if err != nil {
t.Fatal(err)
}
if local.PrivateKey == panel.PrivateKey {
t.Fatal("scratch pairs collided")
}
provided := map[string]realityKeyPair{
"reality-node-host": {PrivateKey: panel.PrivateKey, PublicKey: panel.PublicKey},
}
if err := persistKeyMaterial(provided); err != nil {
t.Fatal(err)
}
got, err := realityKey("reality-node-host")
if err != nil {
t.Fatal(err)
}
if got.PrivateKey != panel.PrivateKey || got.PublicKey != panel.PublicKey {
t.Fatalf("panel pair did not win: got %s/%s want %s/%s", got.PrivateKey, got.PublicKey, panel.PrivateKey, panel.PublicKey)
}
info, err := os.Stat(cfg.KeyFile)
if err != nil {
t.Fatal(err)
}
if info.Mode().Perm() != 0o600 {
t.Fatalf("keys.json mode %v, want 0600", info.Mode().Perm())
}
// Persisting identical material is a byte-for-byte no-op.
before, _ := os.ReadFile(cfg.KeyFile)
if err := persistKeyMaterial(provided); err != nil {
t.Fatal(err)
}
after, _ := os.ReadFile(cfg.KeyFile)
if string(before) != string(after) {
t.Fatal("repeat persist changed keys.json")
}
// Entries without both halves are ignored.
if err := persistKeyMaterial(map[string]realityKeyPair{"reality-node-bad": {PrivateKey: "x"}}); err != nil {
t.Fatal(err)
}
keys, err := loadKeys()
if err != nil {
t.Fatal(err)
}
if _, ok := keys["reality-node-bad"]; ok {
t.Fatal("incomplete keyMaterial entry was persisted")
}
if err := persistKeyMaterial(nil); err != nil {
t.Fatal(err)
}
}
func TestMaterializeBackfillTracking(t *testing.T) {
old := cfg.KeyFile
cfg.KeyFile = filepath.Join(t.TempDir(), "keys.json")
defer func() { cfg.KeyFile = old }()
resource := map[string]interface{}{
"inbounds": []interface{}{map[string]interface{}{
"tls": map[string]interface{}{"reality": map[string]interface{}{
"private_key": "${REALITY_PRIVATE:reality-node-bf}",
}},
}},
}
// No panel material: materialize generates locally; the used pair must be
// tracked and reported as backfill so the panel can adopt it.
art := &artifacts{}
out, err := materialize(resource, art)
if err != nil {
t.Fatal(err)
}
used := art.used["reality-node-bf"]
if used.PrivateKey == "" || used.PublicKey == "" {
t.Fatal("materialize did not track the used keypair")
}
outMap, _ := asMap(out)
inbound, _ := asMap(asSlice(outMap["inbounds"])[0])
tlsMap, _ := asMap(inbound["tls"])
realityMap, _ := asMap(tlsMap["reality"])
deployed := realityMap["private_key"]
if deployed != used.PrivateKey {
t.Fatalf("materialized private_key %v != tracked pair %q", deployed, used.PrivateKey)
}
backfill := backfillKeyMaterial(art.used, nil)
if len(backfill) != 1 || backfill["reality-node-bf"] != used {
t.Fatalf("backfill mismatch: %v", backfill)
}
// When the panel provided the id, nothing is backfilled.
provided := map[string]realityKeyPair{"reality-node-bf": used}
if got := backfillKeyMaterial(art.used, provided); len(got) != 0 {
t.Fatalf("expected no backfill for panel-provided key, got %v", got)
}
// toMap carries keyMaterial only when set.
art.KeyMaterial = backfill
if _, ok := art.toMap()["keyMaterial"]; !ok {
t.Fatal("toMap missing keyMaterial")
}
art.KeyMaterial = nil
if _, ok := art.toMap()["keyMaterial"]; ok {
t.Fatal("toMap should omit empty keyMaterial")
}
}
// ---- misc -----------------------------------------------------------------------
func TestSafeError(t *testing.T) {