detached 1f467a1

fix(traffic): reload trusted cidr changes

commit
1f467a18b2480fad5e3963af549cf2daeefc0f8a
author
brice · · 2026-06-30 02:48 UTC
parent
f46f30c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
5 files · +73 -4
M internal/adapters/in/traffic/manager.go +23 -0
@@ -362,6 +362,29 @@ func parseTrustedCIDRs(values []string) ([]*net.IPNet, error) {
362362 return trusted, nil
363363 }
364364
365+func trustedCIDRsEqual(left []string, right []string) bool {
366+ left = normalizedCIDRs(left)
367+ right = normalizedCIDRs(right)
368+ if len(left) != len(right) {
369+ return false
370+ }
371+ for i := range left {
372+ if left[i] != right[i] {
373+ return false
374+ }
375+ }
376+ return true
377+}
378+
379+func normalizedCIDRs(values []string) []string {
380+ out := make([]string, 0, len(values))
381+ for _, value := range values {
382+ out = append(out, strings.TrimSpace(value))
383+ }
384+ sort.Strings(out)
385+ return out
386+}
387+
365388 func trustedRemoteAddr(trusted []*net.IPNet, addr net.Addr) bool {
366389 if len(trusted) == 0 {
367390 return true
@@ -362,6 +362,29 @@ func parseTrustedCIDRs(values []string) ([]*net.IPNet, error) {
362 return trusted, nil362 return trusted, nil
363 }363 }
364 364
365+func trustedCIDRsEqual(left []string, right []string) bool {
366+ left = normalizedCIDRs(left)
367+ right = normalizedCIDRs(right)
368+ if len(left) != len(right) {
369+ return false
370+ }
371+ for i := range left {
372+ if left[i] != right[i] {
373+ return false
374+ }
375+ }
376+ return true
377+}
378+
379+func normalizedCIDRs(values []string) []string {
380+ out := make([]string, 0, len(values))
381+ for _, value := range values {
382+ out = append(out, strings.TrimSpace(value))
383+ }
384+ sort.Strings(out)
385+ return out
386+}
387+
365 func trustedRemoteAddr(trusted []*net.IPNet, addr net.Addr) bool {388 func trustedRemoteAddr(trusted []*net.IPNet, addr net.Addr) bool {
366 if len(trusted) == 0 {389 if len(trusted) == 0 {
367 return true390 return true
M internal/adapters/in/traffic/tcp.go +1 -1
@@ -419,7 +419,7 @@ func (r *entryPointRuntime) closeActiveConns() {
419419 }
420420
421421 func (r *entryPointRuntime) matches(entryPoint domain.EntryPoint) bool {
422- return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol
422+ return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol && trustedCIDRsEqual(r.entryPoint.TrustedCIDRs, entryPoint.TrustedCIDRs)
423423 }
424424
425425 func (r *entryPointRuntime) shouldStopWith(next *entryPointRuntime) bool {
@@ -419,7 +419,7 @@ func (r *entryPointRuntime) closeActiveConns() {
419 }419 }
420 420
421 func (r *entryPointRuntime) matches(entryPoint domain.EntryPoint) bool {421 func (r *entryPointRuntime) matches(entryPoint domain.EntryPoint) bool {
422- return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol422+ return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol && trustedCIDRsEqual(r.entryPoint.TrustedCIDRs, entryPoint.TrustedCIDRs)
423 }423 }
424 424
425 func (r *entryPointRuntime) shouldStopWith(next *entryPointRuntime) bool {425 func (r *entryPointRuntime) shouldStopWith(next *entryPointRuntime) bool {
M internal/adapters/in/traffic/tcp_test.go +24 -1
@@ -88,13 +88,36 @@ func TestTCPPassthroughRejectsUntrustedCIDR(t *testing.T) {
8888 require.NoError(t, manager.Apply(context.Background(), &graph))
8989 defer shutdownManager(t, manager)
9090
91+ assertTCPRejected(t, manager, graph.EntryPoints[0].Address, 1)
92+}
93+
94+func TestTCPPassthroughReloadAppliesTrustedCIDRChange(t *testing.T) {
95+ backend := startTCPEchoServer(t, 0)
96+ graph := tcpGraph(t, freeTCPAddress(t), backend.address)
97+ graph.EntryPoints[0].TrustedCIDRs = []string{"127.0.0.0/8"}
98+ manager := NewManager()
99+ require.NoError(t, manager.Apply(context.Background(), &graph))
100+ defer shutdownManager(t, manager)
101+
91102 conn := dialTCP(t, graph.EntryPoints[0].Address)
103+ assertRoundTrip(t, conn, "allowed")
104+ require.NoError(t, conn.Close())
105+
106+ updated := graph
107+ updated.EntryPoints[0].TrustedCIDRs = []string{"192.0.2.0/24"}
108+ require.NoError(t, manager.Apply(context.Background(), &updated))
109+ assertTCPRejected(t, manager, graph.EntryPoints[0].Address, 1)
110+}
111+
112+func assertTCPRejected(t *testing.T, manager *Manager, address string, refused int64) {
113+ t.Helper()
114+ conn := dialTCP(t, address)
92115 defer conn.Close()
93116 _, err := conn.Write([]byte("blocked"))
94117 require.NoError(t, err)
95118 _, err = bufio.NewReader(conn).ReadByte()
96119 require.Error(t, err)
97- assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == 1 }, time.Second, 10*time.Millisecond)
120+ assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == refused }, time.Second, 10*time.Millisecond)
98121 }
99122
100123 func TestTCPPassthroughEcho(t *testing.T) {
@@ -88,13 +88,36 @@ func TestTCPPassthroughRejectsUntrustedCIDR(t *testing.T) {
88 require.NoError(t, manager.Apply(context.Background(), &graph))88 require.NoError(t, manager.Apply(context.Background(), &graph))
89 defer shutdownManager(t, manager)89 defer shutdownManager(t, manager)
90 90
91+ assertTCPRejected(t, manager, graph.EntryPoints[0].Address, 1)
92+}
93+
94+func TestTCPPassthroughReloadAppliesTrustedCIDRChange(t *testing.T) {
95+ backend := startTCPEchoServer(t, 0)
96+ graph := tcpGraph(t, freeTCPAddress(t), backend.address)
97+ graph.EntryPoints[0].TrustedCIDRs = []string{"127.0.0.0/8"}
98+ manager := NewManager()
99+ require.NoError(t, manager.Apply(context.Background(), &graph))
100+ defer shutdownManager(t, manager)
101+
91 conn := dialTCP(t, graph.EntryPoints[0].Address)102 conn := dialTCP(t, graph.EntryPoints[0].Address)
103+ assertRoundTrip(t, conn, "allowed")
104+ require.NoError(t, conn.Close())
105+
106+ updated := graph
107+ updated.EntryPoints[0].TrustedCIDRs = []string{"192.0.2.0/24"}
108+ require.NoError(t, manager.Apply(context.Background(), &updated))
109+ assertTCPRejected(t, manager, graph.EntryPoints[0].Address, 1)
110+}
111+
112+func assertTCPRejected(t *testing.T, manager *Manager, address string, refused int64) {
113+ t.Helper()
114+ conn := dialTCP(t, address)
92 defer conn.Close()115 defer conn.Close()
93 _, err := conn.Write([]byte("blocked"))116 _, err := conn.Write([]byte("blocked"))
94 require.NoError(t, err)117 require.NoError(t, err)
95 _, err = bufio.NewReader(conn).ReadByte()118 _, err = bufio.NewReader(conn).ReadByte()
96 require.Error(t, err)119 require.Error(t, err)
97- assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == 1 }, time.Second, 10*time.Millisecond)120+ assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == refused }, time.Second, 10*time.Millisecond)
98 }121 }
99 122
100 func TestTCPPassthroughEcho(t *testing.T) {123 func TestTCPPassthroughEcho(t *testing.T) {
M internal/adapters/in/traffic/udp.go +1 -1
@@ -328,7 +328,7 @@ func (r *udpEntryPointRuntime) closeSessions() {
328328 }
329329
330330 func (r *udpEntryPointRuntime) matches(entryPoint domain.EntryPoint) bool {
331- return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol
331+ return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol && trustedCIDRsEqual(r.entryPoint.TrustedCIDRs, entryPoint.TrustedCIDRs)
332332 }
333333
334334 func (r *udpEntryPointRuntime) isClosed() bool { return r.closed.Load() }
@@ -328,7 +328,7 @@ func (r *udpEntryPointRuntime) closeSessions() {
328 }328 }
329 329
330 func (r *udpEntryPointRuntime) matches(entryPoint domain.EntryPoint) bool {330 func (r *udpEntryPointRuntime) matches(entryPoint domain.EntryPoint) bool {
331- return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol331+ return r.entryPoint.Name == entryPoint.Name && r.entryPoint.Address == entryPoint.Address && r.entryPoint.Protocol == entryPoint.Protocol && trustedCIDRsEqual(r.entryPoint.TrustedCIDRs, entryPoint.TrustedCIDRs)
332 }332 }
333 333
334 func (r *udpEntryPointRuntime) isClosed() bool { return r.closed.Load() }334 func (r *udpEntryPointRuntime) isClosed() bool { return r.closed.Load() }
M internal/adapters/in/traffic/udp_test.go +24 -1
@@ -32,7 +32,30 @@ func TestUDPRejectsUntrustedCIDR(t *testing.T) {
3232 require.NoError(t, manager.Apply(context.Background(), &graph))
3333 defer shutdownManager(t, manager)
3434
35+ assertUDPRejected(t, manager, graph.EntryPoints[0].Address, 1)
36+}
37+
38+func TestUDPReloadAppliesTrustedCIDRChange(t *testing.T) {
39+ backend := startUDPEchoServer(t)
40+ graph := udpGraph(t, freeUDPAddress(t), backend.address)
41+ graph.EntryPoints[0].TrustedCIDRs = []string{"127.0.0.0/8"}
42+ manager := NewManager()
43+ require.NoError(t, manager.Apply(context.Background(), &graph))
44+ defer shutdownManager(t, manager)
45+
3546 conn := dialUDP(t, graph.EntryPoints[0].Address)
47+ assertUDPRoundTrip(t, conn, "allowed")
48+ require.NoError(t, conn.Close())
49+
50+ updated := graph
51+ updated.EntryPoints[0].TrustedCIDRs = []string{"192.0.2.0/24"}
52+ require.NoError(t, manager.Apply(context.Background(), &updated))
53+ assertUDPRejected(t, manager, graph.EntryPoints[0].Address, 1)
54+}
55+
56+func assertUDPRejected(t *testing.T, manager *Manager, address string, refused int64) {
57+ t.Helper()
58+ conn := dialUDP(t, address)
3659 defer conn.Close()
3760 _, err := conn.Write([]byte("blocked"))
3861 require.NoError(t, err)
@@ -40,7 +63,7 @@ func TestUDPRejectsUntrustedCIDR(t *testing.T) {
4063 buf := make([]byte, 32)
4164 _, err = conn.Read(buf)
4265 require.Error(t, err)
43- assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == 1 }, time.Second, 10*time.Millisecond)
66+ assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == refused }, time.Second, 10*time.Millisecond)
4467 }
4568
4669 func TestUDPTwoClientsGetIsolatedSessions(t *testing.T) {
@@ -32,7 +32,30 @@ func TestUDPRejectsUntrustedCIDR(t *testing.T) {
32 require.NoError(t, manager.Apply(context.Background(), &graph))32 require.NoError(t, manager.Apply(context.Background(), &graph))
33 defer shutdownManager(t, manager)33 defer shutdownManager(t, manager)
34 34
35+ assertUDPRejected(t, manager, graph.EntryPoints[0].Address, 1)
36+}
37+
38+func TestUDPReloadAppliesTrustedCIDRChange(t *testing.T) {
39+ backend := startUDPEchoServer(t)
40+ graph := udpGraph(t, freeUDPAddress(t), backend.address)
41+ graph.EntryPoints[0].TrustedCIDRs = []string{"127.0.0.0/8"}
42+ manager := NewManager()
43+ require.NoError(t, manager.Apply(context.Background(), &graph))
44+ defer shutdownManager(t, manager)
45+
35 conn := dialUDP(t, graph.EntryPoints[0].Address)46 conn := dialUDP(t, graph.EntryPoints[0].Address)
47+ assertUDPRoundTrip(t, conn, "allowed")
48+ require.NoError(t, conn.Close())
49+
50+ updated := graph
51+ updated.EntryPoints[0].TrustedCIDRs = []string{"192.0.2.0/24"}
52+ require.NoError(t, manager.Apply(context.Background(), &updated))
53+ assertUDPRejected(t, manager, graph.EntryPoints[0].Address, 1)
54+}
55+
56+func assertUDPRejected(t *testing.T, manager *Manager, address string, refused int64) {
57+ t.Helper()
58+ conn := dialUDP(t, address)
36 defer conn.Close()59 defer conn.Close()
37 _, err := conn.Write([]byte("blocked"))60 _, err := conn.Write([]byte("blocked"))
38 require.NoError(t, err)61 require.NoError(t, err)
@@ -40,7 +63,7 @@ func TestUDPRejectsUntrustedCIDR(t *testing.T) {
40 buf := make([]byte, 32)63 buf := make([]byte, 32)
41 _, err = conn.Read(buf)64 _, err = conn.Read(buf)
42 require.Error(t, err)65 require.Error(t, err)
43- assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == 1 }, time.Second, 10*time.Millisecond)66+ assert.Eventually(t, func() bool { return manager.Status().Counters.TotalRefused == refused }, time.Second, 10*time.Millisecond)
44 }67 }
45 68
46 func TestUDPTwoClientsGetIsolatedSessions(t *testing.T) {69 func TestUDPTwoClientsGetIsolatedSessions(t *testing.T) {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close