Do Something

[AWS-S3] S3에서 내 계정으로만 쓰기 가능하고 모두가 읽기만 가능하도록 본문

AWS

[AWS-S3] S3에서 내 계정으로만 쓰기 가능하고 모두가 읽기만 가능하도록

뭐라도해야겠다 2023. 7. 30. 22:53

s3도 쓸려고 하니까 복잡하다

내 Spring boot어플리케이션에서만 s3에 업로드 가능하게 하고 그 외는 전부 막아버리고 싶었다.

목표 : Iam 계정을 사용해서 업로드 가능. 그 외 전부 막기

첫번째로 내 Iam계정을 수정해야 한다.


저 오른쪽 위에 들어가서 [내 보안 자격 증명] 을 누르도록 하자

엑세스 관리 -> 사용자

사용자 추가

사용자를 만들 때 권한 정책에서 "AmazonS3FullAccess"를 줘버리자

PutBucketPolicy인가 이거만 있으면 되는거 같긴 하다. 

 

보안자격 증명 -> 엑세스 키

엑세스 키 만들기 -> 만들기!

accessKey, secretKey가 만들어진다. 

 

결론적으로  AmazonS3FullAccess를 가진 Iam계정과 키를 만들었다

 

이제 s3로 넘어가보자

버킷 -> 권한

 

ACL을 딱히 설정하지 않아서 상관없지만 일단 하나만 체크해두자. 

모두가 읽을 수 있어야 하므로 다른건 건들지 않는다

 

버킷 정책을 수정해야 한다. 

1. 모든 사람이 객체를 읽을 수 있어야 한다. 

2. 내 계정으로는 쓰기가 가능해야 한다. 

이걸 버킷 정책에 그대로 옮기면 된다. 

 

{
    "Version": "2012-10-17",
    "Id": "Policy1689919765186",
    "Statement": [
    // 모든 사람에 대한 접근 허용
        {
            "Sid": "Stmt1689919302606",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::[님 버킷 이름]/*"
        },
    // 내 계정만 put가능
        {
            "Sid": "allowGet",
            "Effect": "Allow",
            "Principal": {
                "AWS": [님 계정]
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::[님 버킷 이름]/*"
        }
    ]
}

다음과 같다. 

"Principal" -> "AWS"에는 

요기 있는 ARN을 적으면 된다. 

 

이렇게 하지 않고 

1. 모든 사람에 대한 접근 허용

2. 모든 사람에 대한 접근 거부 -> 내 계정은 제외 

하는 방법인 

"NotPrincipal"을 사용할 수도 있다. 

 

이렇게 하면 S3설정은 끝난다.

 

스프링에서 사용해보자

yml파일에는 다음과 같이 아까 Iam에서 발급받은 키를 써준다. 

 

지금부터는 코드만 보여주고 주의할점만 적어두겠다

package com.s3.s3test.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class S3config {

    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3Client amazonS3Client() {
        BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
        System.out.println(accessKey + " " + secretKey + " " + region);
        return (AmazonS3Client) AmazonS3ClientBuilder
                .standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
    }
}

s3설정

 

@RestController
@Slf4j
public class controller {
    @Autowired
    S3service service;

    @PutMapping("/custom")
    public String custom(@RequestParam(value = "upfile", required = false) MultipartFile[] files) throws IOException {
        return service.uploadOnS3( files[0]);

    }
}

컨트롤러

package com.s3.s3test.service;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.PutObjectResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
@Service
@Slf4j
public class S3service {
    @Value("${cloud.aws.s3.bucket}")
    private String bucket;
    
    private final AmazonS3 amazonS3;
    public S3service(AmazonS3 amazonS3) {
        this.amazonS3 = amazonS3;
    }

    private static String getUuid() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public String uploadOnS3(final MultipartFile file) throws IOException {
        String[] type = file.getOriginalFilename().split("[.]");
        String fileName = getUuid() +"." + type[type.length-1];
        log.info(fileName);
        try {
            ObjectMetadata om = new ObjectMetadata();
            om.setContentType(file.getContentType());
            PutObjectResult result = amazonS3.putObject(
                    new PutObjectRequest(bucket, fileName, file.getInputStream(), om)
            );
        } catch (Exception e){
            log.error(e.toString());
        }
        return fileName;
    }
}

사진을 올릴 때 s3에 올리고 접근을 하려니까 자꾸 다운로드페이지가 뜨는걸 확인했다. 

알아보니까 "applicatioin/octet-stream"으로 설정이 되는 문제라는걸 확인했고, 마침 ObjectMetadata에 contenttype을 설정해주는 부분이 있어서 이걸 활용했다 . 

ObjectMetadata.setContentType을 사용해서 file.getContentType()을 이용해 파일의 컨텐츠 타입을 그대로 사용하는것으로 해결했다. 

 

이렇게 하면 accessKey나 secretKey를 틀리면 접속이 거부되고, 정확안 키들을 적어야 s3에 올라가는것을 확인하고,

퍼블릭으로 객체 접근은 누구나 되는것을 확인했다. 

 

끝!