Klient SSH — komponent Delphi sgcIndy

· Komponenty

Administracja zdalna serwerami, automatyczne wdrożenia, zarządzanie konfiguracją i monitorowanie infrastruktury — wszystkie te zadania opierają się na bezpiecznym dostępie do powłoki. Czy potrzebujesz wykonać pojedyncze polecenie na zdalnym hoście, otworzyć interaktywną sesję terminala czy skonfigurować tunel przekazywania portów — SSH to protokół, który to umożliwia.

Pakiet sgcIndy zawiera TIdSSHClient — natywny komponent klienta SSH dla Delphi implementujący protokół SSH-2 z pełną obsługą wykonywania poleceń, interaktywnych powłok, alokacji pseudoterminala, przekazywania portów, mechanizmu keep-alive oraz nowoczesnych algorytmów kryptograficznych. Bez zewnętrznych plików wykonywalnych SSH, bez wrapperów DLL — czysta architektura komponentów Delphi z API opartym na zdarzeniach.

Ten artykuł omawia kluczowe funkcje i zawiera przykłady kodu Delphi dla najczęstszych zastosowań SSH.

Kluczowe funkcje

Wykonywanie poleceń
Wykonuj zdalne polecenia i przechwytuj stdout, stderr oraz kody wyjścia. Wygodna metoda jednolinijkowa lub pełna kontrola oparta na kanałach.
Interaktywna powłoka
Otwieraj interaktywne sesje powłoki z obsługą pseudoterminala. Wysyłaj polecenia, asynchronicznie odbieraj dane wyjściowe i obsługuj zmianę rozmiaru terminala.
Przekazywanie portów
Konfiguruj bezpośrednie tunele TCP/IP i odwrócone przekazywanie. Uzyskuj dostęp do zdalnych usług przez szyfrowane tunele SSH.
Nowoczesna kryptografia
Klucze Curve25519, ECDH, AES-GCM, Ed25519. Konfigurowalna negocjacja algorytmów z bezpiecznymi wartościami domyślnymi.
Wiele metod uwierzytelniania
Hasło, klucz publiczny (RSA, ECDSA, Ed25519) i uwierzytelnianie keyboard-interactive. Weryfikacja klucza hosta przez callback zdarzenia.
Wiele kanałów
Do 10 współbieżnych kanałów na połączenie. Uruchamiaj wiele poleceń, powłok lub tuneli jednocześnie w jednej sesji SSH.

Szybki start — wykonanie zdalnego polecenia

Najprostsze zastosowanie: połącz się, uruchom polecenie, pobierz dane wyjściowe i rozłącz się — wszystko w kilku linijkach.

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;

Jednolinijkowo. Metoda Execute otwiera kanał, uruchamia polecenie, czeka na wynik i zwraca dane wyjściowe jako ciąg znaków — idealne do zautomatyzowanych skryptów.

Uwierzytelnianie

Obsługiwane są trzy metody uwierzytelniania. Wszystkie trzy są domyślnie włączone, a klient negocjuje automatycznie z serwerem.

Hasło

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

Klucz publiczny

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

Keyboard-Interactive

Obsługuj wieloetapowe monity uwierzytelniania (MFA, OTP, pytania zabezpieczające) przez zdarzenie OnSSHKeyboardInteractive.

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;

Weryfikacja klucza hosta

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;

Wykonywanie poleceń

Dwa podejścia do uruchamiania zdalnych poleceń: wygodna metoda Execute dla prostych przypadków lub API oparte na kanałach zapewniające pełną kontrolę nad wejściem, wyjściem i statusem zakończenia.

Prosto: metoda 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);

Zaawansowane: wykonanie oparte na kanałach

Do asynchronicznego wykonywania z oddzielną obsługą stdout/stderr i śledzeniem statusu zakończenia.

// 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;

Interaktywne sesje powłoki

Otwórz pseudoterminal i wejdź w interakcję ze zdalną powłoką — idealne do budowania emulatorów terminala SSH lub automatyzowania interaktywnych przepływów 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;

Zmiana rozmiaru terminala i sygnały

// 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);

Przekazywanie portów (tunele SSH)

Twórz szyfrowane tunele umożliwiające dostęp do zdalnych usług tak, jakby były lokalne. Przydatne do bezpiecznego dostępu do baz danych, paneli administracyjnych lub wewnętrznych API za zaporami sieciowymi.

Bezpośrednie tunelowanie TCP/IP (przekazywanie lokalne)

// 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;

Odwrócone przekazywanie (przekazywanie zdalne)

// 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);

Keep-Alive i opcje połączenia

Zapobiegaj przerywaniu bezczynnych połączeń przez zapory sieciowe lub load balancery dzięki wbudowanemu mechanizmowi keep-alive.

// 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

Konfiguracja algorytmów kryptograficznych

Wartości domyślne są bezpieczne i nowoczesne. Dostosuj negocjację algorytmów, gdy wymagają tego polityki zgodności lub kompatybilność z starszymi serwerami.

Kategoria Obsługiwane algorytmy
Wymiana kluczy Curve25519, ECDH (P-256, P-384, P-521), DH Group14/16
Klucze hosta Ed25519, ECDSA (P-256, P-384, P-521), RSA (SHA2-256, SHA2-512)
Szyfry AES-256/192/128-CTR, AES-256/128-GCM
MAC-e 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;

Informacje o zdarzeniach

Komponent udostępnia szczegółowe callbacki zdarzeń dla każdego etapu cyklu życia SSH.

Zdarzenie Wywoływane gdy
OnSSHConnectpołączenie SSH zostało nawiązane
OnSSHDisconnectpołączenie SSH zostaje zamknięte (z powodem i kodem)
OnSSHErrorwystąpił błąd SSH
OnSSHAuthSuccess / OnSSHAuthFailureuwierzytelnianie zakończyło się sukcesem lub niepowodzeniem
OnSSHHostKeyklucz hosta wymaga weryfikacji (akceptuj/odrzuć)
OnSSHChannelDataodebrano dane (stdout) na kanale
OnSSHChannelExtendedDataodebrano rozszerzone dane (stderr) na kanale
OnSSHChannelExitStatusodebrano kod wyjścia zdalnego polecenia
OnSSHChannelExitSignalzdalny proces zakończony sygnałem (z nazwą sygnału)
OnSSHKeyboardInteractiveserwer żąda odpowiedzi keyboard-interactive
OnSSHAuthBannerserwer wysyła wiadomość banera uwierzytelniania

Pełny przykład: skrypt automatycznego wdrożenia

W pełni skonfigurowany klient SSH, który łączy się z uwierzytelnianiem kluczem, uruchamia polecenia wdrożeniowe i przechwytuje status wyjścia.

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;