SSH 客户端 sgcIndy Delphi 组件

· 组件

远程服务器管理、自动化部署、配置管理和基础设施监控——所有这些都依赖于安全 Shell 访问。无论您需要在远程主机上执行单个命令、打开交互式终端会话还是设置端口转发隧道,SSH 都是实现这一切的协议。

sgcIndy 包含 TIdSSHClient——这是一个原生 Delphi SSH 客户端组件,实现了 SSH-2 协议,全面支持命令执行、交互式 Shell、伪终端分配、端口转发、保活以及现代加密算法。无需外部 SSH 可执行文件,无需 DLL 封装——纯 Delphi 组件架构,配备事件驱动 API。

本文将介绍主要功能,并为最常见的 SSH 使用场景提供 Delphi 代码示例。

主要功能

命令执行
执行远程命令并捕获标准输出、标准错误和退出代码。提供单行便捷方法或完整的基于通道的控制。
交互式 Shell
打开支持伪终端的交互式 Shell 会话。发送命令、异步接收输出并处理终端大小调整。
端口转发
建立直接 TCP/IP 隧道和反向转发。通过加密的 SSH 隧道访问远程服务。
现代密码学
支持 Curve25519、ECDH、AES-GCM、Ed25519 密钥。可配置算法协商,开箱即用的安全默认设置。
多种认证方式
支持密码、公钥(RSA、ECDSA、Ed25519)和键盘交互式认证。通过事件回调进行主机密钥验证。
多通道
每个连接最多支持 10 个并发通道。在单个 SSH 会话上同时运行多个命令、Shell 或隧道。

快速入门 — 执行远程命令

最简单的使用场景:连接、运行命令、获取输出并断开连接——仅需几行代码。

var
  oSSH: TIdSSHClient;
  vOutput: string;
begin
  oSSH := TIdSSHClient.Create(nil);
  Try
    oSSH.Host := 'server.example.com';
    oSSH.Port := 22;
    oSSH.Authentication.Username := 'admin';
    oSSH.Authentication.Password := 'secret';
    oSSH.Connect;
    // Execute a command and capture the output
    vOutput := oSSH.Execute('df -h');
    WriteLn(vOutput);
    oSSH.Disconnect;
  Finally
    oSSH.Free;
  End;
end;

一行搞定。Execute 方法打开通道、运行命令、等待结果并以字符串形式返回输出——非常适合脚本自动化。

身份验证

支持三种认证方式。三种方式均默认启用,客户端会自动与服务器协商。

密码认证

oSSH.Authentication.Username := 'admin';
oSSH.Authentication.Password := 'secret';

公钥认证

oSSH.Authentication.Username := 'deploy';
oSSH.Authentication.PrivateKeyFile := 'C:\keys\id_ed25519';
oSSH.Authentication.PublicKeyFile := 'C:\keys\id_ed25519.pub';
oSSH.Authentication.Passphrase := 'keypassphrase';

键盘交互式认证

通过 OnSSHKeyboardInteractive 事件处理多步认证提示(MFA、OTP、安全问题)。

oSSH.OnSSHKeyboardInteractive := OnKeyboardInteractive;
procedure TForm1.OnKeyboardInteractive(Sender: TObject;
  const aName, aInstruction: string;
  aPrompts: TStrings; aEchos: TList; aResponses: TStrings);
begin
  // Respond to each prompt (e.g., "Password:", "OTP:")
  if aPrompts.Count > 0 then
    aResponses.Add('mypassword');
end;

主机密钥验证

oSSH.OnSSHHostKey := OnHostKey;
procedure TForm1.OnHostKey(Sender: TObject;
  const aHostKeyType, aFingerprint: string;
  var aAction: TIdSSHHostKeyVerification);
begin
  // Accept or reject based on known fingerprint
  aAction := sshHostKeyAccept;
end;

命令执行

运行远程命令有两种方式:简单场景使用便捷的 Execute 方法,或使用基于通道的 API 对输入、输出和退出状态进行完全控制。

简单方式:Execute 方法

// Execute and get output (30-second timeout by default)
vOutput := oSSH.Execute('ls -la /var/log');
// Custom timeout (10 seconds)
vOutput := oSSH.Execute('cat /etc/hostname', 10000);

高级方式:基于通道的执行

用于异步执行,可分别处理标准输出/标准错误并跟踪退出状态。

// Open a channel and execute a command
var
  vChannelId: Cardinal;
begin
  vChannelId := oSSH.OpenChannel;
  oSSH.RequestExec(vChannelId, 'tar czf /tmp/backup.tar.gz /data');
  // Output arrives via OnSSHChannelData event
  // Exit status arrives via OnSSHChannelExitStatus event
end;
// Handle stdout
procedure TForm1.OnChannelData(Sender: TObject;
  aChannelId: Cardinal; const aData: TIdBytes);
begin
  Memo1.Lines.Add(BytesToString(aData));
end;
// Handle stderr
procedure TForm1.OnChannelExtendedData(Sender: TObject;
  aChannelId: Cardinal; aDataType: Cardinal; const aData: TIdBytes);
begin
  MemoErrors.Lines.Add(BytesToString(aData));
end;
// Handle exit status
procedure TForm1.OnExitStatus(Sender: TObject;
  aChannelId: Cardinal; aExitStatus: Integer);
begin
  WriteLn('Command exited with code: ' + IntToStr(aExitStatus));
end;

交互式 Shell 会话

打开伪终端并与远程 Shell 交互——非常适合构建 SSH 终端模拟器或自动化交互式 CLI 工作流。

// Open channel, request PTY, then request shell
var
  vChannelId: Cardinal;
