cloudfront에 private content 적용하기

2022.10.29

tui editor doesn't support description!

cloudfront에서 컨텐츠 제한하기

클라우드프론트의 컨텐츠는 누구나 접근이 가능하다. 하지만 이미지나 동영상같은 특정 컨텐츠들을 유료 구독자들이나 아니면 인증이된 특정 사용자들에게만 보여주고 다른 사용자들의 접근은 제한 해야할 경우가 있다. 최근 직장 프로젝트 에서 우연히 이 문제를 겪게 되었다. 상황은 클라우드 프론트에서 제공되는 특정 이미지들이 관리자나 특정 유저들 한테만 보여야 하며 다른사람이 접근해서는 안돼는 조건이 필요한 경우였다. 처음엔 람다엣지로 뭔가를 처리할수있지 않을까 생각했는데 찾아보니 클라우드프론트 자체에 이런 기능이 있었다.

private content 제공 하기

공식문서에서는 private content 라고 부른다. 서명된 URL 이나 서명된 쿠키 가 있어야만 접근이 가능한 컨텐츠다. 이 기능을 사용하려면 먼저 사전 작업이 필요하다.


s3 컨텐츠 접근제한 하기


사용자가 클라우드프론트의 주소가 아닌 s3에 직접 접근하는걸 제한하고 오직 클라우드프론트에서만 컨텐츠를 제공하도록 설정해야한다.

s3 생성


먼저 버킷의 모든 퍼블릭 액세스 차단을 설정한다.


클라우드 프론트 생성

클라우드프론트 생성 그 다음 클라우드프론트 배포를 생성한다. 이때 클라우드프론트에서는 s3에 접근이 가능하도록 설정해야하기때문에 s3 버킷 액세스 메뉴에서 원본 액세스 제어 설정을 선택한다.


클라우드프론트 제어 설정


이름 칸에는 선택한 s3의 url이 자동으로 입력된다. 생성을 누르자.


s3 정책 변경

s3의 정책 편집기로 가서 아래의 정책을 적용해주자. 이 정책은 해당 s3에서 클라우드프론트의 getObject를 허용하는 정책이다.

