-
Notifications
You must be signed in to change notification settings - Fork 1
Closed
Labels
priority:highHigh priority issueHigh priority issuestatus:doneCompletedCompletedtype:enhancementNew feature or requestNew feature or request
Description
Summary
Create the bssh-server binary with a command-line interface for starting and managing the SSH server.
Parent Epic
- Implement bssh-server with SFTP/SCP support #123 - bssh-server 추가 구현
- Depends on: Implement basic SSH server handler with russh #125, Create server configuration system #130 (SSH handler, configuration)
Implementation Details
1. Binary Entry Point
// src/bin/bssh_server.rs
use clap::{Parser, Subcommand};
use bssh::server::{BsshServer, config::ServerConfig};
#[derive(Parser)]
#[command(name = "bssh-server")]
#[command(about = "Backend.AI SSH Server - A lightweight SSH server for containers")]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
command: Option<Commands>,
/// Configuration file path
#[arg(short, long, global = true)]
config: Option<PathBuf>,
/// Bind address
#[arg(short = 'b', long, global = true)]
bind_address: Option<String>,
/// Port to listen on
#[arg(short, long, global = true)]
port: Option<u16>,
/// Host key file(s)
#[arg(short = 'k', long = "host-key", global = true)]
host_keys: Vec<PathBuf>,
/// Verbosity level (-v, -vv, -vvv)
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
verbose: u8,
/// Run in foreground (don't daemonize)
#[arg(short = 'D', long, global = true)]
foreground: bool,
/// PID file path
#[arg(long, global = true)]
pid_file: Option<PathBuf>,
}
#[derive(Subcommand)]
enum Commands {
/// Start the SSH server (default)
Run,
/// Generate a configuration file template
GenConfig {
/// Output path (stdout if not specified)
#[arg(short, long)]
output: Option<PathBuf>,
},
/// Hash a password for configuration
HashPassword,
/// Check configuration file for errors
CheckConfig,
/// Generate host keys
GenHostKey {
/// Key type (ed25519 or rsa)
#[arg(short = 't', long, default_value = "ed25519")]
key_type: String,
/// Output file path
#[arg(short, long)]
output: PathBuf,
/// RSA key bits (only for rsa type)
#[arg(short = 'b', long, default_value = "4096")]
bits: u32,
},
/// Show version and build information
Version,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
// Initialize logging
bssh::utils::logging::init_logging_console_only(cli.verbose);
match cli.command {
None | Some(Commands::Run) => run_server(cli).await,
Some(Commands::GenConfig { output }) => gen_config(output),
Some(Commands::HashPassword) => hash_password(),
Some(Commands::CheckConfig) => check_config(&cli),
Some(Commands::GenHostKey { key_type, output, bits }) => {
gen_host_key(&key_type, &output, bits)
}
Some(Commands::Version) => show_version(),
}
}
async fn run_server(cli: Cli) -> anyhow::Result<()> {
// Load configuration
let config = bssh::server::config::load_config(
cli.config.as_deref(),
&cli.into(),
)?;
tracing::info!(
"Starting bssh-server on {}:{}",
config.server.bind_address,
config.server.port
);
// Create and run server
let server = BsshServer::new(config)?;
// Setup signal handlers
let shutdown = setup_signal_handlers()?;
// Run server with graceful shutdown
tokio::select! {
result = server.run() => {
result?;
}
_ = shutdown => {
tracing::info!("Received shutdown signal");
}
}
tracing::info!("Server stopped");
Ok(())
}
fn gen_config(output: Option<PathBuf>) -> anyhow::Result<()> {
let template = bssh::server::config::generate_config_template();
if let Some(path) = output {
std::fs::write(&path, &template)?;
println!("Configuration template written to {:?}", path);
} else {
println!("{}", template);
}
Ok(())
}
fn hash_password() -> anyhow::Result<()> {
bssh::server::auth::password::hash_password_cli()
}
fn check_config(cli: &Cli) -> anyhow::Result<()> {
let config = bssh::server::config::load_config(
cli.config.as_deref(),
&cli.into(),
)?;
println!("Configuration is valid");
println!(" Bind: {}:{}", config.server.bind_address, config.server.port);
println!(" Host keys: {:?}", config.server.host_keys);
println!(" Auth methods: {:?}", config.auth.methods);
Ok(())
}
fn gen_host_key(key_type: &str, output: &Path, bits: u32) -> anyhow::Result<()> {
use bssh::keygen;
match key_type {
"ed25519" => keygen::generate_ed25519(output)?,
"rsa" => keygen::generate_rsa(output, bits)?,
_ => anyhow::bail!("Unknown key type: {}. Use 'ed25519' or 'rsa'", key_type),
}
println!("Host key generated: {:?}", output);
Ok(())
}
fn show_version() -> anyhow::Result<()> {
println!("bssh-server {}", env!("CARGO_PKG_VERSION"));
println!("Rust SSH server for containers");
println!();
println!("Build info:");
println!(" rustc: {}", rustc_version_runtime::version());
println!(" target: {}", env!("TARGET"));
Ok(())
}
async fn setup_signal_handlers() -> anyhow::Result<impl std::future::Future<Output = ()>> {
use tokio::signal;
let ctrl_c = async {
signal::ctrl_c().await.expect("Failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("Failed to install SIGTERM handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
Ok(async {
tokio::select! {
_ = ctrl_c => {}
_ = terminate => {}
}
})
}2. Update Cargo.toml
[[bin]]
name = "bssh-server"
path = "src/bin/bssh_server.rs"
[dependencies]
rustc_version_runtime = "0.3" # For version info3. CLI Help Output
bssh-server 1.0.0
Backend.AI SSH Server - A lightweight SSH server for containers
USAGE:
bssh-server [OPTIONS] [COMMAND]
COMMANDS:
run Start the SSH server (default)
gen-config Generate a configuration file template
hash-password Hash a password for configuration
check-config Check configuration file for errors
gen-host-key Generate host keys
version Show version and build information
help Print this message or the help of the given subcommand(s)
OPTIONS:
-c, --config <FILE> Configuration file path
-b, --bind-address <ADDR> Bind address [default: 0.0.0.0]
-p, --port <PORT> Port to listen on [default: 22]
-k, --host-key <FILE> Host key file(s)
-v, --verbose... Verbosity level
-D, --foreground Run in foreground
--pid-file <FILE> PID file path
-h, --help Print help
-V, --version Print version
Files to Create/Modify
| File | Action |
|---|---|
src/bin/bssh_server.rs |
Create - Server binary |
Cargo.toml |
Modify - Add binary target |
Testing Requirements
- CLI help output verification
- Config generation command
- Config check command
- Signal handling (SIGTERM, SIGINT)
# Test CLI
bssh-server --help
bssh-server gen-config > test-config.yaml
bssh-server check-config -c test-config.yaml
bssh-server hash-password
bssh-server gen-host-key -t ed25519 -o /tmp/test_host_keyAcceptance Criteria
- Binary compiles and runs
- Clap-based CLI with subcommands
- Configuration file loading
- CLI argument overrides
- Signal handling for graceful shutdown
- gen-config command works
- hash-password command works
- check-config command works
- gen-host-key command works
- Proper exit codes
- Tests passing
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
priority:highHigh priority issueHigh priority issuestatus:doneCompletedCompletedtype:enhancementNew feature or requestNew feature or request