begin
  vChannelId := oSSH.OpenChannel;
  // Request a pseudo-terminal (xterm, 80x24)
  oSSH.RequestPTY(vChannelId, 'xterm', 80, 24);
  // Start the shell
  oSSH.RequestShell(vChannelId);
  // Send commands to the shell
  oSSH.SendChannelData(vChannelId, 'cd /var/log' + #13#10);
  oSSH.SendChannelData(vChannelId, 'tail -f syslog' + #13#10);
end;

终端大小调整与信号

// Notify the server of terminal resize
oSSH.SendWindowChange(vChannelId, 120, 40, 0, 0);
// Send Ctrl+C (interrupt signal)
oSSH.SendSignal(vChannelId, 'INT');
// Set an environment variable before running commands
oSSH.SetEnvironmentVariable(vChannelId, 'LANG', 'en_US.UTF-8');
// Signal end of input
oSSH.SendEOF(vChannelId);

端口转发(SSH 隧道)

创建加密隧道,像访问本地服务一样访问远程服务。适用于安全访问防火墙后面的数据库、管理面板或内部 API。

直接 TCP/IP 隧道(本地转发)

// Tunnel to a remote database through SSH
var
  vTunnelId: Cardinal;
begin
  vTunnelId := oSSH.OpenDirectTCPIP(
    'db-internal.example.com',  // Remote host
    5432,                        // Remote port (PostgreSQL)
    '127.0.0.1',                 // Originator IP
    0);                           // Originator port
  // Send/receive data through the tunnel
  oSSH.SendChannelData(vTunnelId, vDatabaseQuery);
end;

反向转发(远程转发)

// Ask the server to forward a remote port to us
oSSH.RequestForwarding('0.0.0.0', 8080);
// Cancel the forwarding
oSSH.CancelForwarding('0.0.0.0', 8080);

保活与连接选项

通过内置保活机制防止空闲连接被防火墙或负载均衡器断开。

// Send keep-alive every 30 seconds, disconnect after 3 failures
oSSH.KeepAlive.Enabled := True;
oSSH.KeepAlive.Interval := 30;
oSSH.KeepAlive.MaxCount := 3;
// Connection options
oSSH.SSHOptions.ConnectTimeout := 10000;  // 10 seconds
oSSH.SSHOptions.ReadTimeout := 30000;     // 30 seconds
oSSH.SSHOptions.MaxChannels := 10;       // Concurrent channels

加密算法配置

默认配置安全且符合现代标准。当合规策略或旧版服务器兼容性有要求时,可自定义算法协商。

类别 支持的算法
密钥交换 Curve25519, ECDH (P-256, P-384, P-521), DH Group14/16
主机密钥 Ed25519, ECDSA (P-256, P-384, P-521), RSA (SHA2-256, SHA2-512)
对称加密 AES-256/192/128-CTR, AES-256/128-GCM
消息认证码 HMAC-SHA2-256, HMAC-SHA2-512, HMAC-SHA1
// Customize algorithm preferences
oSSH.Algorithms.KexAlgorithms := 'curve25519-sha256';
oSSH.Algorithms.Ciphers := 'aes256-gcm@openssh.com'
		,aes256-ctr';
oSSH.Algorithms.HostKeyAlgorithms := 'ssh-ed25519,rsa-sha2-256';
oSSH.Algorithms.MACs := 'hmac-sha2-256';
// Force re-keying to refresh encryption
oSSH.Rekey;

事件参考

该组件为 SSH 生命周期的每个阶段提供精细的事件回调。

事件 触发时机
OnSSHConnectSSH 连接已建立
OnSSHDisconnectSSH 连接关闭(含原因和代码)
OnSSHError发生 SSH 错误
OnSSHAuthSuccess / OnSSHAuthFailure身份验证成功或失败
OnSSHHostKey需要验证主机密钥(接受/拒绝)
OnSSHChannelData通道上接收到数据(标准输出)
OnSSHChannelExtendedData通道上接收到扩展数据(标准错误)
OnSSHChannelExitStatus收到远程命令退出代码
OnSSHChannelExitSignal远程进程被信号终止(含信号名称)
OnSSHKeyboardInteractive服务器请求键盘交互式响应
OnSSHAuthBanner服务器发送认证横幅消息

完整示例:自动化部署脚本

一个完整配置的 SSH 客户端,使用密钥认证连接,运行部署命令并捕获退出状态。

uses
  IdSSHClient, IdSSHClasses;
var
  oSSH: TIdSSHClient;
  vOutput: string;
begin
  oSSH := TIdSSHClient.Create(nil);
  Try
    // Connection
    oSSH.Host := 'production.example.com';
    oSSH.Port := 22;
    // Key-based authentication
    oSSH.Authentication.Username := 'deploy';
    oSSH.Authentication.PrivateKeyFile := 'C:\keys\deploy_ed25519';
    // Keep connection alive through firewalls
    oSSH.KeepAlive.Enabled := True;
    oSSH.KeepAlive.Interval := 30;
    // Events
    oSSH.OnSSHHostKey := OnHostKey;
    oSSH.OnSSHError := OnError;
    // Connect
    oSSH.Connect;
    // Run deployment commands
    vOutput := oSSH.Execute('cd /opt/app && git pull origin main');
    WriteLn(vOutput);
    vOutput := oSSH.Execute('systemctl restart myapp');
    WriteLn(vOutput);
    vOutput := oSSH.Execute('systemctl status myapp');
    WriteLn(vOutput);
    // Disconnect
    oSSH.Disconnect;
  Finally
    oSSH.Free;
  End;
end;