Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ SSH server implementation using the russh library for accepting incoming connect
- `config.rs` - `ServerConfig` with builder pattern for server settings
- `handler.rs` - `SshHandler` implementing `russh::server::Handler` trait
- `session.rs` - Session state management (`SessionManager`, `SessionInfo`, `ChannelState`)
- `exec.rs` - Command execution for SSH exec requests
- `auth/` - Authentication provider infrastructure

**Key Components**:
Expand All @@ -213,12 +214,22 @@ SSH server implementation using the russh library for accepting incoming connect
- Connection limits and timeouts
- Authentication method toggles (password, publickey, keyboard-interactive)
- Public key authentication configuration (authorized_keys location)
- Command execution configuration (shell, timeout, allowed/blocked commands)

- **SshHandler**: Per-connection handler for SSH protocol events
- Public key authentication via AuthProvider trait
- Rate limiting for authentication attempts
- Channel operations (open, close, EOF, data)
- PTY, exec, shell, and subsystem request handling
- Command execution with stdout/stderr streaming

- **CommandExecutor**: Executes commands requested by SSH clients
- Shell-based command execution with `-c` flag
- Environment variable configuration (HOME, USER, SHELL, PATH)
- Stdout/stderr streaming to SSH channel
- Command timeout with graceful process termination
- Command allow/block list validation for security
- Exit code propagation to client

- **SessionManager**: Tracks active sessions with configurable capacity
- Session creation and cleanup
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ regex = "1.12.2"
lazy_static = "1.5"
ctrlc = "3.5.1"
signal-hook = "0.4.1"
nix = { version = "0.30", features = ["poll"] }
nix = { version = "0.30", features = ["poll", "process", "signal"] }
atty = "0.2.14"
arrayvec = "0.7.6"
smallvec = "1.15.1"
Expand All @@ -52,10 +52,10 @@ uuid = { version = "1.19.0", features = ["v4"] }
fastrand = "2.3.0"
tokio-util = "0.7.17"
shell-words = "1.1.1"
libc = "0.2"

[target.'cfg(target_os = "macos")'.dependencies]
security-framework = "3.5.1"
libc = "0.2"

[dev-dependencies]
tempfile = "3.23.0"
Expand Down
57 changes: 57 additions & 0 deletions src/server/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use std::time::Duration;
use serde::{Deserialize, Serialize};

use super::auth::{AuthProvider, PublicKeyAuthConfig, PublicKeyVerifier};
use super::exec::ExecConfig;

/// Configuration for the SSH server.
///
Expand Down Expand Up @@ -72,6 +73,10 @@ pub struct ServerConfig {
/// Configuration for public key authentication.
#[serde(default)]
pub publickey_auth: PublicKeyAuthConfigSerde,

/// Configuration for command execution.
#[serde(default)]
pub exec: ExecConfig,
}

/// Serializable configuration for public key authentication.
Expand Down Expand Up @@ -139,6 +144,7 @@ impl Default for ServerConfig {
allow_keyboard_interactive: false,
banner: None,
publickey_auth: PublicKeyAuthConfigSerde::default(),
exec: ExecConfig::default(),
}
}
}
Expand Down Expand Up @@ -282,6 +288,24 @@ impl ServerConfigBuilder {
self
}

/// Set the exec configuration.
pub fn exec(mut self, exec_config: ExecConfig) -> Self {
self.config.exec = exec_config;
self
}

/// Set the command execution timeout in seconds.
pub fn exec_timeout_secs(mut self, secs: u64) -> Self {
self.config.exec.timeout_secs = secs;
self
}

/// Set the default shell for command execution.
pub fn exec_shell(mut self, shell: impl Into<PathBuf>) -> Self {
self.config.exec.default_shell = shell.into();
self
}

/// Build the ServerConfig.
pub fn build(self) -> ServerConfig {
self.config
Expand Down Expand Up @@ -413,4 +437,37 @@ mod tests {
// Provider should be created successfully (verifies no panic)
let _provider = config.create_auth_provider();
}

#[test]
fn test_exec_config_default() {
let config = ServerConfig::default();
assert_eq!(config.exec.default_shell, PathBuf::from("/bin/sh"));
assert_eq!(config.exec.timeout_secs, 3600);
}

#[test]
fn test_builder_exec_config() {
let exec_config = ExecConfig::new()
.with_shell("/bin/bash")
.with_timeout_secs(1800);

let config = ServerConfig::builder().exec(exec_config).build();

assert_eq!(config.exec.default_shell, PathBuf::from("/bin/bash"));
assert_eq!(config.exec.timeout_secs, 1800);
}

#[test]
fn test_builder_exec_timeout() {
let config = ServerConfig::builder().exec_timeout_secs(600).build();

assert_eq!(config.exec.timeout_secs, 600);
}

#[test]
fn test_builder_exec_shell() {
let config = ServerConfig::builder().exec_shell("/bin/zsh").build();

assert_eq!(config.exec.default_shell, PathBuf::from("/bin/zsh"));
}
}
Loading
Loading