본문으로 바로가기

[STS 적용 - 3] AWS S3 Access SDK For Java

category AWS 2021. 11. 11. 15:10
반응형

EB와 IAM 까지 만들었다면 이제 소스를 통해 실제로 접근이 가능한지 체크해야 한다.

SDK For Java v1

gradle 에서 아래를 추가한다.

// s3
implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.1000')
implementation group: 'com.amazonaws', name: 'aws-java-sdk-sts', version: '1.11.895'
implementation group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: '1.11.895'

S3 Client 에 접근하기 위해 Access Key와 Secret Key를 직접 소스에 입력한다.

STS 적용 전

AmazonS3 s3 = AmazonS3ClientBuilder.standard()
    .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("AccessKey", "SecretKey")))
    .build();

s3.putObject(new PutObjectRequest("버킷이름", "파일주소", 멀티파트 스트림)
    .withCannedAcl(CannedAccessControlList.PublicRead));

위처럼 하는 경우 보안상 좋지 않으니 이제부터 임시 Access Key 와 임시 Secret Key 를 발급받아 사용할 것이다.


STS 적용 후

private AmazonS3 s3;

public AmazonS3 s3Client() throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        // System.getProperty("user.home")
        // Window -> 사용자 계정 폴더
        // Linux -> tomcat 기준 CATALINA_HOME 인 것 같음
        //          설정 경로는 CATALINA_HOME으로 추정 ps -ef | grep catalina -> -Dcatalina.home=해당경로
        //          경로를 바꾼다면 tomcat이 안되지 않을까 싶음
        // System.getProperty를 "~/" 로 해보았으나 /usr/share/tomcat 을 추출함 -> /usr/share/tomcat/~/.aws/credentials (x)
        File configFile = new File(System.getProperty("user.home"), ".aws/credentials");
        AWSCredentialsProvider credentialsProvider = new ProfileCredentialsProvider(new ProfilesConfigFile(configFile), "default");

        AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard()
            .withCredentials(credentialsProvider)
            .build();

        AssumeRoleRequest roleRequest = new AssumeRoleRequest()
            .withRoleArn(resourceNumber)
            .withRoleSessionName("세션이름 아무거나")
            .withDurationSeconds(900); // 15분 부터 36시간 까지이며 기본은 1시간이다.

        AssumeRoleResult assumeRoleResult = stsClient.assumeRole(roleRequest);
        Credentials temporaryCredentials = assumeRoleResult.getCredentials();

        log.debug("temp access key : {}", temporaryCredentials.getAccessKeyId());
        log.debug("temp secret key : {}", temporaryCredentials.getSecretAccessKey());

        AWSCredentials credentials = new BasicSessionCredentials(temporaryCredentials.getAccessKeyId(), temporaryCredentials.getSecretAccessKey(), temporaryCredentials.getSessionToken());

        stsClient.shutdown();
        if (s3 != null) s3.shutdown();

        s3 = AmazonS3ClientBuilder.standard()
            .withCredentials(new AWSStaticCredentialsProvider(credentials))
            .build();
}

AWS 문서를 뒤져보면 ProfileCredentialsProvider 를 통해 서버내 위치한 ~/.aws/credentials 파일을 읽어서 기본 값인 default 를 읽어 Access Key와 Secret Key를 불러와 사용하는 것으로 나와있다.

하지만 문서대로 실행해보면 다음과 같은 에러를 볼 수 있다.
Profile cannot be null

그래서 며칠간 이것저것 시도해본 결과 EB 정책으로 ec2-role 에 S3 정책을 부여했을 때 되는 것을 확인하여 ec2 프로필을 만들어서 해보았으나 여전히 sts:assumeRole Access Deny 403 에러를 만났었다.

그래서 바꾼 것이 File을 불러서 Profile을 읽고 (이거는 됨 왜 ? 왜 되는걸까) 사용하는 방식으로 변경하였다.

기본적으로 tomcat 기준으로 불러오다 보니 문서에서 말하는 ~/.aws/ 경로가 아닌 /usr/share/tomcat/ 경로에 위치해야 했다.

해당 경로에 아래 폴더 및 파일을 생성한다.

