SSH 클라이언트 sgcIndy Delphi 컴포넌트

· 컴포넌트

원격 서버 관리, 자동화된 배포, 설정 관리, 인프라 모니터링 — 이 모든 것은 보안 셸 접근에 의존해요. 원격 호스트에서 단일 명령을 실행하거나 대화형 터미널 세션을 열거나 포트 포워딩 터널을 설정해야 할 때, SSH는 이를 가능하게 해주는 프로토콜이에요.

sgcIndy 패키지에는 TIdSSHClient가 포함돼 있어요 — 명령 실행, 대화형 셸, 의사 터미널 할당, 포트 포워딩, Keep-Alive 및 최신 암호화 알고리즘을 완벽히 지원하는 SSH-2 프로토콜을 구현한 네이티브 Delphi SSH 클라이언트 컴포넌트예요. 외부 SSH 실행 파일이나 DLL 래퍼 없이 — 이벤트 기반 API를 제공하는 순수 Delphi 컴포넌트 아키텍처예요.

이 글에서는 주요 기능을 살펴보고 가장 일반적인 SSH 사용 사례에 대한 Delphi 코드 예제를 제공해요.

주요 기능

명령 실행
원격 명령을 실행하고 stdout, stderr, 종료 코드를 캡처해요. 한 줄짜리 편의 메서드 또는 전체 채널 기반 제어를 사용할 수 있어요.
대화형 셸
의사 터미널을 지원하는 대화형 셸 세션을 열 수 있어요. 명령을 보내고 비동기로 출력을 수신하며 터미널 크기 변경도 처리해요.
포트 포워딩
직접 TCP/IP 터널과 역방향 포워딩을 설정해요. 암호화된 SSH 터널을 통해 원격 서비스에 접근할 수 있어요.
최신 암호화
Curve25519, ECDH, AES-GCM, Ed25519 키를 지원해요. 별도 설정 없이 안전한 기본값으로 알고리즘 협상을 구성할 수 있어요.
다양한 인증 방식
비밀번호, 공개 키(RSA, ECDSA, Ed25519), 키보드 대화형 인증을 지원해요. 이벤트 콜백을 통한 호스트 키 검증이 가능해요.
멀티 채널
연결당 최대 10개의 동시 채널을 지원해요. 단일 SSH 세션을 통해 여러 명령, 셸 또는 터널을 동시에 실행할 수 있어요.

빠른 시작 — 원격 명령 실행

가장 간단한 사용 사례예요. 연결하고, 명령을 실행하고, 출력을 받고, 연결을 해제하는 모든 작업을 몇 줄로 처리해요.

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

고급: 채널 기반 실행

stdout/stderr를 별도로 처리하고 종료 상태를 추적하는 비동기 실행에 사용해요.

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

대화형 셸 세션

의사 터미널을 열고 원격 셸과 상호 작용해요 — 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);

Keep-Alive 및 연결 옵션

내장 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

암호화 알고리즘 설정

기본값은 안전하고 최신이에요. 컴플라이언스 정책이나 레거시 서버 호환성이 필요한 경우 알고리즘 협상을 사용자 정의할 수 있어요.

카테고리 지원 알고리즘
키 교환 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
MAC 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 연결이 종료될 때 (이유 및 코드 포함)
OnSSHErrorSSH 오류가 발생했을 때
OnSSHAuthSuccess / OnSSHAuthFailure인증이 성공하거나 실패했을 때
OnSSHHostKey호스트 키 검증이 필요할 때 (수락/거부)
OnSSHChannelData채널에서 데이터(stdout)를 수신했을 때
OnSSHChannelExtendedData채널에서 확장 데이터(stderr)를 수신했을 때
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;