Post

05 JSCH

05 JSCH

1. Memo(20215.12.02.)

  • 스프링 환경에서 RestAPI 사용중,
  • 배치가 스케줄러로 1회 호출하여 순차적으로 1작업 루틴(연결-작업-제거)씩 하는 상황이었으나,
  • 외부 요청에서 하는 작업으로 변경하면서 동시에 실행하는 상황이 생김.
  • 따라서 기존에 Component로 만들어서 주입하던 방식을 사용하면, 스레드에 불안전함!
  • 커넥션만 별도로 분리하고, 필요한 작업만 서비스단에서 처리할것!

2. JSCH

1. JSCH

  • 자바에서 SSH 기반 기능을 구현하기 위한 라이브러리
  • SSH 서버에 접속해서 명령을 실행하거나, SFTP로 파일을 업로드·다운로드하거나, 포트포워딩을 할 수 있게 만들어주는 도구

2. JSCH 특징

  • 자바로 구현된 순수 라이브러리라, 별도 플랫폼 의존성 없이 어디서든 사용 가능
  • SSH 프로토콜을 사용해 원격 서버와 안전하게 통신
  • SFTP 기능을 통해 파일 송수신 가능
  • SSH 터널링(포트포워딩)도 지원
  • 스프링과는 무관하며, 그냥 자바 프로젝트에서 가져다 쓰면 되는 외부 라이브러리

3. JSCH 기본형태

1. 기본 형태

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    JSch jsch = new JSch();
    
    // SSH 로그인 정보 설정
    Session session = jsch.getSession("username", "host", 22); //유저이름, IP,포트
    session.setPassword("password"); //패스워드
    
    // 호스트 키 검증 생략(테스트용)
    session.setConfig("StrictHostKeyChecking", "no");
    
    // 세션 연결
    session.connect();
    
    // 채널 생성 및 작업
    ChannelExec channel = (ChannelExec) session.openChannel("exec");  
       
    // 종료
    channel.disconnect();
    session.disconnect();

2. 채널종류

채널 종류설명사용 예시사용 빈도
exec원격 서버에서 단일 명령 실행ls -al, mkdir test높음
shell인터랙티브 쉘 연결서버 터미널 접속, 스크립트 실행중간
sftpSFTP를 통한 파일 업/다운로드파일 업로드/다운로드높음
subsystemSSH 서브시스템 호출SFTP, 기타 서버 서브시스템낮음
direct-tcpip로컬→원격 포트포워딩DB 접속 터널링, SSH 터널중간
forwarded-tcpip원격→로컬 포트포워딩원격 서비스 로컬 접근낮음
x11X11 GUI 포워딩리눅스 GUI 프로그램 실행매우 낮음

1. exec 채널

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  // exec 채널 생성
  ChannelExec execChannel = (ChannelExec) session.openChannel("exec");
  execChannel.setCommand("ls -al");
  
  // 출력 스트림
  InputStream in = execChannel.getInputStream();
  execChannel.connect();
  
  // 결과 출력
  byte[] buffer = new byte[1024];
  while (true) {
      int read = in.read(buffer);
      if (read == -1) break;
      System.out.print(new String(buffer, 0, read));
  }
  
  // 종료
  execChannel.disconnect();
  session.disconnect();

2. sftp 채널

1
2
3
4
5
6
7
8
9
  // SFTP 채널 생성
  ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
  sftpChannel.connect();
  
  // 파일 업로드
  sftpChannel.put("C:/local/file.txt", "/remote/path/file.txt");
  
  // 파일 다운로드
  sftpChannel.get("/remote/path/file.txt", "C:/local/file.txt");

3. direct-tcpip 채널 (포트포워딩)

1
2
3
4
5
  // 로컬 포트포워딩 설정 (ChannelDirectTCPIP 객체 안 만듦)
  session.setPortForwardingL(3306, "10.0.0.5", 3306);
  
  // JDBC 연결 (터널 통해 원격 DB 접속)
  Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "pass");
  • 세션에서 setPortForwardingL(3306, “10.0.0.5”, 3306)을 설정하면,
  • 세션이 살아 있는 동안 OS 수준에서 로컬 포트 3306으로 들어오는 모든 트래픽이 SSH 세션을 통해 원격 10.0.0.5:3306으로 전달함

