이 문서는 구글에 널려 있는 서명된 URL 관련 가이드 메뉴얼을 따라할 시 작동되지 않았거나, 용어의 의미가 모호해서 어려움을 겪으신 분들을 위한 자료입니다.
구글에 서명된 URL(Signed URL)과 관련된 자료를 찾아보았을 때, 용어에 대해 제대로 이해하지 못한 채 작성된 정보, 잘못된 정보들이 꽤 많아서 그러한 가이드를 따라할 시 URL이 제대로 작동되지 않았습니다.
그래서 최대한 AWS 공식문서를 참조해 나름대로 Signed URL을 구현했고, 성공했습니다.
단시간(최소 몇 분) 동안만 유효한 URL을 사용하여 프라이빗 콘텐츠를 배포할 수 있습니다. 이때, 유효한 URL을 서명된 URL(Signed URL)이라고 합니다.
S3 버킷을 생성하고, 버킷 안에 간단한 index.html 파일을 업로드 합니다. 버킷 앞단에는 CloudFront를 붙이고, CloudFront의 서명된 URL을 이용해서만 버킷의 index.html에 접근할 수 있도록 합니다.
버킷을 생성합니다.

ACL은 비활성화하고,

퍼블릭 액세스를 허용합니다.

아래와 같은 내용의 index.html 파일을 버킷에 업로드합니다.
<h1>hello world!</h1>

리눅스 서버를 하나 생성합니다.
openssl 패키지를 설치합니다.
(이미 설치되어 있을 수도 있습니다)
% sudo yum install -y openssl
먼저 RSA 알고리즘을 이용한 private key를 생성합니다.
% openssl genrsa -out private_key.pem
그리고 private key를 이용해 public key를 생성합니다.
% openssl rsa -in private_key.pem -out public_key.pem -pubout
[CloudFront 콘솔] > [키 관리] > [퍼블릭 키] > [퍼블릭 키 생성]

생성한 퍼블릭 키 내용을 넣고 생성합니다.

[CloudFront 콘솔] > [키 관리] > [키 그룹] > [키 그룹 생성]

아까 생성한 퍼블릭 키를 선택하고 키 그룹을 생성합니다.

배포를 생성합니다.
원본 도메인은 이전에 생성한 S3 버킷을 선택하고, OAI를 사용하도록 한 뒤, 버킷에 대한 OAI를 생성합니다.
(OAI는 CloudFront를 통해서만 원본에 액세스할 수 있도록 제한합니다)

다른 값들은 기본값으로 하되, 뷰어 액세스 제한 항목을 주목합니다.
뷰어 액세스 제한을 Yes로 지정하고, 인증 유형을 이전에 생성한 키 그룹으로 지정합니다.

다른 값들은 모두 기본값으로 두고, 배포를 생성합니다.

다시 리눅스 서버로 돌아옵니다.
아래 내용의 policy라는 파일을 생성합니다.
% vim policy
{
"Statement": [
{
"Resource": "base URL or stream name",
"Condition": {
"DateLessThan": {
"AWS:EpochTime": ending date and time in Unix time format and UTC
}
}
}
]
}
Resource 속성에는 CloudFront의 도메인과 접근할 파일 경로를 입력합니다.
(프로토콜은 http 또는 https을 사용할 수 있고, 프로토콜에 따라 서명값이 다르게 나옵니다. 도메인 뒤에 접근 파일 경로까지 반드시 적어줘야 합니다)
AWS:EpochTime에는 서명된 URL이 만료될 시간을 입력합니다.
=> EpochTime 확인 사이트
{
"Statement": [
{
"Resource": "https://dip2vyhcaqkt6.cloudfront.net/index.html",
"Condition": {
"DateLessThan": {
"AWS:EpochTime": 1654909923
}
}
}
]
}
policy과 private key을 이용해 서명값을 생성합니다.
한 줄로 된 긴 서명값이 생성됩니다.
% cat policy | tr -d "\n" | tr -d " \t\n\r" | openssl sha1 -sign private_key.pem | openssl base64 -A | tr -- '+=/' '-_~'
ZaXiGaLrSdEj3RXp...YF3T4DBJA__
서명된 URL의 형식은 아래와 같습니다.
<policy에 적힌 resource 값>?Expires=<policy에 적힌 epochtime>&Signature=<생성한 서명값>&Key-Pair-Id=<Cloudfront 퍼블릭 키 ID>
<policy에 적힌 resource 값>와 <policy에 적힌 epochtime>는 policy 파일 내용에서 확인 가능합니다.
{
"Statement": [
{
"Resource": "https://dip2vyhcaqkt6.cloudfront.net/index.html",
"Condition": {
"DateLessThan": {
"AWS:EpochTime": 1654909923
}
}
}
]
}
<생성한 서명값>은 한 줄로 된 긴 값입니다.
ZaXiGaLrSdEj3RXp...YF3T4DBJA__
<Cloudfront 퍼블릭 키 ID>는 [CloudFront 콘솔] > [키 관리] > [퍼블릭 키]에서 확인 가능합니다.

결과적으로 생성된 서명된 URL은 아래와 같습니다.
https://dip2vyhcaqkt6.cloudfront.net/index.html?Expires=1654909923&Signature=ZaXiGaLrSdEj3RXpugEY0TPGlDPtSN44AHGMGPS~1zzicTdU-Hvb-Q2ZY0Nd3-UmEr0HbHtFHDI8HHPU0lbazfUngmX1BTpMSMr53sLdUJRXqxuPF~d-G-9-idGzBO686S1iR-WxisiduG1Se544UE7fNcg6RJOa6UrGyVmbMdbu4hv4Ebt5YxYkQZiDxqyBlPKxBvquny9Bw3OjmVmJNsGZUxdNdg-RlS-31gOLj1e7koI~ZfS1Wb1BVaPcra9WY2Qe1WehQSLjxy7o63vCi1mrzgLCaWuFXLuttRxmrl7fIONHytqfg-joyid8GwEZuR-PCF93tt87GYF3T4DBJA__&Key-Pair-Id=K36JF7FEPZS1C3
단순히 CloudFront 도메인을 이용해 접속하면 접속되지 않습니다.

생성한 서명된 URL을 이용하면 정상적으로 접속되는 것을 확인할 수 있습니다.

Expires 속성에 명시된 만료 EpochTime이 지나면 해당 서명된 URL은 만료됩니다.
Missing Key 에러가 나타난다면, Key-Pair-Id 속성값이 잘못되었다는 의미입니다.
CloudFront 콘솔의 퍼블릭 키 ID를 제대로 입력했는지 확인해보시기 바랍니다.
액세스 거부(403)는 여러가지 원인으로 발생할 수 있습니다.
EpochTime 불일치: policy 파일에 적힌 EpochTime과 동일한지 확인하세요.잘못된 서명값: policy파일에 적힌 http 프로토콜과 URL로 접근할 때 http 프로토콜이 같아야 합니다. 예를 들어, policy 파일의 Resource에는 http로 적어서 서명값을 만들고, 접근할 때 https 프로토콜을 사용한다면 접근이 제한됩니다.참고 사이트