B2G 사업으로 인해 AWS 가 아닌 Gov Naver Cloud 에 서버가 있는게 있다.
해당 서버들도 Object Storage 를 사용하기 때문에 STS 를 적용해주어야한다.
Naver Cloud 의 Object Storage는 Amazon S3 API를 사용하고 있기 때문에 앞서 사용한 소스의 일부를 그대로 쓰면 된다.
STS 적용 전
private AmazonS3 s3;
public void s3Client() {
s3 = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endPoint, regionName))
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("access key", "secret key")))
.build();
}
Create Sub Account
Naver Cloud 에서 STS를 적용하기 위해서는 Sub Account
상품을 이용해야한다. 무료다.
Sub Account 에서 서브 계정 생성을 누르고 아래 이미지 처럼 필요한 정보를 입력하고 만든다.
API 전용으로 생성한 Sub Account 를 클릭하여 들어간 후 정책에서 추가를 누른다음 아래 2개를 추가해준다.
- NCP_OBJECT_STORAGE_MANAGER -> Object Storage 관련하여 접근하기 위해 사용됨
- NCP_SUB_ACCOUNT_VIEWER -> STS API를 호출하기 위해서 사용됨
Access Key 탭에서 Access Key 와 Secret Key 를 복사한다.
STS 적용
기본 적인 환경 구성은 AWS와 동일하다.
Window 라면 사용자 폴더 내 .aws를 만들어 config 와 credentials 를 메모장으로 만든다.
메모장으로 파일을 만들면 확장자가 .txt가 붙으니 편집 하고 나선 .txt 를 지워준다
Linux 라면 ~/.aws/
에 생성한다.
[default]
region = gov-standard
[default]
aws_access_key_id = 아까 복사한 Access Key
aws_secret_access_key = 아까 복사한 Secret Key
만약 같은 환경에서 AWS, NCS 두 곳을 등록하기 위해서는 config 파일에서 아래와 같이 한다.
[default]
blahblah
[profile 프로필명]
설정값
꼭 config에서 다른 프로필을 지정할 때는 앞에 profile 을 붙이라고 권장하는 것 같다. 없어도 실행되긴 함.
credentials 에서는 아래와 같다.
[default]
blahblah
[프로필명]
blahblah
다음은 Java 소스이다. 직접적인 호출은 AWS를 통하는 것이 아니기 때문에 Naver Cloud 에서 제공하는 STS API를 호출해야한다.
STS API 를 호출해야 Naver Cloud 가 AWS로 API를 통신한 다음 승인해주기 때문에 그렇다.
// ncs 프로필 사용
AWSCredentialsProvider awsCredentialsProvider = new ProfileCredentialsProvider("ncs");
String ncsAccessKey = awsCredentialsProvider.getCredentials().getAWSAccessKeyId();
String ncsSecretKey = awsCredentialsProvider.getCredentials().getAWSSecretKey();
// 서버 timestamp 추출
String timestamp = Long.toString(System.currentTimeMillis());
// signature 생성
String signature = makeSignature(timestamp, ncsAccessKey, ncsSecretKey);
// 유효시간 15분으로 설정
// 이 또한 AWS API를 쓰는거기 때문에 최소 15분 ~ 36시간 이며 기본은 1시간이다
NcsStsRequestDTO ncsStsRequestDTO = NcsStsRequestDTO.builder().durationSec(900).build();
// STS 생성
String url = "https://sts.apigw.gov-ntruss.com/api/v1/credentials"; // 생성은 POST, 조회는 GET
RequestEntity<NcsStsRequestDTO> requestEntity = RequestEntity
.post(url)
.header("x-ncp-apigw-timestamp", timestamp)
.header("x-ncp-iam-access-key", ncsAccessKey)
.header("x-ncp-apigw-signature-v2", signature)
.body(ncsStsRequestDTO);
// RestTemplate
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler()); // 에러 핸들링 커스텀
// RestTemplate 은 Timeout을 지정하지 않으면 요청 후 무한으로 대기하기 때문에 무조건 넣자
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpComponentsClientHttpRequestFactory.setConnectTimeout(3000); // connection timeout 3초
httpComponentsClientHttpRequestFactory.setReadTimeout(5000); // read timeout 5초
restTemplate.setRequestFactory(httpComponentsClientHttpRequestFactory);
ResponseEntity<Map> response = restTemplate.exchange(requestEntity, Map.class);
// 200 아닌 경우 에러 핸들링 DB에 로그 남기기
if (response.getStatusCode() != HttpStatus.OK) {
Map result = response.getBody();
if (result != null && result.containsKey("errorCode")) {
JSONObject json = new JSONObject();
String errorCode = String.valueOf(result.get("errorCode"));
String message = String.valueOf(result.get("message"));
log.error("errorCode : " + errorCode);
log.error("message : " + message);
json.put("errorCode", errorCode);
json.put("message", message);
if (result.containsKey("actions")) {
String actions = String.valueOf(result.get("actions"));
log.error("actions : " + actions);
json.put("actions", actions);
}
// Error Log Save
ErrorLog errorLog = ErrorLog.builder()
.url(url)
.header(requestEntity.getHeaders().toString())
.method(requestEntity.getMethod().toString())
.message(json.toString())
.build();
errorLogRepository.save(errorLog);
throw new RuntimeException();
}
}
// 200 인 경우
Map result = response.getBody();
log.debug("result : " + result);
if (result == null || !result.containsKey("accessKey")) {
throw new RuntimeException();
}
tempAccessKey = result.get("accessKey").toString();
tempAccessKeyExpireTime = result.get("expireTime").toString();
if (result.containsKey("accessKey")) {
log.debug("accessKey : " + result.get("accessKey"));
log.debug("secretKey : " + result.get("keySecret"));
log.debug("createTime : " + result.get("createTime"));
log.debug("expireTime : " + result.get("expireTime"));
log.debug("useMfa : " + result.get("useMfa"));
tempAccessKey = String.valueOf(result.get("accessKey"));
s3 = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("https://kr.object.gov-ncloudstorage.com", "gov-standard"))
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(result.get("accessKey").toString(), result.get("keySecret").toString())))
.build();
}
STS API 를 사용하기 위해서는 Signature를 만들어야하는데 그냥 아래 메서드를 별도로 만들어서 쓰자. 복붙하기 !
public String makeSignature(String timestamp, String accessKey, String secretKey) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException {
String space = " ";
String newLine = "\n";
String method = "POST";
String url = "/api/v1/credentials";
String message = new StringBuilder()
.append(method)
.append(space)
.append(url)
.append(newLine)
.append(timestamp)
.append(newLine)
.append(accessKey)
.toString();
SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
return Base64.encodeBase64String(rawHmac);
}
위 내용들을 1개의 Service로 만들어서 내부 로직에 구현하고 필요 시 마다 임시 Access Key를 만들어 사용하면 된다.
만약 Naver Cloud를 사용하는 경우에는 API를 호출하여 STS 를 생성한 직후 S3 관련된 메서드를 실행할 경우 에러가 난다.
글쓴이가 생각하는 원인은 Naver Cloud 가 AWS S3 API를 이용하는 것이기 때문에 실시간으로 API를 등록하고 곧바로 쓰기에는
아직 AWS S3 API에 발급된 임시 Access Key 가 등록이 되지 않아 약간의 시간차가 필요한 것으로 보인다.
그래서 글쓴이는 임시 Access Key 를 발급 하고 이를 사용하는 곳에서는 강제로 1~2 초 정도 대기 시간을 주었다.
Thread.sleep(2000);
'AWS' 카테고리의 다른 글
EFS Mount 하는 방법 (0) | 2023.05.18 |
---|---|
EC2 EBS Scale Up (0) | 2022.11.30 |
[STS 적용 - 3] AWS S3 Access SDK For Java (1) | 2021.11.11 |
[STS 적용 - 2] IAM 생성 (0) | 2021.11.11 |
[STS 적용 - 1] Elastic Beanstalk 생성 (0) | 2021.11.11 |