fix(harness): bind the resilience relay to loopback by default
The relay forwards SSH and UDP without authentication, and its defaults listened on every interface. It now defaults to 127.0.0.1 with a -listen flag for UDP; the disposable fixture passes -tcp :2222 -listen 0.0.0.0 explicitly.
- commit
- 706193c225cdacc5e5f318e81ab617cc53791255
- author
- brice · · 2026-10-08 06:01 UTC
- parent
- fd82dd0
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +21 -4
M scripts/remote-resilience-harness/relay/main.go +20 -3
| @@ -35,6 +35,7 @@ type relay struct { | |||
| 35 | 35 | ctx context.Context | |
| 36 | 36 | cancel context.CancelFunc | |
| 37 | 37 | remote string | |
| 38 | + | listen net.IP | |
| 38 | 39 | degraded bool | |
| 39 | 40 | blackout atomic.Bool | |
| 40 | 41 | bytes atomic.Uint64 | |
| @@ -48,21 +49,37 @@ type relay struct { | |||
| 48 | 49 | func main() { | |
| 49 | 50 | remote := flag.String("remote", "remote", "fixture upstream host") | |
| 50 | 51 | control := flag.String("control", "", "required private Unix control path") | |
| 51 | - | tcp := flag.String("tcp", ":2222", "SSH listener") | |
| 52 | + | // The relay forwards without authentication, so it listens on loopback | |
| 53 | + | // unless the disposable fixture asks for every interface. | |
| 54 | + | tcp := flag.String("tcp", "127.0.0.1:2222", "SSH listener") | |
| 55 | + | listen := flag.String("listen", "127.0.0.1", "UDP proxy listen IP") | |
| 52 | 56 | degraded := flag.Bool("degraded", false, "enable seeded degraded profile") | |
| 53 | 57 | flag.Parse() | |
| 54 | 58 | if *control == "" { | |
| 55 | 59 | fmt.Fprintln(os.Stderr, "-control is required") | |
| 56 | 60 | os.Exit(2) | |
| 57 | 61 | } | |
| 62 | + | listenIP := net.ParseIP(*listen) | |
| 63 | + | if listenIP == nil { | |
| 64 | + | fmt.Fprintln(os.Stderr, "-listen must be an IP address") | |
| 65 | + | os.Exit(2) | |
| 66 | + | } | |
| 58 | 67 | ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) | |
| 59 | - | r := &relay{ctx: ctx, cancel: cancel, remote: *remote, degraded: *degraded, connections: make(map[net.Conn]struct{})} | |
| 68 | + | r := &relay{ctx: ctx, cancel: cancel, remote: *remote, listen: listenIP, degraded: *degraded, connections: make(map[net.Conn]struct{})} | |
| 60 | 69 | if err := r.serve(*tcp, *control); err != nil { | |
| 61 | 70 | fmt.Fprintln(os.Stderr, err) | |
| 62 | 71 | os.Exit(1) | |
| 63 | 72 | } | |
| 64 | 73 | } | |
| 65 | 74 | ||
| 75 | + | // listenIP is the UDP proxy bind address, loopback when unset. | |
| 76 | + | func (r *relay) listenIP() net.IP { | |
| 77 | + | if r.listen == nil { | |
| 78 | + | return net.IPv4(127, 0, 0, 1) | |
| 79 | + | } | |
| 80 | + | return r.listen | |
| 81 | + | } | |
| 82 | + | ||
| 66 | 83 | func (r *relay) serve(addr, path string) error { | |
| 67 | 84 | listener, err := net.Listen("tcp", addr) | |
| 68 | 85 | if err != nil { | |
| @@ -172,7 +189,7 @@ func (r *relay) apply(c command) (any, error) { | |||
| 172 | 189 | if len(r.proxies) >= 32 { | |
| 173 | 190 | return nil, errors.New("UDP listener limit") | |
| 174 | 191 | } | |
| 175 | - | p, err := quicnettest.New(quicnettest.Config{ServerAddr: upstream, ListenAddr: &net.UDPAddr{IP: net.IPv4zero}, Seed: 317, ToServer: link, ToClient: link}) | |
| 192 | + | p, err := quicnettest.New(quicnettest.Config{ServerAddr: upstream, ListenAddr: &net.UDPAddr{IP: r.listenIP()}, Seed: 317, ToServer: link, ToClient: link}) | |
| 176 | 193 | if err != nil { | |
| 177 | 194 | return nil, err | |
| 178 | 195 | } | |
| @@ -35,6 +35,7 @@ type relay struct { | |||||
| 35 | ctx context.Context | 35 | ctx context.Context | ||
| 36 | cancel context.CancelFunc | 36 | cancel context.CancelFunc | ||
| 37 | remote string | 37 | remote string | ||
| 38 | + | listen net.IP | |||
| 38 | degraded bool | 39 | degraded bool | ||
| 39 | blackout atomic.Bool | 40 | blackout atomic.Bool | ||
| 40 | bytes atomic.Uint64 | 41 | bytes atomic.Uint64 | ||
| @@ -48,21 +49,37 @@ type relay struct { | |||||
| 48 | func main() { | 49 | func main() { | ||
| 49 | remote := flag.String("remote", "remote", "fixture upstream host") | 50 | remote := flag.String("remote", "remote", "fixture upstream host") | ||
| 50 | control := flag.String("control", "", "required private Unix control path") | 51 | control := flag.String("control", "", "required private Unix control path") | ||
| 51 | - | tcp := flag.String("tcp", ":2222", "SSH listener") | 52 | + | // The relay forwards without authentication, so it listens on loopback |
| 53 | + | // unless the disposable fixture asks for every interface. | |||
| 54 | + | tcp := flag.String("tcp", "127.0.0.1:2222", "SSH listener") | |||
| 55 | + | listen := flag.String("listen", "127.0.0.1", "UDP proxy listen IP") | |||
| 52 | degraded := flag.Bool("degraded", false, "enable seeded degraded profile") | 56 | degraded := flag.Bool("degraded", false, "enable seeded degraded profile") | ||
| 53 | flag.Parse() | 57 | flag.Parse() | ||
| 54 | if *control == "" { | 58 | if *control == "" { | ||
| 55 | fmt.Fprintln(os.Stderr, "-control is required") | 59 | fmt.Fprintln(os.Stderr, "-control is required") | ||
| 56 | os.Exit(2) | 60 | os.Exit(2) | ||
| 57 | } | 61 | } | ||
| 62 | + | listenIP := net.ParseIP(*listen) | |||
| 63 | + | if listenIP == nil { | |||
| 64 | + | fmt.Fprintln(os.Stderr, "-listen must be an IP address") | |||
| 65 | + | os.Exit(2) | |||
| 66 | + | } | |||
| 58 | ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) | 67 | ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) | ||
| 59 | - | r := &relay{ctx: ctx, cancel: cancel, remote: *remote, degraded: *degraded, connections: make(map[net.Conn]struct{})} | 68 | + | r := &relay{ctx: ctx, cancel: cancel, remote: *remote, listen: listenIP, degraded: *degraded, connections: make(map[net.Conn]struct{})} |
| 60 | if err := r.serve(*tcp, *control); err != nil { | 69 | if err := r.serve(*tcp, *control); err != nil { | ||
| 61 | fmt.Fprintln(os.Stderr, err) | 70 | fmt.Fprintln(os.Stderr, err) | ||
| 62 | os.Exit(1) | 71 | os.Exit(1) | ||
| 63 | } | 72 | } | ||
| 64 | } | 73 | } | ||
| 65 | 74 | ||||
| 75 | + | // listenIP is the UDP proxy bind address, loopback when unset. | |||
| 76 | + | func (r *relay) listenIP() net.IP { | |||
| 77 | + | if r.listen == nil { | |||
| 78 | + | return net.IPv4(127, 0, 0, 1) | |||
| 79 | + | } | |||
| 80 | + | return r.listen | |||
| 81 | + | } | |||
| 82 | + | ||||
| 66 | func (r *relay) serve(addr, path string) error { | 83 | func (r *relay) serve(addr, path string) error { | ||
| 67 | listener, err := net.Listen("tcp", addr) | 84 | listener, err := net.Listen("tcp", addr) | ||
| 68 | if err != nil { | 85 | if err != nil { | ||
| @@ -172,7 +189,7 @@ func (r *relay) apply(c command) (any, error) { | |||||
| 172 | if len(r.proxies) >= 32 { | 189 | if len(r.proxies) >= 32 { | ||
| 173 | return nil, errors.New("UDP listener limit") | 190 | return nil, errors.New("UDP listener limit") | ||
| 174 | } | 191 | } | ||
| 175 | - | p, err := quicnettest.New(quicnettest.Config{ServerAddr: upstream, ListenAddr: &net.UDPAddr{IP: net.IPv4zero}, Seed: 317, ToServer: link, ToClient: link}) | 192 | + | p, err := quicnettest.New(quicnettest.Config{ServerAddr: upstream, ListenAddr: &net.UDPAddr{IP: r.listenIP()}, Seed: 317, ToServer: link, ToClient: link}) |
| 176 | if err != nil { | 193 | if err != nil { | ||
| 177 | return nil, err | 194 | return nil, err | ||
| 178 | } | 195 | } | ||
M scripts/remote-resilience-harness/visual.py +1 -1
| @@ -76,7 +76,7 @@ def main(): | |||
| 76 | 76 | if transport not in ("quic", "ssh"): | |
| 77 | 77 | raise ValueError("VEV_RESILIENCE_TRANSPORT must be quic or ssh") | |
| 78 | 78 | relay_args = ["-degraded"] if os.environ.get("VEV_RESILIENCE_DEGRADED") == "1" else [] | |
| 79 | - | relay = subprocess.Popen(["docker", "exec", client, "/tmp/relay", "-remote", "upstream", "-control", "/home/demo/relay/control.sock", *relay_args]) | |
| 79 | + | relay = subprocess.Popen(["docker", "exec", client, "/tmp/relay", "-remote", "upstream", "-control", "/home/demo/relay/control.sock", "-tcp", ":2222", "-listen", "0.0.0.0", *relay_args]) | |
| 80 | 80 | processes.append(relay) | |
| 81 | 81 | control_id = 0 | |
| 82 | 82 | def control(op, **fields): | |
| @@ -76,7 +76,7 @@ def main(): | |||||
| 76 | if transport not in ("quic", "ssh"): | 76 | if transport not in ("quic", "ssh"): | ||
| 77 | raise ValueError("VEV_RESILIENCE_TRANSPORT must be quic or ssh") | 77 | raise ValueError("VEV_RESILIENCE_TRANSPORT must be quic or ssh") | ||
| 78 | relay_args = ["-degraded"] if os.environ.get("VEV_RESILIENCE_DEGRADED") == "1" else [] | 78 | relay_args = ["-degraded"] if os.environ.get("VEV_RESILIENCE_DEGRADED") == "1" else [] | ||
| 79 | - | relay = subprocess.Popen(["docker", "exec", client, "/tmp/relay", "-remote", "upstream", "-control", "/home/demo/relay/control.sock", *relay_args]) | 79 | + | relay = subprocess.Popen(["docker", "exec", client, "/tmp/relay", "-remote", "upstream", "-control", "/home/demo/relay/control.sock", "-tcp", ":2222", "-listen", "0.0.0.0", *relay_args]) |
| 80 | processes.append(relay) | 80 | processes.append(relay) | ||
| 81 | control_id = 0 | 81 | control_id = 0 | ||
| 82 | def control(op, **fields): | 82 | def control(op, **fields): | ||