3.스프링에서 JSCH

1. 커넥션분리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    @Component
    public class JSchManager {
    
      private String username = "user";
      private String host = "host.com";
      private int port = 22;
      private String password = "password";
  
      public Session getSession() throws JSchException {
          JSch jsch = new JSch(); // 세션을 신규로 생성함.
          Session session = jsch.getSession(username, host, port);
          session.setPassword(password);
          session.setConfig("StrictHostKeyChecking", "no");
          session.connect();
          return session; // 돌려주는 세션 값이 신규 객체라 스레드에 안전함. 
      }
    }

2. 서비스단

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    @Service
    public class RemoteService {
    
        private final JSchManager jschManager;
    
        public RemoteService(JSchManager jschManager) {
            this.jschManager = jschManager;
        }
    
        public String runCommand() {
            Session session = null;
            try {
                session = jschManager.getSession(); // 매번 새 Session 생성받기 때문에 안전.
                ChannelExec channel = (ChannelExec) session.openChannel("exec");
                channel.setCommand("ls -al");
                InputStream in = channel.getInputStream();
                channel.connect();
    
                // 결과 처리
                channel.disconnect();
                return new String(in.readAllBytes());
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                if (session != null && session.isConnected()) {
                    session.disconnect(); 
                }
            }
        }
    }

4. 비슷한 라이브러리

1. Apache Mina SSHD

  • 아파치 재단에서 제공하는 SSH/SFTP 라이브러리
  • 클라이언트·서버 모두 구현 가능
  • 유지보수가 활발한 편
  • JSCH보다 구조가 명확하고 확장성이 좋다는 평가가 많음
  • 최근 스프링에서도 SSH 기능이 필요하면 Mina 기반 도구를 쓰는 경우도 있음

2. SSHJ

  • JSCH의 현대적인 대안으로 많이 언급됨
  • API가 단순하고 직관적
  • 키 관리, 암호화 알고리즘 지원이 더 풍부
  • JSCH보다 코드 작성 난이도가 낮음
  • SFTP도 안정적으로 지원

3. Ganymed SSH-2

  • 예전에는 JSCH와 많이 비교되던 라이브러리
  • 한동안 업데이트가 끊겼지만 아직도 사용 중인 레거시 프로젝트가 있음
  • 경량 SSH 클라이언트

4. JCraft SCP/SFTP 확장 라이브러리들

  • JSCH 기반 확장 모듈들이 존재
  • SFTP 기능만 간단하게 쓰고 싶을 때 가벼운 옵션

5. 비교

구분JSCHSSHJApache Mina SSHD
개발/유지보수업데이트 거의 없음 (레거시 수준)꾸준한 유지보수아파치 재단에서 활발히 유지
사용 난이도난이도 있음, API 직관성이 낮음가장 쉬움, API 구조 명확중간 정도 (확장성 중심 설계)
SSH 클라이언트지원지원지원
SSH 서버 구현불가불가가능 (서버/클라이언트 모두 지원)
SFTP안정적이지만 오래된 방식안정적, 현대적 암호화 지원완전 지원
암호화 알고리즘 지원구식, 최신 알고리즘 부족최신 알고리즘 대부분 지원최신 알고리즘 지원
문서/예제적고 오래됨풍부공식 문서 매우 자세함
성능경량, 빠른 편경량, 빠르고 안정적무겁지만 안정적
장점단순 구조, 오래 사용되어 정보 풍부직관적, 최신 기술 대응, 사용성 좋음확장성 최고, 서버 구현 가능
단점오래됨, 유지보수 부족약간의 학습 필요무거움, 설정 복잡
추천 용도레거시 프로젝트 유지최신 프로젝트에서 SSH/SFTP고급 기능·서버 구현이 필요한 경우
This post is licensed under CC BY 4.0 by the author.