티스토리 뷰

Spring 웹 애플리케이션에서 AWS SDK를 이용하여 AWS 리소스에 액세스하기 위해서는 accessKeysecretKey로 AWS Credentials 자격증명을 관리하는 객체를 생성해서 액세스할 수 있습니다.

 

그런데 이러한 웹 애플리케이션이 EC2에 배포가 된다면 accessKeysecretKey를 발급하지 않고도 IAM에 역할을 부여하고 인스턴스 메타데이터를 이용하여 액세스할 수 있는 방법도 있습니다.

 

그런데 인스턴스 메타데이터를 이용하여 AWS 리소스에 액세스할 경우 주의해야 할 점이 있는데요. 지금부터 천천히 알아보도록 하겠습니다.

 

애플리케이션 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class AmazonS3Configuration {
    @Bean
    public AmazonS3 amazonS3() {
        AWSCredentials awsCredentials = InstanceProfileCredentialsProvider.getInstance().getCredentials();
 
        return AmazonS3ClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_2)
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
                .build();
    }
}
cs

위 코드는 인스턴스 메타데이터를 이용하여 EC2에 부여된 IAM 역할을 기반으로 credentials로 AmazonS3Client를 Spring Bean으로 설정하는 코드입니다.



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
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.S3Object;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
 
@Slf4j
@Component
@EnableScheduling
@RequiredArgsConstructor
public class AWSCredentialsTestJob {
    private static final String BUCKET_NAME = "버킷네임";
 
    private final AmazonS3 amazonS3;
 
    @Scheduled(cron = "0 0/30 * * * *")
    public void check() {
        log.info("현재시간 : {}", LocalDateTime.now());
 
        try {
            S3Object object = amazonS3.getObject(BUCKET_NAME, "1234.txt");
            log.info("s3 object key : {}", object.getKey());
        } catch (Exception e) {
            log.info("예외발생 : ", e);
        }
    }
}
cs

그리고 30분 간격으로 S3 버킷에 저장돼있는 객체(1234.txt)를 조회하도록 하였습니다.

 

 

애플리케이션을 EC2에 배포하고 30분 간격으로 로그를 확인해보니 6시간 30분 후 The provided token has expired 라는 예외 메세지가 나왔습니다. 원인은 토큰이 만료되었기 때문에 발생하는데 이제 이러한 원인을 파악해보겠습니다.

 

참고

AWS SDK마다 다르지만 S3는 위와 같은 예외 메세지가 나오고 SES, SQS 등 다른 서비스에서는 The security token included in the request is expired라는 예외 메세지가 나오기도 합니다.

 

 

원인분석

The security token included in the request is expired의 원인을 파악하기 위해 공식 문서를 확인해본 결과 다음과 같은 내용이 있었습니다.

 

InstanceProfileCredentialsProvider 클래스를 이용할 경우 임시 보안 자격 증명으로 AWS 리소스에 액세스할 수 있다고 합니다.



임시 보안 자격 증명은 몇분에서 몇 시간 동안 지속될 수 있고 만료되거나 혹은 이전에 새로운 자격증명을 요청할 수 있다고 합니다.

 

여기까지만 찾아봤을 때 InstanceProfileCredentialsProvider를 사용하게되면 만료되기 전에 혹은 이전에 새로운 자격증명을 요청하도록 구성해야 하는데 이러한 방법을 어떻게 해야 하는지 또 다시 한번 관련 자료를 찾아보았습니다.

 

 

AWS SDK for Java 1.x 공식문서를 확인해본 결과 다음과 같은 내용이 있었습니다. 번역해보자면 다음과 같습니다.

 

이 접근 방식을 사용할 때 SDK는 인스턴스 프로필의 Amazon EC2 인스턴스와 연결된 IAM 역할과 연결된 권한과 동일한 권한을 가진 임시 AWS 자격 증명을 검색합니다. 이러한 자격 증명은 일시적이고 결국 만료되지만 InstanceProfileCredentialsProvider는 획득한 자격 증명이 계속해서 AWS에 대한 액세스를 허용하도록 사용자를 위해 자격 증명을 주기적으로 새로 고칩니다.

자동 자격 증명 새로 고침은 기본 공급자 체인의 일부로 자체 InstanceProfileCredentialsProvider를 생성하는 기본 클라이언트 생성자를 사용하거나 InstanceProfileCredentialsProvider 인스턴스를 클라이언트 생성자에 직접 전달하는 경우에만 발생합니다. 다른 방법을 사용하여 인스턴스 프로파일 자격 증명을 얻거나 전달하는 경우 만료된 자격 증명을 확인하고 새로 고칠 책임이 있습니다.

InstanceProfileCredentialsProvider 클래스를 이용하면 획득한 자격 증명에 대해서 주기적으로 새로 고치게끔 해주지만 자격 증명을 자동으로 새로고침 하기 위해서는 InstanceProfileCredentialsProvider 인스턴스를 클라이언트 생성자에 직접 전달하는 경우에만 발생한다고 합니다. 그러면 이제 처음으로 돌아가 애플리케이션 코드를 수정해보도록 하겠습니다.

 

애플리케이션 코드수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class AmazonS3Configuration {
    @Bean
    public AmazonS3 amazonS3() {
//        AWSCredentials awsCredentials = InstanceProfileCredentialsProvider.getInstance().getCredentials();
 
        return AmazonS3ClientBuilder.standard()
                .withRegion(Regions.AP_NORTHEAST_2)
                .withCredentials(InstanceProfileCredentialsProvider.getInstance())
                .build();
    }
}
cs

처음에 설정한 코드에서는 AWSStaticCredentialsProvider 생성자를 전달하여 AmazonS3Client 클라이언트를 생성하였지만 InstanceProfileCredentialsProvider 인스턴스를 직접 전달하도록 코드를 변경해보겠습니다.

 

 

아까보다 로깅을 조금 더 오래 해봤는데 예외가 발생하지 않고 정상적으로 S3 버킷에 액세스 된 걸 확인하실 수 있습니다.

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/04   »
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
글 보관함