main
go 151 lines 4.79 KB
Raw
1 package discovery
2
3 import (
4 "testing"
5 "time"
6
7 "github.com/gosuda/portal-tunnel/v2/portal/auth"
8 "github.com/gosuda/portal-tunnel/v2/portal/identity"
9 "github.com/gosuda/portal-tunnel/v2/types"
10 )
11
12 func mustSigningIdentity(t *testing.T) types.Identity {
13 t.Helper()
14 signingIdentity, err := identity.ResolveSecp256k1Identity("")
15 if err != nil {
16 t.Fatalf("identity.ResolveSecp256k1Identity() error = %v", err)
17 }
18 return signingIdentity
19 }
20
21 func mustUnsignedDescriptor(t *testing.T, signing types.Identity, relayURL string) types.RelayDescriptor {
22 t.Helper()
23 now := time.Now().UTC().Truncate(time.Microsecond)
24 return types.RelayDescriptor{
25 Address: signing.Address,
26 Version: types.DiscoveryVersion,
27 IssuedAt: now,
28 ExpiresAt: now.Add(time.Hour),
29 APIHTTPSAddr: relayURL,
30 }
31 }
32
33 func mustSignedDescriptor(t *testing.T, signing types.Identity, relayURL string, issuedAt time.Time) types.RelayDescriptor {
34 t.Helper()
35 authority, err := identity.NewLocalAuthority(signing)
36 if err != nil {
37 t.Fatalf("identity.NewLocalAuthority() error = %v", err)
38 }
39 signed, err := auth.SignRelayDescriptor(types.RelayDescriptor{
40 Address: signing.Address,
41 Version: types.DiscoveryVersion,
42 IssuedAt: issuedAt,
43 ExpiresAt: issuedAt.Add(DiscoveryDescriptorTTL),
44 APIHTTPSAddr: relayURL,
45 }, authority)
46 if err != nil {
47 t.Fatalf("SignRelayDescriptor() error = %v", err)
48 }
49 return signed
50 }
51
52 func TestInsertAnnouncedAcceptsValidDescriptor(t *testing.T) {
53 set := NewRelaySet(nil)
54 signing := mustSigningIdentity(t)
55 now := time.Now().UTC().Truncate(time.Microsecond)
56 desc := mustSignedDescriptor(t, signing, "https://relay-ann.example", now)
57 if err := set.InsertAnnounced(desc, now); err != nil {
58 t.Fatalf("InsertAnnounced() error = %v", err)
59 }
60 if got := relayStates(set); len(got) != 1 {
61 t.Fatalf("len(relayStates()) = %d, want 1", len(got))
62 }
63 }
64
65 func TestInsertAnnouncedRejectsUnsigned(t *testing.T) {
66 set := NewRelaySet(nil)
67 signing := mustSigningIdentity(t)
68 now := time.Now().UTC().Truncate(time.Microsecond)
69 desc := mustUnsignedDescriptor(t, signing, "https://relay-unsigned.example")
70 if err := set.InsertAnnounced(desc, now); err == nil {
71 t.Fatal("expected unsigned reject")
72 }
73 }
74
75 func TestInsertAnnouncedIgnoresSupersededRollback(t *testing.T) {
76 set := NewRelaySet(nil)
77 signing := mustSigningIdentity(t)
78 now := time.Now().UTC().Truncate(time.Microsecond)
79 relayURL := "https://relay-roll.example"
80 newer := mustSignedDescriptor(t, signing, relayURL, now)
81 if err := set.InsertAnnounced(newer, now); err != nil {
82 t.Fatalf("seed insert error = %v", err)
83 }
84 older := mustSignedDescriptor(t, signing, relayURL, now.Add(-time.Minute))
85 if err := set.InsertAnnounced(older, now); err != nil {
86 t.Fatalf("superseded insert error = %v", err)
87 }
88
89 states := relayStates(set)
90 if len(states) != 1 {
91 t.Fatalf("len(relayStates()) = %d, want 1", len(states))
92 }
93 if got := states[0].Descriptor.IssuedAt; !got.Equal(newer.IssuedAt) {
94 t.Fatalf("stored issued_at = %v, want %v", got, newer.IssuedAt)
95 }
96 }
97
98 func TestInsertAnnouncedRejectsRollbackAcrossRelayURL(t *testing.T) {
99 set := NewRelaySet(nil)
100 signing := mustSigningIdentity(t)
101 now := time.Now().UTC().Truncate(time.Microsecond)
102 newer := mustSignedDescriptor(t, signing, "https://relay-roll-new.example", now)
103 if err := set.InsertAnnounced(newer, now); err != nil {
104 t.Fatalf("seed insert error = %v", err)
105 }
106 older := mustSignedDescriptor(t, signing, "https://relay-roll-old.example", now.Add(-time.Minute))
107 if err := set.InsertAnnounced(older, now); err == nil {
108 t.Fatal("expected rollback reject")
109 }
110 }
111
112 func TestInsertAnnouncedBlocksCrossIdentityTakeover(t *testing.T) {
113 set := NewRelaySet(nil)
114 owner := mustSigningIdentity(t)
115 attacker := mustSigningIdentity(t)
116 now := time.Now().UTC().Truncate(time.Microsecond)
117 relayURL := "https://relay-takeover.example"
118
119 ownerDesc := mustSignedDescriptor(t, owner, relayURL, now)
120 if err := set.InsertAnnounced(ownerDesc, now); err != nil {
121 t.Fatalf("owner insert error = %v", err)
122 }
123
124 attackerDesc := mustSignedDescriptor(t, attacker, relayURL, now.Add(time.Second))
125 if err := set.InsertAnnounced(attackerDesc, now); err == nil {
126 t.Fatal("expected takeover reject")
127 }
128
129 states := relayStates(set)
130 if len(states) != 1 {
131 t.Fatalf("len(relayStates()) = %d, want 1", len(states))
132 }
133 if got := states[0].Descriptor.Address; got != owner.Address {
134 t.Fatalf("retained address = %q, want %q", got, owner.Address)
135 }
136 }
137
138 func TestAnnounceLimiterAllowsBurstThenThrottles(t *testing.T) {
139 limiter := NewAnnounceLimiter(60, 5) // 1/sec sustained, burst 5
140 for i := range 5 {
141 if !limiter.Allow("10.0.0.1") {
142 t.Fatalf("burst[%d] should be allowed", i)
143 }
144 }
145 if limiter.Allow("10.0.0.1") {
146 t.Fatal("burst budget should be exhausted")
147 }
148 if !limiter.Allow("10.0.0.2") {
149 t.Fatal("different IP should have its own bucket")
150 }
151 }