ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [AWS S3] S3 호환 타 클라우드 플랫폼의 presigned URL 생성하기
    DEVELOP 2023. 1. 23. 22:47

     

    AWS가 아닌 타 클라우드 플랫폼에서 S3 호환 API를 이용할 때, presigned url을 생성하는 방법에 대한 자료가 많이 없어서 처음에 엄청나게 헤맸다.

    나 다음의 어느 누군가는 헤매지 않길 바라면서... 해결과정을 블로그에 기록해둔다.

     

    Presigned URL이란?

    presigned url이란 미리 서명된 url로, 해당 url로 접속 시 일정기간동안 인증정보가 없어도 오브젝트 스토리지에 접근할 수 있게 해준다.

    이 개념이 필요한 이유는 보안 때문인데, 클라이언트에 시크릿 키처럼 공개되면 안되는 정보들이 노출되는 위험을 방지하기 위해 사용한다.

     

     

    클라이언트가 서버에 presigned url을 받기위한 요청을 날리고, url을 받으면 그 때 그 url로 직접 파일 업로드/다운로드를 하는 개념이다.

    따라서 퍼포먼스적인 측면에서도, 클라이언트가 직접 스토리지에 접근하게되므로 서버의 리소스 사용을 줄일 수 있다는 장점이 있다.

     


    AWS SDK V2?

    AWS 문서에 presigned url을 생성하는 예제 코드가 있다.

    https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/PresignedUrlUploadObject.html

     

    미리 서명된 URL을 생성하여 객체 업로드 - Amazon Simple Storage Service

    미리 서명된 URL을 생성하여 객체 업로드 미리 서명된 URL의 생성자가 해당 객체에 대한 액세스 권한을 보유할 경우, 미리 서명된 URL은 URL에서 식별된 객체에 대한 액세스 권한을 부여합니다. 즉,

    docs.aws.amazon.com

     

    PutObjectRequest -> PutObjectPresignRequest -> PresignedPutObjectRequest 순으로 객체를 만들고 마지막 객체에서 만들어진 url을 반환하는 방식이다.

    그러나 이 방식은 AWS의 오브젝트 스토리지를 사용할 때에만 작동한다. 이유는 이 코드로 생성된 url의 엔드포인트가 aws의 스토리지 엔드포인트로 반환되기 때문이다.

     

    따로 엔드포인트를 설정할 수 있는 메소드가 분명 있을거라 생각했고, 리퀘스트 빌더에 .endpointOverride() 메소드가 있어서 여기에 타 클라우드 플랫폼의 엔드포인트 URI를 넣어주었지만, 이런 목적에 사용하는 메소드가 아닌지 런타임 익셉션을 내뱉었다.

     

    여기서 정말 많은 시간을 할애했는데, 결국 SDK V1을 사용하여 해결했다.

     


     

    AWS SDK V1

     

    build.gradle에 의존성 추가

    implementation 'com.amazonaws:aws-java-sdk-s3:${Version}'

     

    위 sdkv2버전의 패키지명은 software.amazon.awssdk로 시작하고, v1 버전은 com.amazonaws로 시작한다.

    v1버전에서 제공하는 클라이언트 빌더를 사용하면 타 클라우드 플랫폼과 호환되는 객체를 생성할 수 있다.

     

    val client = AmazonS3ClientBuilder
        .standard()
        .withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration("스토리지 엔드포인트", "kr-standard"))
        .withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials("accessId", "accessSecret")))
        .enablePathStyleAccess()
        .build()

    .withEndpointConfiguration에 사용하는 클라우드 스토리지의 엔드포인트와 리전을 문자열로 넣어준다.

     

    리전은 보통 해당 클라우드의 사용가이드에 명시하고 있으니, 찾아보고 맞게 기입하면 된다. 나와있지 않은 경우, 국내 플랫폼은  "kr-standard"를 많이 사용하는 것 같다.

     

    credentials에 발급받은 accesskey와 secret을 입력한다.

     

    .enablePathStyleAccess()

    이 옵션이 조금 중요한데, 만약 사용하는 클라우드 플랫폼에서 virtual host형식 URL을 지원하는지 여부에 따라 해당 옵션을 추가할지 결정해야한다.

    GET https://{endpoint}/{bucket-name}/{object-name} # path style
    ex) https://example.storage.com/bucket-name/sample-object
    GET https://{bucket-name}.{endpoint}/{object-name} # virtual host style
    ex) https://bucket-name.example.storage.com/sample-object

    path 스타일과 virtual host 스타일은 다음과 같이 약간의 차이가 있다. 보통은 virtual host 스타일을 지원하는데, 간혹 지원하지 않는 플랫폼이 있기 때문에, 만약 이 옵션을 빼고 진행했다가 ENOTFOUND(endpoint not found)에러가 발생한다면 추가하면 된다.

     

     

    Presigned PUT URL

    val presignedUrlRequest = GeneratePresignedUrlRequest(
        conf.bucketName,
        objectKey,
        HttpMethod.PUT
    )
    val url = client.generatePresignedUrl(presignedUrlRequest)
    
    return url

    파일 업로드 URL을 생성하기 위해 GeneratePresignedUrlRequest객체를 생성한다.

    버킷네임, 오브젝트키, HTTP 메서드 세가지 매개변수가 들어가고,

    만든 리퀘스트 객체를 클라이언트의 generatePresignedUrl 메소드의 매개변수로 넣어주면 된다.

    리퀘스트 객체를 생성하지 않고 바로 generatePresignedUrl에 버킷네임과 오브젝트키만 넣어주어도 url 생성이 가능하다.

    따로 생성한 이유는 http 메서드를 지정할 수 있기 때문이다.

    이렇게 하면 업로드와 다운로드 url을 구분해서 생성할 수 있다.

     

     

    Presigned GET URL

    val presignedUrlRequest = GeneratePresignedUrlRequest(
        conf.bucketName,
        objectKey,
        HttpMethod.GET
    )
    val url = client.generatePresignedUrl(presignedUrlRequest)
    
    return url

    다운로드 URL도 동일하고, http메서드만 GET으로 바꿔주었다.

     

     


     

    postman으로 테스트시, 반환받은 url로 직접 업로드, 다운로드가 정상 수행되는 것을 확인할 수 있다.

     

    상세정보 모자이크 처리

     

    ^ㅠ^

     

    서버에서 반환받은 url로 get 요청시 이미지가 정상 로드된다.

     

     

    만약 동일한 url이어도 유효기간이 지난 후에 요청을 보내면 거부된다.

     

    url에는 다음과 같은 정보들이 쿼리 파라미터로 제공되는데, aws signature 4버전 서명에 사용되는 파라미터들이다.

    각 파라미터에 대한 상세설명은 다음 링크에서 확인할 수 있다.

    (참고로 4버전 이전의 서명버전들은 deprecated되었다고 한다.)

    https://docs.aws.amazon.com/AmazonECR/latest/APIReference/CommonParameters.html

     

    Common Parameters - Amazon Elastic Container Registry

    Common Parameters The following list contains the parameters that all actions use for signing Signature Version 4 requests with a query string. Any action-specific parameters are listed in the topic for that action. For more information about Signature Ver

    docs.aws.amazon.com

     

     

    다음은 전체 샘플 코드이다. 언어는 코틀린으로 작성했다.

    블로그 글 작성을 위해 핵심 부분만 작성했으니, 상황에 맞게 응용하면 된다.

    val client = AmazonS3ClientBuilder
        .standard()
        .withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration("스토리지 엔드포인트", "kr-standard"))
        .withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials("accessId", "accessSecret")))
    //    .enablePathStyleAccess() 		virtual host style 미지원시 추가
        .build()
    
    fun presignPut(bucketName: String, objectKey: String) {
        val presignedUrlRequest = GeneratePresignedUrlRequest(
            bucketName,
            objectKey,
            HttpMethod.PUT
        )
        val url = client.generatePresignedUrl(presignedUrlRequest)
        
        return url
    }
    
    fun presignGet(bucketName: String, objectKey: String) {
        val presignedUrlRequest = GeneratePresignedUrlRequest(
            bucketName,
            objectKey,
            HttpMethod.GET
        )
        val url = client.generatePresignedUrl(presignedUrlRequest)
    
        return url
    }

     

    댓글

Designed by Tistory.