이번에 프로젝트를 진행하면서 자바 코드로 작성한 배치 처리 프로그램에서 SCP로 파일을 외부 서버로 전송할 일이 생겼다. 검색을 하고 몇 개를 훑어본 뒤에 j2ssh-maverick을 사용하기로 결정했다.
SCP 파일 복사에 사용할 key 파일 생성
ssh-keygen 명령어로 키 파일을 생성한다.
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/madvirus/.ssh/id_rsa):
Created directory '/home/madvirus/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/madvirus/.ssh/id_rsa.
Your public key has been saved in /home/madvirus/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:....생략..... madvirus@mvpc
The key's randomart image is:
+---[RSA 2048]----+
|o. .+** . |
|. ..=o = |
|.o +.o. o |
|. *o.o. o |
| ..Eo .S |
|+o= + . |
|+*.* + . |
|+.* * . |
|o*oo ... |
+----[SHA256]-----+
명령어를 실행하고 나면 홈 디렉토리의 .ssh 폴더에 비밀키 id_rsa 파일과 공개키 id_rsa.pub 파일이 생성된다.
공개키 내용을 복사할 원격 서버의 authorized_keys 파일에 추가
SCP로 파일을 복사할 원격 서버 계정의 authorized_keys 파일에 공개키 파일을 추가한다. 원격 서버의 홈 디렉토리에 .ssh 디렉토리가 없다면 .ssh 디렉토리를 생성한다. 단 권한은 700으로 설정한다. ~/.ssh/authorized_keys 파일이 없다면 authorized_keys 파일도 생성한다. 이 파일의 권한은 600으로 설정한다.
-원격서버에 scp에 사용할 계정으로 로그인
$ cd ~
$ mkdir .ssh
$ chmod 700 .ssh
$ touch .ssh/authorized_keys
$ chmod 600 .ssh/authorized_keys
.ssh 폴더와 authorized_keys 파일의 권한이 올바르지 않으면 sshd는 SSH 연결을 허용하지 않는다. 이에 대한 내용은 http://man.openbsd.org/sshd를 참고한다.
원격 서버에 ~/.ssh 폴더와 ~/.ssh/authorized_keys 파일을 생성했다면 공개키 파일의 내용을 원격서버의 authorized_keys 파일에 추가한다. 아래 명령어를 사용하면 간편하게 추가할 수 있다.
cat ~/.ssh/id_rsa.pub | ssh 계정@원격서버주소 'cat >> ~/.ssh/authorized_keys'
프로젝트에 의존 추가
j2ssh-maverick을 사용하려면 다음의 의존을 pom.xml 파일에 추가한다.
<dependency>
<groupId>com.sshtools</groupId>
<artifactId>j2ssh-maverick</artifactId>
<version>1.5.5</version>
</dependency>
j2ssh-maverick과 키 파일을 이용한 SCP 복사
앞서 생성한 비밀키 파일인 id_rsa를 이용해서 로컬의 파일을 원격지 서버에 전송하는 코드를 작성할 차례이다. 전체 코드는 다음과 같다.
File keyFilePath = new File("id_rsa 파일 경로");
String remoteHost = "원격서버주소";
String remoteUser = "batch";
File localFile = new File("전송할 로컬 파일");
String remotePath = "/home/batch/file";
SshClient ssh = null;
try {
SshConnector con = SshConnector.createInstance();
byte[] pkKey = FileCopyUtils.copyToByteArray(keyFilePath);
SshPrivateKeyFile pkf = SshPrivateKeyFileFactory.parse(pkKey);
SshKeyPair pair = pkf.toKeyPair("");
ssh = con.connect(new SocketTransport(remoteHost, 22), remoteUser);
Ssh2PublicKeyAuthentication sshAuth = new Ssh2PublicKeyAuthentication();
sshAuth.setPrivateKey(pair.getPrivateKey());
sshAuth.setPublicKey(pair.getPublicKey());
int authResult = ssh.authenticate(sshAuth);
if (authResult == SshAuthentication.COMPLETE) {
ScpClient scpClient = new ScpClient(localFile, ssh);
scpClient.put(localFile.getAbsolutePath(), remotePath, false);
scpClient.exit();
} else {
// 인증에 실패한 경우, 알맞은 처리
}
} finally {
if (ssh != null)
try {
ssh.disconnect();
} catch (Exception ex) {
}
}
인증에 실패할 경우 원격서버의 .ssh 디렉토리 권한과 .ssh/authorized_keys 파일의 권한이 올바른지 확인해본다.
j2ssh-maverick에 대한 다양한 내용은 https://github.com/sshtools/j2ssh-maverick 사이트에서 확인할 수 있다.