cd /usr/share/tomcat
mkdir .aws
cd .aws
vi config
[default]
region = ap-northeast-2
vi credentials
[default]
aws_access_key_id = 아까 복사해둔 Access Key
aws_secret_access_key = 아까 복사해둔 Secret Key

war 파일을 생성한 후 eb 에서 애플리케이션 배포를 통해 배포한 후 S3 접근 API로 확인하면 정상 접근 될 것이다.

SDK For Java v2

gradle 에 v1 버전을 주석처리하고 새로 추가한다.

v2 는 eb 환경이 아니고 기본 EC2에서 인스턴스를 만들었을 경우로 적용했다.

implementation platform('software.amazon.awssdk:bom:2.15.0')
implementation 'software.amazon.awssdk:kinesis' // 종속성 섹션에 사용할 SDK 모듈
implementation 'software.amazon.awssdk:sts'
implementation 'software.amazon.awssdk:s3'
private S3client s3;

public S3client s3Client() throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        ProfileCredentialsProvider profileCredentialsProvider = ProfileCredentialsProvider.create();

        StsClient stsClient = StsClient.builder()
            .credentialsProvider(profileCredentialsProvider)
            .build();

        AssumeRoleRequest assumeRoleRequest = AssumeRoleRequest.builder()
            .roleArn(roleARN)
            .roleSessionName("sessionName")
            .durationSeconds(900)
            .build();

        StsAssumeRoleCredentialsProvider stsAssumeRoleCredentialsProvider = StsAssumeRoleCredentialsProvider.builder()
            .refreshRequest(assumeRoleRequest)
            .stsClient(stsClient)
            .build();

        AwsCredentials tempAwsCredentials = stsAssumeRoleCredentialsProvider.resolveCredentials();
        AwsCredentialsProvider awsCredentialsProvider = () -> tempAwsCredentials;

        stsClient.close();
        if (s3 != null) s3Client.close();

        s3 = S3Client.builder()
            .credentialsProvider(awsCredentialsProvider)
            .build();
}
ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();

S3 Policy 추가

퍼블릭 엑세스 차단은 모두 비활성화 한다.

eb를 통해 애플리케이션을 배포 하고 나면 S3 에 Bucket 이 새로 생성되며 해당 Bucket 을 클릭하고 상단 탭 중 권한 탭으로 들어가
버킷 정책을 편집하여 아래 내용을 추가한다.

{
    "Version": "{ yyyy-mm-dd }",
    "Statement": [
        {
            "Sid": "eb-ad78f54a-f239-4c90-adda-49e5f56cb51e",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::{ 계정숫자 }:role/aws-elasticbeanstalk-ec2-role"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::elasticbeanstalk-{ 리전값-계정숫자 }/resources/environments/logs/*"
        },
        {
            "Sid": "eb-blahblah",
            "Effect": "Deny",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:DeleteBucket",
            "Resource": "arn:aws:s3:::elasticbeanstalk-{ 리전값-계정숫자 }"
        },
        {
            "Sid": "eb-blahblah",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::{ 계정숫자 }:role/aws-elasticbeanstalk-ec2-role",
                    "arn:aws:iam::{ 계정숫자 }:user/{ user1 역할명 }"
                ]
            },
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::elasticbeanstalk-{ 리전값-계정숫자 }",
                "arn:aws:s3:::elasticbeanstalk-{ 리전값-계정숫자 }/resources/environments/*"
            ]
        }
    ]
}

Difference ?

이것저것 시도해본 결과 EB를 통해 만들어진 EC2 는 aws cli 가 깔려있는 버전(?) 이라고 생각되고
그냥 EC2 상품에서 인스턴스를 만든 것과 다르다 라고 판단했다.

왜냐하면 ~/.aws/credentials 경로를 불러서 하는 방법이 잘 되기 때문이다.

반응형

'AWS' 카테고리의 다른 글

EFS Mount 하는 방법  (0) 2023.05.18
EC2 EBS Scale Up  (0) 2022.11.30
[STS 적용 - 4] NCS S3 STS 적용  (0) 2021.11.11
[STS 적용 - 2] IAM 생성  (0) 2021.11.11
[STS 적용 - 1] Elastic Beanstalk 생성  (0) 2021.11.11