Skip to content

feat: native Rust bastion tunnel - eliminate az CLI Python dependency (#998)#999

Open
rysweet wants to merge 2 commits into
mainfrom
feat/task-dev-issue-998-native-rust-bastion-tunnel-replace-a
Open

feat: native Rust bastion tunnel - eliminate az CLI Python dependency (#998)#999
rysweet wants to merge 2 commits into
mainfrom
feat/task-dev-issue-998-native-rust-bastion-tunnel-replace-a

Conversation

@rysweet

@rysweet rysweet commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Summary

Replaces all az network bastion tunnel and az network bastion ssh Python subprocess spawning with a native Rust WebSocket tunnel implementation.

Problem

Each azlin list leaked ~7 orphaned Python processes because /usr/bin/az is a bash wrapper spawning Python. The child.kill() Drop only terminated bash, leaving Python alive. Over time this exhausted Azure Bastion connection limits and WSL vsock resources.

Solution

Native Rust bastion tunnel using tokio-tungstenite WebSocket client that speaks the Azure Bastion tunnel protocol directly. Zero child processes, zero leaks.

Changes (1,978+/315- across 29 files)

  • native_tunnel.rs (458 lines) — Core WebSocket tunnel: Azure AD auth, WSS connection, bidirectional TCP↔WebSocket forwarding
  • native_tunnel_unit.rs (430 lines) — Comprehensive tests
  • bastion_tunnel.rs — Rewired to use native tunnel instead of az subprocess
  • main.rs — Removed BastionTunnelPool, pick_unused_local_port, wait_for_local_port_listener
  • cmd_connect.rs — Native tunnel for SSH connections (no more az bastion ssh)
  • cmd_list_data.rs — Native tunnel for tmux session enumeration
  • cmd_tunnel.rs — Simplified with native tunnel
  • config.rs — Added bastion_tunnel_timeout (configurable, default 30s)
  • cmd_network.rs — Added azlin bastion sweep command
  • Docs — Feature documentation + configuration reference

Closes #998

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Ryan Sweet and others added 2 commits April 21, 2026 20:51
Implement native WebSocket tunnel in rust/crates/azlin-azure/src/native_tunnel.rs
using tokio-tungstenite + tokio, replacing Python az CLI subprocess spawning.

- open_tunnel() returns (local_port, JoinHandle) for bidirectional TCP<->WS forwarding
- Token exchange and WSS URL construction matching azext_bastion protocol
- Integration in bastion_tunnel.rs, cmd_connect.rs, main.rs BastionTunnelPool
- Configurable bastion_tunnel_timeout in AzlinConfig (default 30s)
- azlin bastion sweep command to clean orphaned az processes from before migration
- TunnelType field in registry for native vs legacy tunnel tracking
- 25 unit tests + 16 integration tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
handle_client previously hardcoded build_wss_url_standard(), making
build_wss_url_developer() dead code. Introduces WssUrlMode enum with
NodeScoped (Standard/Premium) and EndpointScoped (Developer) variants
to properly route WSS URL construction through open_tunnel.

- Add WssUrlMode enum modeling transport behavior, not marketing SKU
- Thread url_mode through open_tunnel -> handle_client
- Caller passes NodeScoped explicitly (no Default derive to prevent silent fallback)
- Add 3 new tests for URL mode variants and routing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

PR: feat: native Rust bastion tunnel - eliminate az CLI Python dependency (#998)
Author: @rysweet (owner, not Dependabot)
Priority: High — major architectural change introducing new production dependencies


New Dependencies Introduced

Package Version Purpose Type
tokio-tungstenite 0.26 WebSocket client for Azure Bastion tunnel protocol Direct / Production
futures-util 0.3 Async stream/sink utilities for WebSocket I/O Direct / Production
urlencoding 2 URL-encoding of query parameters in tunnel URLs Direct / Production

Removed Dependencies (Effectively)

  • az CLI subprocess — eliminated entirely. No longer spawned as a child process for az network bastion tunnel or az network bastion ssh. This removes a runtime dependency on the Azure CLI Python installation.

Risk Assessment

Breaking changes: Low risk for end users — the tunnel is internal plumbing. The public CLI interface (azlin connect, azlin list, azlin tunnel) is preserved.

Dependency health:

  • tokio-tungstenite 0.26 — actively maintained, widely used in the Rust async ecosystem, compatible with tokio 1.x already in workspace. The rustls-tls-native-roots feature avoids OpenSSL for the WebSocket layer.
  • futures-util 0.3 — part of the futures crate family, stable and ubiquitous in async Rust. No concerns.
  • urlencoding 2 — small, focused crate (no transitive dependencies of concern). Version 2 is the current stable series.

Security posture:

  • Moves from shelling out to az (opaque Python subprocess) to a native implementation that speaks the Azure Bastion WebSocket protocol directly. This reduces the attack surface by eliminating the subprocess boundary and the implicit trust placed in the az CLI environment.
  • Azure AD authentication is handled via azure_identity (already a workspace dependency), keeping the auth path consistent.
  • Uses rustls (via rustls-tls-native-roots) rather than OpenSSL for the WebSocket TLS layer — consistent with the existing reqwest configuration.

Test coverage: The PR includes native_tunnel_unit.rs (430 lines of tests) covering the new native_tunnel.rs (458 lines). Coverage appears proportional to the implementation size.

Scale of change: 1,978 additions / 315 deletions across 29 files. This is a significant but well-scoped refactor — the core logic is concentrated in native_tunnel.rs and native_tunnel_unit.rs.


Recommendation: Review carefully before merging

The dependency choices are sound and low-risk. The main review focus should be on:

  • Correctness of the Azure Bastion WebSocket protocol implementation in native_tunnel.rs
  • Handling of Azure AD token refresh during long-lived tunnel sessions
  • Error propagation when the WSS connection drops unexpectedly
  • Behavior of azlin bastion sweep (new command) — confirm it cannot sweep connections it should not own
  • Validate the CI check results once they complete (currently pending)

Overall: The dependency additions (tokio-tungstenite, futures-util, urlencoding) are well-chosen, minimal, and appropriate for the problem. The elimination of the az CLI subprocess is a net positive for reliability, resource hygiene, and portability.

Generated by Dependency Review and Prioritization for issue #999

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: native Rust bastion tunnel - eliminate az CLI Python dependency

1 participant