{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "AllowCloudFrontServicePrincipalReadOnly",
        "Effect": "Allow",
        "Principal": {
            "Service": "cloudfront.amazonaws.com"
        },
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::{버킷 이름}/*",
        "Condition": {
            "StringEquals": {
                "AWS:SourceArn": "arn:aws:cloudfront::{AWS ID}:distribution/{클라우드 프론트 ID}"
            }
        }
    }
}

이때 {AWS ID} 는 s3와 클라우드프론트를 소유한 계정의 id 여야한다. 이렇게 정책을 적용하면 s3의 url에 사용자가 직접 접근할수 없고 클라우드프론트에서만 접근이 가능하다.


클라우드프론트에 서명자 추가하기

서명된 URL서명된 쿠키를 생성하려면 서명자가 필요하다. 서명자를 추가하면 클라우드프론트는 컨텐츠에 접근하려는 사용자에게 서명된 url 이나 쿠키를 요구한다. 서명자는 키 그룹 또는 AWS 계정 으로 지정할수있는데 AWS 계정 을 서명자로 사용할경우 루트 계정을 필요로 하기때문에 aws에서도 권장하지 않는 방법이다. 여기선 키 그룹을 사용하며 이 그룹안에 있는 키 페어로 URL 또는 쿠키를 생성한다.


서명자의 RSA 키 페어 생성하기

키 그룹 에는 public-private RSA 키 페어가 필요하다. 생성 방법은 여러가지가 있지만 aws 공식 문서에 나와있는 openSSL 을 이용한 생성방법은 다음과 같다.

  1. openSSL 명령어로 private_key.pem 파일을 생성한다.
    openssl genrsa -out private_key.pem 2048
    
  2. 생성된 private_key.pem 파일로 부터 public_key 를 추출한다.
    openssl rsa -pubout -in private_key.pem -out public_key.pem
    

클라우드프론트에 public key 및 키 그룹 등록하기


클라우드프론트 퍼블릭키1


생성한 public_key 를 클라우드프론트에 등록해준다.


클라우드프론트 퍼블릭키2

아 이름에 공백은 불가능하니 _ 로 바꿔주자. 그다음 바로 아래의 키 그룹 메뉴로 가서 아까 생성한 퍼블릭 키로 그룹을 생성해주자. 이건 간단한거니까 이미지 없어도 RG?


클라우드프론트 동작 수정

마지막으로 클라우드프론트 동작에 생성한 키 그룹을 적용시켜주자. 동작 설정

뷰어 액세스

뷰어 액세스 제한을 설정하고 나면 클라우드프론트는 이제 서명된URL 이나 쿠키가 없다면 아래와 같이 에러를 응답한다.

에러메세지

특정 경로에만 지정하고 싶을땐 어떻게 하나요?

액세스 제한 설정을 동작 마다 지정할수있다. 예를들면 /protect 경로에만 적용하고싶다면 /protect/* 경로인 동작을 생성하고 거기에만 적용시키면 된다.


서명된 URL 생성하기

이제 설정이 끝났고 컨텐츠를 보기 위해서는 서명된 URL 이나 쿠키가 있어야만 볼수있다. 이 문서에 url과 쿠키 차이점이 무엇이고 언제 쓰면 좋은지 설명되어 있으니 참조해보자. 이 예제에선 서명된 url을 사용할것이다 왜냐면 난 아직 서명된 쿠키를 써본적이 없으니까.


미리 준비된 정책과 사용자 지정 정책

url을 생성하기전 아직 한단계가 더 남았다. 바로 url에 적용할 정책을 지정하는것이다. 미리 준비된(canned) 정책 과 원하는대로 커스텀할수있는 사용자 지정 정책이있다.


정책 비교


설명이 참 불친절하다. 쉽게 말하자면 준비된 정책은 만료기간만 지정할수있고 사용자 지정 정책은 접근가능한 시작날짜,ip 대역,만료기간 까지 세세하게 지정할수가있다. 그럼 이제 공식문서에 있는 "정책을 사용해 url을 생성하는 방법"을 한번 보자.


url 생성1 url 생성2 url 생성3


喝


너무 걱정하진말자 저건 사실 서명된 url의 원리구조를 설명한것이다. 물론 저 방법에 따라 url을 직접 생성할수있지만 그럴필요가 있나. 우리는 aws-sdk에 있는 메소드를 사용하여 url을 생성하면 된다. 관심이 있다면(물론 없겠지만..) 여기서 읽어보자.


nodejs 에서 서명된 URL 생성

aws-sdk 에 AWS.CloudFront.Signer 라는 url 과 쿠키를 생성할수있는 클래스가 있다. 아래는 실제 url을 생성하는 예시 코드이다.


  • 미리 준비된 정책을 이용한 url 생성
    const AWS = require('aws-sdk');
    
    // 클라우드프론트에 적용했던 퍼블릭,프라이빗 키가 필요하다. 여기선 예시로 직접 작성했지만 프라이빗 키는 노출되면 안된다.
    const privateKey = `-----BEGIN RSA PRIVATE KEY-----
      XXXXXXXX
      XXXXXXXX
      XXXXXXXX
      XXXXXXXX
    -----END RSA PRIVATE KEY-----`
    
    const cloudFront = new AWS.CloudFront.Signer('여기에 퍼블릭 키', privateKey);
    
    cloudFront.getSignedUrl({
      url: 'https://여러분의 클라우드프론트 주소/파일경로',
      expires: Math.floor((new Date()).getTime() / 1000) + (60 * 60 * 1) // 생성한 시간(현재시간)부터 1시간까지
    }, (err, url) => {
      if (err) throw err;
      console.log(url);
    });
    

  • 사용자 지정 정책을 이용한 url 생성
    이 방법은 쪼금 더 복잡하다. 적용하고 싶은 정책을 직접 json 형태로 작성해야하기때문이다. 아래의 양식을 따라야 한다.

    {
    "Statement": [
        {
            "Resource": "https://*",
            "Condition": {
                "IpAddress": {    //허용 ip 범위 지정
                    "AWS:SourceIp": "192.0.2.10/32"
                },
                "DateGreaterThan": {    //지정 시간 이후부터 접근가능
                    "AWS:EpochTime": 1357034400
                },
                "DateLessThan": {       //만료 기간 설정
                    "AWS:EpochTime": 1357120800
                }
            }
        }
    ]
    }
    

    이 양식대로 사용자 지정 url 생성 방법은 이렇다.

    const AWS = require('aws-sdk');
    
    const policy = JSON.stringify({
    "Statement": [
        {
            "Resource": "https://적용할 주소",
            "Condition":{
                "DateGreaterThan": {    //현재 시간부터 1시간 이후부터
                    "AWS:EpochTime": Math.floor((new Date()).getTime() / 1000) + (60 * 60 * 1)
                },
                "DateLessThan": {       //현재 시간부터 2시간 이후까지 즉, 지금이 12시면 1시부터 2시 까지만 유효하다.
                    "AWS:EpochTime": Math.floor((new Date()).getTime() / 1000) + (60 * 60 * 2)
                }
            }
        }
    ]
    );
    
    const cloudFront = new AWS.CloudFront.Signer(publickey, privateKey);
    const url=await cloudFront.getSignedUrl({policy})    //이 함수의 파라미터 `policy` 로 전달한거다 반드시 문자열로 줘야한다!
    

    다른 예제를 보고싶다면 여기를 참고해보자


이제 끝났어요

이렇게 클라우드프론트에 private content 적용이 끝났다. 이런기능이 있는지도 몰랐는데 알고보니 상당히 유용한거 같다. 이걸 공부하면서 aws 문서를 여러번 읽었지만 설명이 참 어렵고 부실했다. 특히 aws 문서에 나와있던 nodejs 에서 url 생성하는 예제는 너무 오래된 문서라 신뢰가 안가서 다른방법을 찾다가 우연히 찾은 블로그에서 aws-sdk 를 이용한 생성 방법을 찾았었다. 그 포스트도 이 포스팅처럼 private content를 설정하는 내용이었는데 미리 준비된 정책사용자 지정 정책 을 이해하는데 도움이 많이 되었다. 링크는 아래의 참조에 걸어두겠다.

reference


Do you want something exciting?

© 2022. YSH All rights reserved.