@@ -250,22 +250,32 @@ fn stub_smoke() {
250250 ok ( "broker UDS /v1/health → ok" ) ;
251251
252252 // 3. Upload the prebuilt stub binary and kick it off. We start it under
253- // `setsid` so closing the ssh session doesn't SIGHUP the child.
253+ // `setsid` so closing the ssh session doesn't SIGHUP the child. Stub
254+ // binds 0.0.0.0:<STUB_PORT> so the dashboard is reachable directly
255+ // from outside the VM as well as via SSH port-forward.
254256 step ( "3/7 scp + launch coolify-stub" ) ;
255257 scp_upload ( & cfg. ssh_key , & host, & stub_bin, REMOTE_BIN ) . expect ( "scp stub binary" ) ;
256258 ssh ( & cfg. ssh_key , & host, & format ! ( "chmod +x {REMOTE_BIN}" ) ) . expect ( "chmod stub" ) ;
259+ // coold's INPUT chain is default-deny via the coolify firewall scaffold,
260+ // so we explicitly accept inbound connections to the stub's port before
261+ // starting it. Idempotent: -C checks before -I inserts.
262+ let open_port = format ! (
263+ "iptables -C INPUT -p tcp --dport {STUB_PORT} -j ACCEPT 2>/dev/null || \
264+ iptables -I INPUT -p tcp --dport {STUB_PORT} -j ACCEPT"
265+ ) ;
266+ let _ = ssh ( & cfg. ssh_key , & host, & open_port) ;
257267 let wg_ip = wg0_ip ( & cfg. ssh_key , & host) ;
258268 let launch = format ! (
259269 "rm -f {REMOTE_LOG}; \
260- COOLIFY_HOSTS={wg_ip} BROKER_SOCKET_PATH=/run/coolify/broker.sock PORT={STUB_PORT} HOST=127 .0.0.1 \
270+ COOLIFY_HOSTS={wg_ip} BROKER_SOCKET_PATH=/run/coolify/broker.sock PORT={STUB_PORT} HOST=0 .0.0.0 \
261271 setsid nohup {REMOTE_BIN} >{REMOTE_LOG} 2>&1 < /dev/null & \
262272 echo $!",
263273 ) ;
264274 let pid = ssh ( & cfg. ssh_key , & host, & launch)
265275 . unwrap_or_else ( |e| panic ! ( "launch stub on {host}: {e}" ) )
266276 . trim ( )
267277 . to_string ( ) ;
268- ok ( & format ! ( "stub launched pid={pid}" ) ) ;
278+ ok ( & format ! ( "stub launched pid={pid} (0.0.0.0:{STUB_PORT}) " ) ) ;
269279
270280 // Ensure cleanup even on panic. Drop order: this runs *before*
271281 // EphemeralCluster::drop so logs are always retrievable for triage.
@@ -394,14 +404,25 @@ fn stub_smoke() {
394404 if std:: env:: var ( "E2E_KEEP_VMS" ) . as_deref ( ) == Ok ( "1" ) {
395405 e2e_tests:: log_line ( "" ) ;
396406 e2e_tests:: log_line ( & format ! ( "E2E_KEEP_VMS=1 — stub left running on {host}" ) ) ;
397- e2e_tests:: log_line ( "Reach the dashboard via SSH port-forward (stub binds 127.0.0.1 + firewall default-deny):" ) ;
398407 e2e_tests:: log_line ( & format ! (
399- " ssh -i {key} -N -L 3000:127.0.0.1:{STUB_PORT} root@{host}" ,
408+ "Direct URL: http://{host}:{STUB_PORT}"
409+ ) ) ;
410+ e2e_tests:: log_line ( "SSH port-forward:" ) ;
411+ e2e_tests:: log_line ( & format ! (
412+ " ssh -i {key} -N -L {STUB_PORT}:127.0.0.1:{STUB_PORT} root@{host}" ,
413+ key = cfg. ssh_key
414+ ) ) ;
415+ e2e_tests:: log_line ( & format ! (
416+ " → open http://localhost:{STUB_PORT}"
417+ ) ) ;
418+ e2e_tests:: log_line ( & format ! (
419+ "Tail stub log: ssh -i {key} root@{host} 'tail -f {REMOTE_LOG}'" ,
420+ key = cfg. ssh_key
421+ ) ) ;
422+ e2e_tests:: log_line ( & format ! (
423+ "Kill stub: ssh -i {key} root@{host} pkill -f coolify-stub" ,
400424 key = cfg. ssh_key
401425 ) ) ;
402- e2e_tests:: log_line ( "Then open http://localhost:3000 in your browser." ) ;
403- e2e_tests:: log_line ( & format ! ( "Tail stub log: ssh -i {key} root@{host} 'tail -f {REMOTE_LOG}'" , key = cfg. ssh_key) ) ;
404- e2e_tests:: log_line ( & format ! ( "Kill stub: ssh -i {key} root@{host} pkill -f coolify-stub" , key = cfg. ssh_key) ) ;
405426 e2e_tests:: log_line ( "Destroy VM later: CONFIRM_SWEEP=1 cargo test -p e2e-tests --test install cleanup_leaked_hetzner -- --ignored --nocapture" ) ;
406427 }
407428 // StubGuard kills the stub (unless E2E_KEEP_VMS=1); EphemeralCluster::drop
0 commit comments