MCPcopy Create free account
hub / github.com/NVIDIA/OpenShell / channel_open_direct_tcpip

Method channel_open_direct_tcpip

crates/openshell-supervisor-process/src/ssh.rs:291–362  ·  view source on GitHub ↗
(
        &mut self,
        channel: russh::Channel<russh::server::Msg>,
        host_to_connect: &str,
        port_to_connect: u32,
        _originator_address: &str,
        _originator_port: u32,

Source from the content-addressed store, hash-verified

289 }
290
291 async fn channel_open_direct_tcpip(
292 &mut self,
293 channel: russh::Channel<russh::server::Msg>,
294 host_to_connect: &str,
295 port_to_connect: u32,
296 _originator_address: &str,
297 _originator_port: u32,
298 _session: &mut Session,
299 ) -> Result<bool, Self::Error> {
300 // Validate port range before truncating u32 -> u16. The SSH protocol
301 // uses u32 for ports, but valid TCP ports are 0-65535. Without this
302 // check, port 65537 truncates to port 1 (privileged).
303 if port_to_connect > u32::from(u16::MAX) {
304 ocsf_emit!(SshActivityBuilder::new(openshell_ocsf::ctx::ctx())
305 .activity(ActivityId::Refuse)
306 .action(ActionId::Denied)
307 .disposition(DispositionId::Blocked)
308 .severity(SeverityId::Medium)
309 .message(format!(
310 "direct-tcpip rejected: port {port_to_connect} exceeds valid TCP range for host {host_to_connect}"
311 ))
312 .build());
313 return Ok(false);
314 }
315
316 // Only allow forwarding to loopback destinations to prevent the
317 // sandbox SSH server from being used as a generic proxy.
318 if !is_loopback_host(host_to_connect) {
319 ocsf_emit!(SshActivityBuilder::new(openshell_ocsf::ctx::ctx())
320 .activity(ActivityId::Refuse)
321 .action(ActionId::Denied)
322 .disposition(DispositionId::Blocked)
323 .severity(SeverityId::Medium)
324 .message(format!(
325 "direct-tcpip rejected: non-loopback destination {host_to_connect}:{port_to_connect}"
326 ))
327 .build());
328 return Ok(false);
329 }
330
331 let host = host_to_connect.to_string();
332 // SSH protocol port is bounded by u32 but only u16 is meaningful;
333 // saturate as a guard for malformed clients.
334 let port = u16::try_from(port_to_connect).unwrap_or(u16::MAX);
335 let netns_fd = self.netns_fd;
336
337 tokio::spawn(async move {
338 let addr = format!("{host}:{port}");
339 let tcp = match connect_in_netns(&addr, netns_fd).await {
340 Ok(stream) => stream,
341 Err(err) => {
342 ocsf_emit!(
343 SshActivityBuilder::new(openshell_ocsf::ctx::ctx())
344 .activity(ActivityId::Fail)
345 .severity(SeverityId::Low)
346 .status(StatusId::Failure)
347 .message(format!("direct-tcpip: failed to connect to {addr}: {err}"))
348 .build()

Callers

nothing calls this directly

Calls 4

connect_in_netnsFunction · 0.85
is_loopback_hostFunction · 0.70
spawnFunction · 0.70
closeMethod · 0.45

Tested by

no test coverage detected