Skip to content

feat: Add jump_host field support in config.yaml #115

@inureyes

Description

@inureyes

Problem / Background

Currently bssh supports jump hosts through:

  1. CLI -J/--jump-host option
  2. SSH config file ~/.ssh/config with ProxyJump directive

However, there are several limitations:

Issue 1: No config.yaml support for jump hosts

bssh's own config.yaml does NOT support jump host configuration. Users cannot specify jump hosts per-cluster or per-node in the configuration file, which limits the usefulness of the configuration system for networks that require bastion hosts.

Issue 2: Commands missing -J support

Several commands do not pass through the CLI -J option:

Command -J Support Location Issue
exec ✅ Works - -
interactive ✅ Works - -
ping ❌ Missing src/commands/ping.rs:57 Hardcoded .with_jump_hosts(None)
upload ❌ Missing src/commands/upload.rs FileTransferParams lacks jump_hosts field
download ❌ Missing src/commands/download.rs FileTransferParams lacks jump_hosts field

Proposed Solution

Part A: Add jump_host field to config.yaml

Add jump_host field support at three levels:

1. Global Default Level (Defaults struct)

Add jump_host: Option<String> to apply to all clusters by default.

2. Cluster Level (ClusterDefaults struct)

Add jump_host: Option<String> to override global default for specific clusters.

3. Node Level (NodeConfig::Detailed variant)

Add jump_host: Option<String> to override cluster default for specific nodes.

Example config.yaml format:

defaults:
  user: ubuntu
  port: 22
  jump_host: bastion.example.com  # Global default jump host

clusters:
  production:
    nodes:
      - host: prod1.internal
      - host: prod2.internal
    jump_host: prod-bastion.example.com  # Cluster-level override
    
  staging:
    nodes:
      - host: staging1.internal
        jump_host: staging-bastion:2222  # Node-level override
      - host: staging2.internal  # Uses cluster jump_host
    jump_host: staging-bastion.example.com
    
  development:
    nodes:
      - host: dev1.example.com  # No jump host needed (direct access)
        jump_host: ""  # Explicitly disable jump host for this node
      - host: dev2.internal  # Uses global default jump_host

Priority Order (highest to lowest):

  1. Node-level jump_host (in NodeConfig::Detailed)
  2. Cluster-level jump_host (in ClusterDefaults)
  3. Global default jump_host (in Defaults)
  4. CLI -J option (fallback when config doesn't specify)

Empty string ("") should explicitly disable jump host inheritance.

Part B: Enable -J support for ping/upload/download commands

  1. Add jump_hosts: Option<String> field to FileTransferParams struct
  2. Pass cli.jump_hosts through dispatcher for upload/download commands
  3. Replace hardcoded None in ping command with actual jump_hosts value

Acceptance Criteria

Config.yaml Support

  • Add jump_host: Option<String> field to Defaults struct in src/config/types.rs
  • Add jump_host: Option<String> field to ClusterDefaults struct in src/config/types.rs
  • Add jump_host: Option<String> field to NodeConfig::Detailed variant in src/config/types.rs
  • Implement get_jump_host() resolution method in src/config/resolver.rs following priority order
  • Update src/app/dispatcher.rs to integrate config-based jump_hosts with CLI override logic

Command Support

  • Add jump_hosts: Option<String> to FileTransferParams in src/commands/upload.rs
  • Update src/commands/ping.rs:57 - Replace .with_jump_hosts(None) with resolved jump_hosts
  • Update src/app/dispatcher.rs ping handler to pass jump_hosts
  • Update src/app/dispatcher.rs upload handler to pass jump_hosts
  • Update src/app/dispatcher.rs download handler to pass jump_hosts

Testing & Documentation

  • Add unit tests for jump_host resolution with various priority combinations
  • Update example configuration in documentation
  • Ensure backward compatibility (existing configs without jump_host continue to work)

Technical Considerations

Implementation Locations

  • src/config/types.rs - Add fields to Defaults, ClusterDefaults, and NodeConfig::Detailed
  • src/config/resolver.rs - Add get_jump_host() method
  • src/commands/ping.rs:57 - Replace hardcoded None
  • src/commands/upload.rs:25-33 - Add field to FileTransferParams
  • src/app/dispatcher.rs:82-94 - Pass jump_hosts to ping_nodes
  • src/app/dispatcher.rs:109-118 - Pass jump_hosts to upload handler
  • src/app/dispatcher.rs:133-142 - Pass jump_hosts to download handler

Resolution Logic Example

/// Get jump host for a specific node in a cluster.
pub fn get_jump_host(&self, cluster_name: &str, node_index: usize) -> Option<String> {
    if let Some(cluster) = self.get_cluster(cluster_name) {
        // Check node-level first
        if let Some(NodeConfig::Detailed { jump_host: Some(jh), .. }) = cluster.nodes.get(node_index) {
            if jh.is_empty() {
                return None;  // Explicitly disabled
            }
            return Some(jh.clone());
        }
        // Check cluster-level
        if let Some(jh) = &cluster.defaults.jump_host {
            if \!jh.is_empty() {
                return Some(jh.clone());
            }
        }
    }
    // Fall back to global default
    self.defaults.jump_host.clone().filter(|s| \!s.is_empty())
}

Additional Context

Related code:

  • src/cli/bssh.rs:119 - pub jump_hosts: Option<String>
  • src/jump/ module - Jump host parsing and chain management already implemented
  • src/executor/parallel.rs:156-157 - with_jump_hosts() builder method exists

This feature would enable full declarative configuration of complex network topologies with bastion hosts, matching the capabilities of traditional SSH config files while providing the additional benefits of bssh's cluster management.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions