본문 바로가기

Public Cloud/AWS

AWS 자습 노트 - 7. PUT request를 이용한 S3에 Object upload (+ Web Server 구축)

반응형

들어가며

S3는 URL을 이용해서 Public Access 가 가능하고, 이런 특징을 이용해서 단순한 안내나 정보를 제공하기 위한 Static Web Server로 활용할 수 있다. AWS Management Console을 이용하면 간단하게 설정할 수 있는 내용이지만 여기서는 PUT request를 이용해서 Static Web Server에 필요한 HTML파일과 Content 파일을 Upload한 후에 WEB server를 설정하는 과정을 공유하려고 한다. 

 

Test WEB Content

index,html만 있으면 넘 간단해서 그래도 한 페이지 정도 더 넘어가게 만들고, 무려 Image Content도 포함시키는 정성도 들였다. (Error page만 있었으면 완벽했었을 것 같다.)

<HTML>
<BODY>
	<H1>Welcome! Browndwarf world</H1>
	<UL>
		<LI><A HREF="teide1.html">Teide-1</A></LI>
	</UL>
</BODY>
</HTML>
<index.html>

 

<HTML>
<BODY>
	<H1>Teide-1</H1>
	<div>
		<div><img src="Teige1.PNG" alt="Teide1"></div>
		<P></P>
		<div>Teide 1 was the first brown dwarf to be verified, in 1995 </div>
	</div>
	<P></P>
	<P></P>
	<A HREF="index.html">Back</A>
</BODY>
</HTML>
<teide1.html>

How to use PUT API

AWS과 Network이 분리된 Public 환경에서 PUT API를 통해 S3에 Object를 Upload 할 때는 아래와 같은 Syntax에 맞춰야 한다.

PUT {Local File Path}

HTTP 1.1 Host {Bucket URL}

Date {date}

Content-Type {content type}

Authorization: {authorization string}

 

  • Local File Path : Upload 하려는 File의 Local Path
  • Bucket URL : Bucket의 endpoint. 
  • date : <요일>, <일> <월> <년> <시>:<분>:<초> GMT
  • content type : File의 Content Type. application/octet-stream 으로 하면 대부분의 경우 Upload에 문제가 없다.
  • authorization string : AWS에서 허가된 사용자만 Upload할 수 있게 제한하기 위한 특정 형식의 string. 사용자 Key와 Seceret Key 값을 바탕으로 특정 공식에 의해 만들어야 한다. (참조)

다른 요소들은 직관적으로 어떤 값을 넣을 지 알 수 있었으나, 'authorization string'을 구하기 위해서는 Bucket에 접근이 허용된 Access Key/Password 정보가 필요하다. 이는 AWS의 IAM을 통해 설정한 후에 가져와야 한다. (AWS에 대한 지식이 없는 상태에서 애초 설정한 과제를 진행하다 보니 내용이 산으로 가고 있었다.)

IAM

IAM은 Identity and Access Management의 약자로 AWS에서 Authentication/Authorization을 관리 기능을 제공한다. 다른 서비스와는 다르게 Region이나 AZ(가용영역)을 초월해 계정 전체에 영향을 미친다. AWS Management Console로 들어가 IAM 으로 진입한 후 왼쪽 Menu에서 '사용자 추가'를 선택하면 <pic 1> 화면으로 진입한다. (여기서 사용자를 등록하고, 그 사용자에게 Bucket에 대한 Access 권한을 주고, 사용자 Key를 이용해서 PUT API로 Bucket에 Object를 Upload 하려고 한다.) <pic 1>은 사용자를 추가하는 과정의 1단계를 보여주고 있으며 여기서는 사용자 이름을 'browndwarf_access_s3'로 하고 프로그램적으로 접근할  때에만 Access를 허용하도록 설정했다. (이 사용자는 AWS Management Console을 사용할 수 없게 설정한 것이다.)

 

<pic 1. IAM 사용자 추가 시작 화면>

사용자 추가 다음 단계는 <pic 2>에서 보이는 권한 설정이다. 사용자에게 권한 설정하는 방식은 아래와 같이 3가지가 있고, 이 포스팅에서는 권한을 직접 설정하는 방식으로 진행했다.

 

  • 이미 권한이 설정되어 있는 그룹에 사용자를 추가함으로써 그룹에 설정된 권한을 상속 받는 방법
  • 기존 사용자와 동일한 권한을 복사하는 방법
  • 권한 직접 설정

권한에 대한 정책들 중에 AmazonS3FullAccess와 AmazonS3ReadOnlyAccess가 있는데 PUT으로 Object를 Write해야 하기 때문에 AmazonS3FullAccess 권한을 설정했다. (정책들이 너무 많기 때문에 정책 필터에 'S3'를 사용했다.)

 

<pic 2. IAM 사용자 추가 권한 설정 화면

그리고 Tag를 설정하는 3단계를 건너 뛰면 <pic 3>과 같이 사용자 추가 검토 단계가 나오고, 여기서 다음 단계로 넘어가면 <pic 4>와 같이 추가된 사용자 정보를 보여준다. <pic 4>에서 마지막 두 Field에 '액세스 키 ID'와 '비밀 액세스 키'가 나와있는데 앞에서 언급했던 'authorization string'를 계산하려면 이 값들이 필요하다. Console 상에서는 '비밀 액세스 키'가 가려져 있고, 위에 있는 '.csv 다운로드' 버튼을 눌러 내려받아야 한다.

 

<pic 3 추가할 사용자 검토 화면>
<pic 4 사용자 추가 결과 화면>

 

위의 과정을 모두 끝내고 사용자를 확인하면 <pic 5>과 같이 새로 추가한 사용자를 확인할 수 있다.

<pic 5. 사용자 List>

PUT Command 준비와 실행

다시 Local 머신으로 돌아와 위에서 다운로드 받은 파일에서 Access Key ID와 Secret access key를 확인해보자. (보안 자료이기 때문에 이 포스팅에서는 일부 내용을 가렸다.)

[root@localhost Downloads]# ll
total 11196
-rw-r--r--. 1 root root      205 Aug 30 15:30 credentials.csv
...
[root@localhost Downloads]# cat credentials.csv 
User name,Password,Access key ID,Secret access key,Console login link
browndwarf_access_s3,,AKIA************MC4G,Qc1EOdEw******************GVI7vX3Px5mE03,https://browndwarf.signin.aws.amazon.com/console

Bash shell에서 curl command와 PUT api 조합으로 S3 bucket에 Object를 Upload하기 위한 Code들은 구글링으로 쉽게 찾을 수 있고, 필자는 https://gist.github.com/ryantbrown/에서 아래와 같은 Code를 얻었다.

# Set AWS credentials and S3 paramters
AWS_KEY="AKIA************MC4G"
AWS_SECRET="Qc1EOdEw******************GVI7vX3Px5mE03"
S3_BUCKET="browndwarfbucket"
S3_BUCKET_PATH="/"
S3_ACL="x-amz-acl:public-read"

function s3Upload
{
  path=$1
  file=$2

  acl=${S3_ACL}
  bucket=${S3_BUCKET}
  bucket_path=${S3_BUCKET_PATH}

  date=$(date +"%a, %d %b %Y %T %z")
  content_type="application/octet-stream"
  sig_string="PUT\n\n$content_type\n$date\n$acl\n/$bucket$bucket_path$file"
  signature=$(echo -en "${sig_string}" | openssl sha1 -hmac "${AWS_SECRET}" -binary | base64)

  curl -X PUT -T "$path/$file" \
    -H "Host: $bucket.s3.amazonaws.com" \
    -H "Date: $date" \
    -H "Content-Type: $content_type" \
    -H "$acl" \
    -H "Authorization: AWS ${AWS_KEY}:$signature" \
    "https://$bucket.s3.amazonaws.com$bucket_path$file"
}

# set the path based on the first argument
path=$1

# loop through the path and upload the files
for file in "$path"/*; do
  s3Upload "$path" "${file##*/}" "/"
done

 

설정해야할 parameter는 5개 이며 필자의 환경에 맞게 수정했다.

 

  • AWS_KEY : 위에서 download 받은 csv파일에서 Access Key ID 값
  • AWS_SECRET : 위에서 download 받은 csv파일에서 Secret Access Key 값
  • S3_BUCKET : 필자가 만든 Bucket의 이름 ; browndwarfbucket 
  • S3_BUCKET_PATH : Object가 Upload될 S3 Bucket 내부 Path ; root path로 설정
  • S3_ACL : Upload한 Object 접근 정책(ACL)을 설정하는 값으로 private, public-read, public-read-write, aws-exec-read, authenticated-read, bucket-owner-read, bucket-owner-full-control 등으로 정의해야 하는데 Static Web Page로 활용하기 위해서 public-read로 설정하였다.

이렇게 Code를 수정한 후, Script를 실행 가능하게 변경 한 후 실행하였다. upload 결과를 Management Console로 확인해 보니 <pic 6>과 같이 문제 없이 이루어진 것을 확인할 수 있었다.

