Implement SSH Banner/MOTD support
- Add banner and banner_file fields to SshSecurityConfig - Enterprise default: 'MarkBaseSSH - Secure File Transfer Server' - Support banner_file for reading from /etc/motd - Send SSH_MSG_USERAUTH_BANNER before USERAUTH_SUCCESS - Pass security_config to perform_ssh_auth function All 229 tests pass.
This commit is contained in:
@@ -179,7 +179,7 @@ fn handle_connection_complete(
|
||||
)
|
||||
};
|
||||
let mut auth_handler = AuthHandler::new(provider);
|
||||
let auth_user = perform_ssh_auth(&mut stream, &mut auth_handler, &mut encryption_ctx)?;
|
||||
let auth_user = perform_ssh_auth(&mut stream, &mut auth_handler, &mut encryption_ctx, security_config.clone())?;
|
||||
info!("SSH authentication succeeded: user={}", auth_user.username);
|
||||
|
||||
let upload_hook = if upload_hook_config.enabled {
|
||||
@@ -335,6 +335,7 @@ fn perform_ssh_auth(
|
||||
stream: &mut TcpStream,
|
||||
auth_handler: &mut AuthHandler,
|
||||
encryption_ctx: &mut EncryptionContext,
|
||||
security_config: Arc<Mutex<SshSecurityConfig>>,
|
||||
) -> Result<AuthUser> {
|
||||
info!("Starting SSH authentication");
|
||||
info!(
|
||||
@@ -395,6 +396,29 @@ fn perform_ssh_auth(
|
||||
|
||||
match auth_handler.handle_userauth_request(&auth_request, &session_id)? {
|
||||
AuthResult::Success => {
|
||||
// Send banner if configured (SSH_MSG_USERAUTH_BANNER)
|
||||
let security = security_config.lock().unwrap();
|
||||
let banner = if let Some(file) = &security.banner_file {
|
||||
std::fs::read_to_string(file).ok()
|
||||
} else {
|
||||
security.banner.clone()
|
||||
};
|
||||
drop(security);
|
||||
|
||||
if let Some(banner_text) = banner {
|
||||
let mut banner_payload = Vec::new();
|
||||
banner_payload.write_u8(PacketType::SSH_MSG_USERAUTH_BANNER as u8)?;
|
||||
banner_payload.write_u32::<BigEndian>(banner_text.len() as u32)?;
|
||||
banner_payload.write_all(banner_text.as_bytes())?;
|
||||
// Language tag (empty SSH string)
|
||||
banner_payload.write_u32::<BigEndian>(0)?;
|
||||
|
||||
let encrypted_banner =
|
||||
EncryptedPacket::new(&banner_payload, encryption_ctx, true)?;
|
||||
encrypted_banner.write(stream)?;
|
||||
info!("Sent SSH_MSG_USERAUTH_BANNER");
|
||||
}
|
||||
|
||||
let success_payload = vec![PacketType::SSH_MSG_USERAUTH_SUCCESS as u8];
|
||||
let encrypted_success =
|
||||
EncryptedPacket::new(&success_payload, encryption_ctx, true)?;
|
||||
|
||||
@@ -41,6 +41,14 @@ pub struct SshSecurityConfig {
|
||||
/// 最大心跳失败次数,超过则断开连接
|
||||
pub keep_alive_max_count: u32,
|
||||
|
||||
/// Banner/MOTD内容
|
||||
/// 登录后显示的欢迎信息(参考OpenSSH Banner)
|
||||
pub banner: Option<String>,
|
||||
|
||||
/// Banner文件路径
|
||||
/// 从文件读取banner内容(如/etc/motd)
|
||||
pub banner_file: Option<String>,
|
||||
|
||||
/// 活动会话数(运行时状态)
|
||||
pub active_sessions: u32,
|
||||
}
|
||||
@@ -57,6 +65,8 @@ impl SshSecurityConfig {
|
||||
connect_timeout: 30, // 30秒超时
|
||||
keep_alive_interval: 15, // 15秒心跳间隔
|
||||
keep_alive_max_count: 3, // 3次失败后断开
|
||||
banner: Some("MarkBaseSSH - Secure File Transfer Server\n".to_string()),
|
||||
banner_file: None, // 不使用文件
|
||||
active_sessions: 0, // 运行时状态
|
||||
}
|
||||
}
|
||||
@@ -71,6 +81,8 @@ impl SshSecurityConfig {
|
||||
connect_timeout: 60, // 开发:更长超时
|
||||
keep_alive_interval: 30, // 开发:更宽松心跳
|
||||
keep_alive_max_count: 5, // 开发:更多失败容忍
|
||||
banner: None, // 开发:不显示banner
|
||||
banner_file: None,
|
||||
active_sessions: 0,
|
||||
}
|
||||
}
|
||||
@@ -126,6 +138,14 @@ impl SshSecurityConfig {
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|v| v as u32)
|
||||
.unwrap_or(3),
|
||||
banner: security
|
||||
.get("banner")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from),
|
||||
banner_file: security
|
||||
.get("banner_file")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from),
|
||||
active_sessions: 0,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user