S3 서비스
S3 서비스의 전체 이름은 Amazon Simple Storage Service이다. S가 세 개라서 S3로 부른다. 오브젝트 스토리지 서비스로 높은 내구성과 높은 가용성을 저렴한 가격으로 제공해주기 때문에 AWS 인프라를 사용하게 되면 로그 저장용이나 정적인 컨텐츠 보관용으로 꼭 사용하게 된다. S3의 내구성을 좀더 살펴보자면, S3가 제공하는 내구성은 99.999999999% 이다. 9개 무려 11개로 사실 이정도면 거의 서비스 다운이 없다고 보면 된다. (AWS의 모든 리전은 최소 3개의 AZ를 가지고 있는데 S3는 무조건 3개의 AZ를 전부 사용하고 있고, 물리적 복사본을 각 AZ당 2개 이상 가지고 있다.)
분산 구조라서 객체의 Create / Delete만 지원하고 Modify의 경우 overwrite(재생성)으로 진행된다. HTTP(S) 프로토콜을 사용하여 S3에 오브젝트 업로드/다운로드가 가능하다. 최종 일관성(Eventual Consistency)를 지원하기 때문에 Overwrite나 Delete의 경우 내부 복제가 완료되기까지 약간의 시간이 걸릴 수 있다.
버킷(Bucket)과 객체(Object)
S3 서비스에서 객체(Object)를 저장하기 위해서는 Bucket 이라는 걸 만들어야 한다. Bucket은 객체를 담는 바구니, 우리에게 익숙한 개념으로는 폴더라고 생각하면 된다. 다만 버킷은 Flat한 구조라서 버킷 내부적으로 하위구조(Hierachie)를 가질 수는 없지만, “/”를 구분자로 하여 논리적으로 하위구조를 가질 수 있게 해준다.(folder/object와 같은 형태)
버킷은 기본값이 private이다. public으로 열고 싶으면 S3 버킷 정책에 대해 살펴보아야 하는데 보안상 public으로 여는건 권고하지 않고 있다. 버킷명은 모든 AWS 계정, 모든 region에 걸쳐 전세계적으로 고유한 값이다. 버킷주소는 “버킷명.리전명.amazonaws.com”의 형태이다.
객체는 버킷에 저장되는 오브젝트로 최소 1Byte에서 최대 5TB까지 가능하다. 저장 가능한 객체의 갯수는 무제한이다. 역시 기본값은 private이고 객체의 주소는 고유한 형태로 버킷명+키값(오브젝트명)이다.
S3 접근 권한 없는 사용자가 private S3에 접근하기(pre-signed url 발행)
S3 버킷의 기본 정책은 private이다. public에서 접근해서 object를 다운받거나 업로드 해야 하는 경우 버킷 자체를 public으로 열기엔 부담스럽고, 그렇다고 pulblic에 있는 사용자에게 S3 접근권한을 일일히 부여하기도 번거롭다. 이럴 때 사용할 수 있는게 pre-signed url이다. S3 접근을 위한 임시 url로 일정 시간이 지나면 만료되어 public 접근이 불가능해진다.
1. S3 접근 권한이 없는 사용자가 S3 접근 권한을 가지고 있는 EC2에 HTTP(S)등의 API로 S3 접근 권한 URL을 요청한다.
2. EC2에서 S3로 pre-signed url 발행을 요청한다.
3. S3에서 pre-singed url 을 발행하여 EC2에 전달한다.
4. EC2는 url을 사용자에게 전달한다.
5. 사용자는 발행받은 url (pre-signed url)로 S3에 접근하여 업로드/다운로드를 수행한다.
실제로 어떻게 구현되는지 하나씩 살펴보겠다.
먼저 EC2에서 AWS Credential을 가지고 오는 부분은 전에 작성한 페이지로 대체한다. 요약하자면 EC2는 Role로 S3 접근권한을 가질 수 있다.
maven 기반 spring boot 소스로 구성하였다.
먼저 pom.xml 에 aws sdk를 추가한다.
...
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.11.920</version>
</dependency>
...
pre-singed url을 발행하는 소스는 아래와 같다. pre-signed url을 발행 할 때는 다음 네가지 설정을 해주어야한다.
- Bucket name : S3 버킷 이름
- Object key : 오브젝트 이름
- Method : REST API 중 허용하려는 Method (GET, POST, PUT 등)
- Expiration : 발행한 URL 유효시간(초단위)
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
@RestController
public class PreSignedUrlController {
private static final long ONE_HOUR = 1000 * 60 * 60; // 1시간;
@GetMapping(path="/s3url-upload")
public String GetS3UrlUpload(@RequestParam("objKey") String objectKey) {
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withRegion(S3BucketConstant.CLIENT_REGION)
.build();
// Generate the pre-signed URL.
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(S3BucketConstant.BUCKET_NAME, objectKey)
.withMethod(HttpMethod.PUT)
.withExpiration(this.getExpiration(ONE_HOUR));
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
return url.toString();
}
@GetMapping(path="/s3url-download")
public String GetS3UrlDownload(@RequestParam("objKey") String objectKey) throws IOException {
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withRegion(S3BucketConstant.CLIENT_REGION)
.build();
// Generate the presigned URL.
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(S3BucketConstant.BUCKET_NAME, objectKey)
.withMethod(HttpMethod.GET)
.withExpiration(this.getExpiration(ONE_HOUR));
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
return url.toString();
}
private Date getExpiration(long expirationAsMilliseconds) {
// Set the pre-signed URL to expire after milliseconds
Date expiration = new Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += expirationAsMilliseconds;
expiration.setTime(expTimeMillis);
return expiration;
}
}
이렇게 발행한 pre-singed url은 아래와 같은 정보를 포함하게 된다. 내가 구성한 정보와 맞지 않는 형태로 접근하는 경우 signature가 맞지 않는 다는 오류가 발생하게 된다.
매개변수 | 설명 |
X-Amz-Algorithm | 서명 버전과 알고리즘을 식별하고, 서명을 계산하는데 사용. 서명 버전 4를 위해서 “AWS4-HMAC-SHA256” 로 설정 |
X-Amz-Credential | 액세스 키 ID와 범위 정보(요청 날짜, 사용하는 리전, 서비스 명). 리전 명은 리전 및 엔드포인트에서 확인 가능 |
X-Amz-Date | 날짜는 ISO 8601형식. 예: 20160115T000000Z |
X-Amz-Expires | 미리 선언된 URL이 유효한 시간 주기. 초단위. 정수 값. 최소 1에서 최대 604800 (7일) 예: 86400 (24시간) |
X-Amz-SignedHeaders | 서명을 계산하기 위해 사용되어지는 헤더 목록. HTTP host 헤더가 요구됨 |
X-Amz-Signature | 요청을 인증하기 위한 서명 |
공식 AWS 가이드 링크
https://aws.amazon.com/ko/blogs/korea/aws-api-call-2-s3-pre-signed-url/
https://aws.amazon.com/ko/premiumsupport/knowledge-center/presigned-url-s3-bucket-expiration/
'AWS' 카테고리의 다른 글
AWS Access key 종류 (0) | 2023.01.03 |
---|---|
EC2 EBS 볼륨 증설(ubuntu os) (0) | 2023.01.03 |
EC2에서 access key 없이 role 활용하여 사용하기(Java aws sdk credential) (0) | 2023.01.03 |
EC2에 SSH Key 없이 Session Manager 활용하여 접속하기 (0) | 2023.01.03 |
Cloud watch에서 EC2 메모리 모니터링 (0) | 2023.01.03 |