먼저 mvn repository에서 JSch라이브러리를 받는다.
JSch 라이브러리 바로가기
dependencies {
...
// https://mvnrepository.com/artifact/com.jcraft/jsch
implementation 'com.jcraft:jsch:0.1.55'
}
필자는 gradle을 활용했으므로 위처럼 입력.
maven이나 jar를 이용하신다면, 그에 맞게 설정하시면 됨.
SshTunnelingInitializer.java
package der.ssh;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PreDestroy;
import java.util.Properties;
@Slf4j
@Profile("prod")
@Component
@ConfigurationProperties(prefix = "ssh")
@Validated
@Setter
public class SshTunnelingInitializer {
private String host;
private String user;
private int sshPort;
private String privateKey;
private int databasePort;
private Session session;
@PreDestroy
public void closeSSH() {
if (session != null && session.isConnected())
session.disconnect();
}
public Integer buildSshConnection() {
Integer forwardedPort = null;
try {
log.info("{}@{}:{}:{} with privateKey",user, host, sshPort, databasePort);
log.info("start ssh tunneling..");
JSch jSch = new JSch();
log.info("creating ssh session");
jSch.addIdentity(privateKey); // 개인키
session = jSch.getSession(user, host, sshPort); // 세션 설정
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
log.info("complete creating ssh session");
log.info("start connecting ssh connection");
session.connect(); // ssh 연결
log.info("success connecting ssh connection ");
// 로컬pc의 남는 포트 하나와 원격 접속한 pc의 db포트 연결
log.info("start forwarding");
forwardedPort = session.setPortForwardingL(33306, "localhost", databasePort);
log.info("successfully connected to database");
} catch (JSchException e){
this.closeSSH();
e.printStackTrace();
log.error("fail to make ssh tunneling : {}", e.getMessage());
}
return forwardedPort;
}
}
SshDataSourceConfig.java
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import javax.sql.DataSource;
@Slf4j
@Profile("prod")
@Configuration
@RequiredArgsConstructor
public class SshDataSourceConfig {
private final SshTunnelingInitializer initializer;
@Bean("dataSource")
@Primary
public DataSource dataSource(DataSourceProperties properties) {
Integer forwardedPort = initializer.buildSshConnection(); // ssh 연결 및 터널링 설정
String url = properties.getUrl().replace("[forwardedPort]", Integer.toString(forwardedPort));
log.info(url);
return DataSourceBuilder.create()
.url(url)
.username(properties.getUsername())
.password(properties.getPassword())
.driverClassName(properties.getDriverClassName())
.build();
}
}
위 두파일을 생성하고, profile에 properties에 맞게 입력한다. properties가 따로 없으면 삭제. 필자의 properties는 prod이다.
해당 properties에
spring.datasource.driverClassName={DB 드라이버}
spring.datasource.url=jdbc:{사용할 DB 드라이버}//127.0.0.1:33306/{DB명(스키마)}
spring.datasource.username={DB USER}
spring.datasource.password={DB Password}
ssh.host={접속할 ssh ip}
ssh.ssh_port={ssh 포트}
ssh.user={user}
ssh.private_key={ssh 비밀키 경로}
ssh.database_port={ssh 접속환경에서 활용할 DB}
위 내용을 입력한다.
여기서 database url에 해당 되는 포트는
위의 포트와 일치해야한다. (proxy원리이기 때문 33306 -> 3306)
다음은 ssh 키 생성이 필요하다.
ssh-keygen -f id_rsa -m PEM
입력한 후 키 생성을 한다.
다음 생성 된 ssh 공개키를 서버의 유저 -> .ssh파일 -> authorized_keys에 입력 (자세한 내용은 공개키 비밀키를 통한 서버 접속을 찾아보면 된다.)
일반적인 open ssh rsa 키 생성으로는 invalid 키라고 에러를 만들기 때문에 꼭 위의 명령어를 입력하여 새로운 키를 만들고 활용하도록 하자.