# s3put.sh를 실행 가능하도록 설정
[root@localhost s3put_example]# chmod 755 s3put.sh 
[root@localhost s3put_example]# ll
total 4
drwxr-xr-x. 2 root root  61 Sep 17 16:51 browndwarf
-rwxr-xr-x. 1 root root 995 Sep 17 16:41 s3put.sh
...
# Upload할 File 확인
[root@localhost s3put_example]# ll ./browndwarf
total 32
-rw-r--r--. 1 root root   124 Sep 17 16:50 index.html
-rw-r--r--. 1 root root   243 Sep 17 16:50 teide1.html
-rw-r--r--. 1 root root 23418 Sep 17 16:12 Teige1.PNG

# Upload 실행
[root@localhost s3put_example]# ./s3put.sh ./browndwarf
[root@localhost s3put_example]# 

<pic 6. Script 실행 결과>

WEB Server 설정

Upload된 파일로 static WEB server를 구축하려면 웹사이트 기본 설정을 해야하고 추가로 Bucket 권한 설정을 수정 해야 한다. 일단 Bucket에 진입한 후 속성 Tab으로 들어가면 <pic 7>과 같이 '정적 웹 사이트 호스팅' 항목이 있다. 이 항목을 클릭해서 보면 <pic 8>과 같이 Web Hosting을 위한 설정을 하게 되어 있다. 제일 중요한 항목은 Default로 보여지는 페이지를 설정하는 '인덱스 문서' 설정인데 upload한 index.html로 설정했다. (예제에서는 error.html은 준비하지 않았다.)

 

<pic 7. Bucket 속성 Tab>
<pic 8. 정적 웹사이트 호스팅 설정>

'정적 웹사이트 호스팅' 부분을 설정한 후에는 <pic 9>와 같이 '권한' tab으로 가서 버킷 정책을 설정해야 한다.

 

<pic 9. 버킷 정책 설정>

'버킷 정책' Tab으로 이동하면 Editor 창이 있고 거기에 아래와 같이 Json 형식으로 정책을 설정하면 된다. Web Hosting 목적을 위해 이 Bucket에 전달되는 Get Object에 대해 전체 허가하게 설정했다. (Editor 창 하단에 '정책생성기' button을 누르면 아래와 같은 Json data를 GUI를 통해 쉽게 얻을 수 있다.)

{
    "Version": "2012-10-17",
    "Id": "Policy1568757593761",
    "Statement": [
        {
            "Sid": "Stmt1568757590553",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::browndwarfbucket/*"
        }
    ]
}

 

Bug Fix

여기까지 구성하고 이 S3의 브라우져에서 end-point url을 접속하면 Web Page가 보여져야 하는데, 그렇게 동작하지 않고 index.html이 download 되는 현상이 있었다. 원인 분석을 하던 중 Stack Overflow에서 Object의 Meta Data가 잘못 설정했을 때 유사한 사례가 있었다는 내용을 보고, 각 Object의 Meta Data를 확인해보니 Content-type이 모두 'application/octet-stream'이었다. 원인은 위에서 사용한 Shell Script에서 Upload 할 때 Content Type을 그렇게 설정해서 PUT API를 실행했기 때문이다.

...
content_type="application/octet-stream"
...
    -H "Content-Type: $content_type" \
...

그래서 <pic 10>과 같이 Object들의 메타데이터 변경으로 진입해 기존에 application/octet-stream으로 설정되어 있던Content-Type 부분을 html 파일은 text/html로, png 파일은 image/png 파일로 변경했다.

 

<pic 10 Object 메타 데이터 수정>

최종 결과

위의 수정을 마치고 나니 <pic 11>과 같이 기본 Page 연결이 이루어졌고, index.html에서 연결되는 Sub Page도 별 문제 없이 연결됐다.

<pic 11. 사이트 접속시>

 

<pic 12. index.html에서 연결되는 Subpage>

To Do

여기까지 하고 두 가지 의문이 생겼다. 하나는 shell script에서 signature 만드는 부분이었는데 AWS 문서에서는 HTTP Header의 Authorization 부분의 값이 'AWS4'로 시작되는 것처럼 안내되어 있는데, 필자가 구한 Shell Script Code에서는 AWS로 시작하는 것이었다. 

...
    -H "Authorization: AWS ${AWS_KEY}:$signature" \
...

이 부분에 대해 찾아보니 'AWS'로 시작되는 인증 String은 Version 2에서 정의된 것이였고,2014년 1월 이후에는 Version 4로 운영된다고 했다. 그리고 2014년 이전에 만들어진 Region에 대해서는 Version 2가 지원이 되지만 그 이후에 만들어진 Region은 Version 4의 인증 방법만 가능하다고 언급했다. (참조) 추후에는 Version 4의 인증 방법을 지원해서 동일한 시도를 해보려고 한다.

 

또 다른 의문은 S3에 Vue, React와 같은 JS Framework로 만들어진 순수 Front-End Web Page가 동작할 것인가에 대한 의문인데 이 것도 나중에 한 번 시도해 보려고 한다.