DynamoDB에 대해서 내용은 이 링크를 꼭 한번 읽어보시길 권유 드립니다.

 

이 예제에서는 1:1 모델링에 적합한, 전달될 유저 ID를 분할키로 사용하고, info라는 키에 name과 phone 정보를 담아보겠습니다. 

 

DynamoDB에서 "test_db"라는 이름으로 테이블을 먼저 생성해 보겠습니다. 그리고 분할키의 이름은 "key"로 지정하겠습니다. 분할키에는 전달될 유저 ID를 지정하겠습니다.

 

정렬키도 별도로 추가하지 않겠습니다. 생성이 완료되었으면 바로 람다 함수를 추가해 보겠습니다. 

함수명을 "save"라는 이름으로 생성합니다. aws-sdk 모듈은 바로 사용 가능해서, 아래 코드를 붙여서 바로 테스트 가능합니다.

var AWS = require("aws-sdk");
AWS.config.update({
    region: 'ap-northeast-2',
    endpoint: "http://dynamodb.ap-northeast-2.amazonaws.com"
})

const dynamo = new AWS.DynamoDB.DocumentClient();

exports.handler = (event, context, callback) => {
    const params = {
        TableName: 'test_db',
        Item : {
            "key" : event.id,
            "info" : event.info
        }
    };

    dynamo.put(params, callback);
};

이 람다 함수가 DynamoDB에 쓰기를 하려면 해당 권한이 필요합니다. FullAccess를 설정하기 보다는 쓰기 권한만 있는 정책을 만듭니다. IAM으로 가서 "역할 만들기"로 이동하여 "정책 생성"을 선택합니다.

서비스는 DynamoDB를 선택, 권한은 DeleteItem, PutItem, UpdateItem 만 설정합니다. 그리고 접근을 허용할 리소스는 생성한 DynamoDB의 arn을 복사해서 삽입합니다.

"rule_lambda_write_item" 이라는 이름으로 정책을 저장하고, 이 정책을 적용한 역할 "role_lambda_write_item"을 생성합니다.

이제 람다 함수로 돌아와서 역할을 설정해 줍니다. 

 

그리고 아래처럼 테스트를 설정 후 실행해 봅니다.

이후 DynamoDB "항목"탭에 가면 아래와 같이 입력된 정보를 확인할 수 있습니다.

 

마지막으로 생성한 람다 함수를 API Gateway와 연결해 보겠습니다. 

 

API Gateway에 대한 상세한 절차는 아래 링크를 참고해 주세요. 여기서는 간략히 기술하겠습니다. 

( 서버리스 서비스 예제 - 01 [ API Gateway - Lambda-ElastiCache(Redis) ] )

 

 

API Gateway로 이동해서 MyStore라는 이름의 API를 생성합니다.

"put-info"라는 이름으로 리소스를 생성하고 메소드는 "POST"를 선택 후 Lambda 함수는 "save"라고 명명한 함수를 선택하자. 이후 API를 prod라는 스테이지 이름으로 명명 후 완료하면 접근 가능한 URL이 부여됩니다.

 

이제 모든 설정이 마무리 되었습니다.  curl로 아래와 같이 테스트를 해보시기 바랍니다.

curl -L -v -d "{\"id\": \"test_id101\", \"info\" : {\"name\":\"console\", \"phone\":\"111) 1234-5678\"}}" -H "Accept: application/json" -H "Content-Type: application/json" path_your_endpoint

정상적으로 완료되었다는 메세지를 확인했다면 DynamoDB에서 결과를 확인할 수 있습니다. 

 

 

이상입니다. 여러분들도 문제없이 잘 진행되셨길 바랍니다. 질문이 있다면 댓글로 달아주세요.

최근 본격적으로 AWS에 서비스를 구축하면서 자잘하게 부딪혔던 작은 문제들을 정리하는 김에 저처럼 처음 접해보시는 분들께 도움이 되길 바라며 공개해 봅니다.

 

앞으로 몇 번에 걸쳐 간단한 샘플을 만들어보면서 실제 서비스 인프라를 구축하기 위해 어떤 과정들이 필요한지 하나씩 정리해 보겠습니다.

 

이번에 제작할 샘플은 REST API를 통해 Redis에 정보를 저장하는 과정을 만들어 보겠습니다. 

 

서비스는 아래와 같이 구성해 보겠습니다.

 

 

작업은 크게 네 단계로 진행합니다. 

 

1. VPC 구성하기

2. Redis 구성하기

3. Lambda 함수 제작

4. API Gateway 구성하기

 

따라하기 식으로 구성했으니 편하게 진행해 보시기 바랍니다. 바로 시작해 보겠습니다. 

 

1. VPC(Virtual Private Cloud) 구성하기

중요한 정보를 저장하는 저장소이므로 VPC를 구성하여 외부의 무분별한 접근을 차단하도록 하겠습니다. (2013년부터 AWS에 구축되는 모든 인프라는 VPC안에 구축되도록 강제되고 있습니다. 필수 과정이니 가능한 상세하게 짚고 넘어가도록 하겠습니다.) 이 과정에서는 VPC를 구성하고 서브넷(VPC 내부에서 서비스 목적에 따라 IP 범위를 나누어 구분하기 위한 설정), 보안 설정을 하게 됩니다. Redis를 외부에서 직접 접근하지 못하게 막아야 하므로 Public IP나 Elastic IP를 사용하지 않고 Private IP로만 설정하겠습니다. (public ip를 할당할 수는 없습니다. ^^a)

 

1-1. VPC 생성하기

마법사로 생성하지 않고 필요한 과정 전부를 직접 만들면서 진행하겠습니다.

 

우선 VPC 탭에서 "VPC 생성" 버튼을 누릅니다. "이름 태크"에는 "test-vpc"로 명명하겠습니다. 

IPv4 CIDR(Classless Inter-Domain Routing) 블록에는 VPC 안에서 사용할 주소 범위를 지정해야 합니다. 세 개의 subnet으로 관리해야 한다는 가정으로 설정하겠습니다. "192.168.0.0/16"으로 입력합니다. IPv6 블록은 없음으로 하겠습니다.

 

VPC와 함께 기본적으로 "라우팅 테이블"(Routing Table-라우터가 이를 사용해 최적의 경로를 결정하고 패킷을 전송합니다.), "네트워크 ACL"(Access Control List-오고 가는 패킷을 분석하여 정해진 규칙에 따라 패킷을 전송/차단합니다.)에 새로 생성한 VPC를 위한 항목이 자동 생성됩니다. 

 

1-2. 서브넷 설정

다음으로 서브넷(Subnet-네트워크 영역을 분리)을 생성해 보겠습니다.

 

DB, Service, Management 영역으로 구분하겠습니다. 그럼 아래와 같은 구조를 가지게 될겁니다. 

 

[db 영역]

CIDR 블럭 : 192.168.10.0/24

가용 ip 대역 : 192.168.10.4 ~ 192.168.10.253 (251개)

 

[Service 영역]

CIDR 블럭 : 192.168.20.0/24

가용 ip 대역 : 192.168.20.4 ~ 192.168.20.253(251개)

 

[Management 영역]

CIDR 블럭 : 192.168.30.0/24

가용 ip 대역 : 192.168.30.4 ~ 192.168.30.253(251개)

 

더보기

AWS는 가용 ip 대역 중 처음 4개 IP주소와 마지막 1개 IP주소를 예약하여 사용합니다.

첫 번째 ip는 subnet id, 마지막 한 개는 broadcast addr, 그리고 두 번째에서 네 번째 ip는 NAT Gateway로 활용됩니다.

(이에 대해서는 https://aws.amazon.com/ko/vpc/faqs/ 에서 찾을 수 있었으며, 5개의 ip는 AWS에의해 예약되었기 때문에, 사용자가 사용할 수 없다고 나와 있으나 각각이 정확히 무엇에 활용되는지에 대한 내용은 찾을 수 없었습니다. 알게 되면 글을 수정해 두겠습니다.)

 

서브넷 영역을 어떻게 사용할지 계획을 세웠으니, 실제로 생성해 보겠습니다.

서브넷 생성화 화면 에서 "이름 태크"에는 영역별로 알맞은 이름을 넣어주세요. "test-vpc-subnet-"을 prefix로 두고 영역별 이름을 db, service, management로 명명해 보겠습니다. 

 

IPv4 CIDR 블럭은 위에 정리했던 내용대로 입력하시면 됩니다. 가용 영역을 선택하지 않으면 AWS가 알아서 설정하게 됩니다.

 

이제 서브넷 설정을 모두 마치면 아래와 같은 결과를 보실 수 있습니다. 

 

 

2. Redis 구성하기

이번에는 Redis를 구성하기 위해서 ElastiCache로 가보겠습니다. 확장성과 안정성을 고려해 Cluster 모드로 설정하겠으나 테스트 용도이니 가장 저렴한 노드로 구성하겠습니다.

 

2-1. Redis 생성하기

 

이름을 "test-redis"로 입력하고 노드 유형을 "cache.t3.small"로 지정하겠습니다. 샤드 당 복제본도 기본설정 2에서 1로 조정했습니다. 

 

그리고 서브넷 그룹에서 "새로 생성"을 선택하고, test-vpc를 위한 그룹을 "test-vpc-group"로 생성합니다. 서브넷은 위에서 "test-vpc-subnet-db" 라고 명명한 서브넷을 선택합니다.

 

나머지는 기본 값으로 두고 생성을 완료합니다. 생성 완료까지는 약 5분 ~ 10분 정도 소요됩니다. 

 

2-2. redis-cli로 Redis 접속해보기

현재 생성된 Redis는 VPC안에 생성되어 있으며, 설정된 보안 그룹에서 inbound에 예외처리를 하지 않아 직접 접근할 수 없는 상태입니다. 또한 RDS와 달리 ElastiCache는 AWS 외부에서 접속 할 수 없도록 되어 있습니다. 이를 위해 ElastiCache-Redis가 설정된 VPC 내부에 EC2를 추가하여 접근해 Redis로 접속할 수 있습니다. 

 

그럼 EC2를 생성, 원격접속하여 redis-cli를 설치 후 Redis로 접속을 시도해 보겠습니다.

 

EC2 생성 과정은 간단히 텍스트로 정리하겠습니다. 

 

1) "Amazon Linux 2 AMI (HVM), SSD Volume Type" 선택

2) "t2.micro" 선택 

3) 인스턴스 구성에서 ..

   3-1. 네트웍크에서 test-vpc 선택

   3-2. 서브넷은 test-vpc-subnet-management 선택

4) 스토리지 추가 , 태그 추가 - 기본 설정

5) 보안 그룹 구성 -> 기존 보안 그룹 선택 -> test-vpc 보안 그룹 선택

6) "검토 및 시작" -> "시작하기" 버튼 선택

7) 기존 키 페어 선택 또는 새 키 페어 생성 -> "새 키 페어 생성" -> "키 페어 이름"에 "test-vpc-key-pair" 입력 -> "키 페어 다운로드" 선택 (다운로드된 키 페어는 EC2에 원격접속할 때 사용됩니다.)

8) "인스턴스 시작" 선택

 

이제 EC2로 접속하여 Redis로 연결해보겠습니다. 외부에서의 접속을 위해 public ip와 라우팅 설정을 추가하겠습니다. 

 

우선 생성된 인스턴스 상태를 살펴보겠습니다. 

 

현재는 public dns가 설정되지 않은 걸 확인 할 수 있습니다.

 

이제 VPC로 다시 가서 "인터넷 게이트웨이"를 선택 후 "인터넷 게이트웨이 생성"을 누르고 생성을 시도합니다. name tag에는 "igw-test-vpc-management"라고 명명합니다. 생성 직후에는 상태가 "detached"로 되어 있습니다. 이제 생성한 igw를 선택 후 "작업" -> "VPC에 연결"을 선택합니다. 그리고 "test-vpc"에 연결합니다. 

 

그리고 EC2 화면에서 생성한 인스턴스를 선택 후 하단 "설명"탭의 "서브넷 ID"를 선택하면 VPC 화면으로 전환됩니다. 이후 하단의 "라우팅 테이블"탭을 선택하고 하단의 "서브넷 연결"을 선택, "서브넷 연결 편집"을 선택 후 "test-vpc-subnet-management" 서브넷을 선택 후 "저장"하세요. 그리고 "작업" 버튼에서 "라우팅 편집"을 선택하고 "라우팅 추가" 버튼을 눌러 라우팅 정보를 추가합니다. 

각 대상에는 "0.0.0.0/0"를 입력하고 "igw-test-vpc-management"를 찾아 선택합니다. 그리고 다시 인스턴스 설정 화면을 돌아옵니다.

 

다음으로 외부에서 접속할 수 있는 public ip를 elastic ip 탭에서 할당해 보겠습니다.

 

"탄력적 IP"라는 메뉴를 선택한 후 "탄력적 IP 주소 할당" 버튼을 선택하여 퍼블릭 주소를 하나 할당합니다. 그리고 "Actions" 버튼을 눌러 "탄력적 IP 주소 연결"을 선택한 후 생성한 인스턴스에 연결해 줍니다.

다시 인스턴스 정보 화면으로 넘어가면 생성한 IP가 public ip에 세팅된 것을 확인할 수 있습니다. 혹시 "퍼블릭 DNS"에 할당된 정보가 없다면 VPC에서 "DNS 호스트 이름"을 활성화하여 저장하면 됩니다.

마지막으로 보안 그룹에서 SSH 포트(22)에 대한 인바운드 규칙을 추가하겠습니다.

 

이제 SSH를 사용하여 인스턴스에 연결해 보겠습니다.

 

위에서 다운받은 ppm(Privacy Enhanced Mail)파일을 그대로 사용할 수 있으며, putty 혹은 ssh client에서 사용하려면 ppk(Putty Privite key) 파일로 변경해야 하는데 이에 대해서는 여기서 상세히 다루지는 않겠습니다.

ssh -i your_pem_file ec2-user@ec2_public_dns 형태로 접속을 시도하면 됩니다. 

 

더보기

"WARNING: UNPROTECTED PRIVATE KEY FILE!" 에러가 발생한 적이 있는데 windows에서는 이상하게 다운받은 c:\Users\내계정명\Downloads에서는 문제 없는데, 다른 드라이브로 옮겨서 테스트 했을 때는 이 에러가 발생했었습니다. 에러 내용은 해당 파일에 접근할 수 있는 권한이 너무 많이 열려 있다는 내용인데, 저와 같은 현상이 발생하는건 애매하네요 -_-a

 

이제 redis-cli를 설치해서 접속해 보겠습니다.

 

우선 gcc가 없다면 먼저 설치해 주세요.

sudo yum install -y gcc

그리고 redis를 다운받아 설치합니다. 

sudo wget http://download.redis.io/redis-stable.tar.gz

sudo tar xvzf redis-stable.tar.gz 

cd redis-stable 

make distclean; make MALLOC=libc; make

sudo cp src/redis-cli /usr/bin/

생성된 Redis를 선택해서 아래 정보를 보시면 기본 엔드포인트가 비어 있는 걸 확인할 수 있습니다.

이는 위에서 클러스터 모드로 설정했기 때문인데, 클러스터 이름을 선택하여 아무 샤드나 선택하여 들어간 후 내부 노드 하나를 선택하면 엔드포인트가 있습니다. (클러스터 모드가 아닌 경우 기본 엔드포인트를 바로 확인할 수 있습니다.)

 

이 엔드포인트를 복사해서 아래와 같이 접속하면 정상적으로 접근 가능합니다. 

redis-cli -h your_redis_node_endpoint

이제 저장소에 대한 설정이 마무리되었습니다. 다음으로는 서비스 영역을 제작할 차례입니다.

3. Lambda 함수 제작

Redis Cluster에 점수를 기록하는 함수를 제작해 보겠습니다.

 

우선 nodejs를 사용해 setScore라는 이름의 람다 함수를 하나 만듭니다.

 

ioredis module이 필요하기 때문에 실제 필요한 코드와 module은 zip으로 압축해서 올려야 합니다. 로컬에서 폴더를 만들어 모듈을 설치하고, index.js 파일에 아래 소스를 붙여 넣으세요.

 

npm install ioredis
// index.js

'use strict';
var Redis = require('ioredis');

exports.handler = (event, context, callback) => {
    var client = new Redis.Cluster([
        { 
            host: "your_redis_endpoint_1",	
            port: 6379
        },
        { 
            host: "your_redis_endpoint_2",
            port: 6379
        }
    ]);

   var key = event.id;
   var score = event.score;
   client.set(key, {"score": score}, (err, ret) => {
        if(err)
            console.info("err = ", err);
        else
            console.info("ret = ", ret);
        client.quit(()=> {
            callback(err, {body:  "Result : " + ret});    
        });
    })  
}

그리고 해당 폴더의 내용을 zip으로 압축 후 "업로드", 그리고 해당 함수를 저장합니다.

redis 주소는 위에서 생성한 세 노드 모두 입력해 두시면, 추후 특정 노드에서 문제 발생 시 별도 처리 없이 즉각 대응 가능합니다. 아래와 같이 설정된 redis 주소를 입력해 두시기 바랍니다. 

이제 이 함수가 Redis가 있는 VPC에 접근할 수 있도록 허용해야 합니다.

 

IAM에서 "역할" -> "역할 만들기"를 선택합니다.

그리고 Lambda를 선택 후 "AWSLambdaVPCAccessExecutionRole"역할을 선택합니다.

그리고 역할 이름을 "lambda-vpc-execution-role"로 입력하여 할당한 후 람다 함수로 돌아가 하단의 "실행 역할"에서 "lambda-vpc-execution-role"를 선택합니다. 

 

그리고 하단의 VPC 항목으로 가서 "test-vpc"를 선택하고 서브넷 항목에 db, management 서브넷을 선택합니다. 보안 그룹은 default를 선택한 후 람다 함수를 저장합니다.

이제 잘 작동하는지 테스트 해보겠습니다.

위와 같이 설정 후 테스트를 시도하면 아래와 결과를 확인할 수 있습니다.

ssh로 접속해서 잘 입력되었는지 확인 해보세요. :)

 

4. API Gateway 구성하기

REST API로 선택, "새 API" 선택, API 이름에는 "leaderboard"라는 이름으로 입력하겠습니다.

 

"leaderboard"API 화면에서 리소스를 생성하고, 리소스 이름은 "put-score"로 지정하세요.

 

그리고 메서드를 "POST"로 생성합니다.

그리고 "Lambda 함수"를 선택 후 위에서 생성한 "setScore" 람다 함수를 지정합니다.

 

이제 생성한 리소스를 배포할 차례입니다. "작업" 버튼을 눌러 "API 배포"를 선택하고 "배포 스테이지"에서 "새 스테이지"를  선택 후 "스테이지 이름"에는 "prod"라고 입력합니다. 

이제 스테이지로 가면 호출할 수 있는 URL을 확인 할 수 있습니다. curl을 이용해서 정상 작동하는지 확인해 보겠습니다.

curl -L -v -d "{\"id\": \"test_id2\", \"score\" : 1000}" -H "Accept: application/json" -H "Content-Type: application/json" path_your_url

 

수고하셨습니다. 최종 테스트를 한번에 잘 완료되셨길 바랍니다. 

 

"따라하기" 처럼 해볼 수 있도록 정리해 봤는데 도움이 되었나 모르겠네요. 어쨌든 나중에 까먹으면 다시 보려고 정리를 시작한 글인데 생각보다 꽤 오래 걸렸네요. 

 

무사히 잘 마치셨길 바라며 다음에는 DynamoDB 사용하면서 정리한 글로 다시 뵙겠습니다. :)

 

 

 

 

( Windows 10 / VisualStudio 2017 환경에서 테스트 되었습니다. )

 

우선 AWS Console에서 필요한 계정과 SQS 설정을 해보겠습니다. 이미 계정 생성과 SQS 설정이 되어 있다면 다음으로 건너띄시기 바랍니다. 

 

여기서는 sqs_tester로 만들어 보겠습니다.

 

권한은 쓰기, 읽기를 분리해도 되고, 테스트를 위해 생성한다면 AmazonSQSFullAccess로 적용해도 괜찮습니다.

이제 SQS를 세팅해 보겠습니다.

FIFO 대기열로 생성해 보겠습니다. 구성은 기본 상태로 진행하겠습니다. 아.. 대기열 이름 끝은 ".fifo"로 끝내야 하니 주의하세요. 그리고 초당 300개의 메세지를 처리할 수 있다고 하니 실제 사용하실 때는 이런 제한사항을 꼭 확인 후 사용하시기 바랍니다.

 

큐가 생성되었다면 이제 접근을 위한 권한을 생성해 보겠습니다. 생성된 큐를 선택하고 하단에서 "권한"탭을 선택합니다.

프린시펄에서는 iam에서 생성한 사용자 ARN을 추가합니다. 그리고 하단의 작업에서 허용할 권한을 선택합니다.

 

아래에는 보내기 위해 필요한 권한과 메세지를 가져오는 측의 권한을 별도 계정에 할당한 결과입니다.

 

이제 AWS 설정 과정은 모두 마무리 되었습니다. 이제 실제 메세지를 보내는 cpp 코드를 만들어 보겠습니다.

 

우선 필요한 package를 NuGet으로 설치하세요.

 

필요한 패키지는 core와 sqs만 있으면 됩니다.

설치 시 자신의 프로젝트를 선택하면 lib include 에 대한 내용은 자동으로 세팅됩니다.

 

리눅스 계열에서는 aws cpp sdk를 git clone 혹은 별도로 다운받아 설치하시면 됩니다. cmake 시 BUILDONLY 옵션으로 core와 sqs만 지정해서 빌드하시는게 빠릅니다. 전체 설치했다가는 ... -_-a

 

이제 main.cpp를 프로젝트에 추가하고 아래 코드를 붙여 넣어보겠습니다. 

#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
#include <aws/sqs/SQSClient.h>
#include <aws/sqs/model/SendMessageRequest.h>
#include <aws/sqs/model/SendMessageResult.h>
#include <iostream>

int main(int argc, char** argv)
{
	Aws::SDKOptions options;
	Aws::InitAPI(options);

	Aws::Client::ClientConfiguration clientConfig;
	clientConfig.region = "ap-northeast-2";

	{
		Aws::String queue_url = "https://sqs.ap-northeast-2.amazonaws.com/111111111/sqs_test.fifo" ;
		Aws::String msg_body = "{\"key\":\"your_key\", \"value\":\"your_message\", \"ip\":\"162.158.186.95\"}";
		Aws::String msg_group_id = "msg_group_name_text";

		Aws::SQS::SQSClient sqs(Aws::Auth::AWSCredentials("your_access_key", "your_secret_key"), clientConfig);

		Aws::SQS::Model::SendMessageRequest sm_req;
		sm_req.SetQueueUrl(queue_url);
		sm_req.SetMessageGroupId(msg_group_id);
		sm_req.SetMessageBody(msg_body);
		sm_req.SetMessageDeduplicationId("unique_value_for_deduplication");	

		auto sm_out = sqs.SendMessage(sm_req);
		if (sm_out.IsSuccess())
		{
			std::cout << "Successfully sent message to " << queue_url <<
				std::endl;
		}
		else
		{
			std::cout << "Error sending message to " << queue_url << ": " <<
				sm_out.GetError().GetMessage() << std::endl;
		}
	}


	Aws::ShutdownAPI(options);
	system("PAUSE");

	return 0;
}

https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/examples-sqs-messages.html 의 샘플 코드에 인증, 지역설정을 추가했습니다. 

 

그리고 FIFO 대기열을 위해 DeduplicationId를 추가했습니다. 이는 FIFO에서 중복된 메세지가 기록되지 않도록 하기 위한 값으로 일정 시간 안에는 유니크해야 합니다. 기록된 메세지가 이미 사라졌거나 같은 값이 존재하더라도 10분(?? 정확한 수치는 공식 문서에서 찾지 못했습니다.) 정도 지나면 중복된 값이 나오더라도 기록됩니다. 

 

만약 중복될 경우에도 메세지를 보내는 어플리케이션에서는 메세지가 정상적으로 보내졌다고 판단되고, FIFO 대기열에서는 DeduplicationID 를 체크해서 중복으로 보내진 내용은 대기열에 추가하지 않습니다. 

 

코드 설명은 짧고 간단해서 별도 추가 설명은 필요 없을 듯 하네요. 혹시 궁금한 점이 있으면 댓글로 남겨주세요.

 

 

java 사용할 때는 credential 파일과 config 파일을 자신 계정 루트에 .aws 폴더 만들어서 넣으면 되는데... 

 

cpp sdk에서는 region 정보가 담긴 config파일을 사용하지 않습니다.

 

https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html

(* cpp sdk에서 credential 설정하는 방법)

 

Aws::SDKOptions options;
Aws::InitAPI(options);

Aws::Client::ClientConfiguration clientConfig;
clientConfig.region = "ap-northeast-2";
.
.
.

Aws::SQS::SQSClient sqs(clientConfig);
.
.
.

위의 샘플 코드처럼 clientconfiguration을 별도 설정하여 사용할 서비스 생성 시 인자로 같이 넘겨주면 됩니다.

plain text 형태로 S3에 업로드한 자료들이 맘에 걸려서 bucket에 올린 자료들을 모두 암호화 하기로 결정했습니다. 

 

여러 복잡한 절차들이 있을 줄 알았는데 생각보다 큰 불편함없이 마무리 할 수 있어서 좋긴 한데, 뭔가 하다 만 느낌이라 걱정이 싹 가시진 않네요. 

 

기존에 자료를 업로드 하는 app과 자료를 읽는 app 모두에서 별다른 조치를 할 필요는 없었습니다. 

 

IAM에 설정된 사용자에 정책을 추가하는 것과 Bucket에 암호화 설정하기. 그리고 기존 데이터 암호화하기로 모든 작업이 끝나네요. 이 설정이 편한게 데이터 암호화를 클라이언트가 직접 하는게 아닌 server side encryption이라 별로 손 댈게 없어서 좋았습니다. put, get 시 별도 하는게 없어 암호화 되었는지 아닌지를 체감하지 애매하지만요.

 

Bucket에 기본 암호화 설정하기

 

일단 Bucket 설정에서 기본 암호화 설정을 해보겠습니다. 

 

암호화 하려는 버킷의 설정에서 기본 암호화를 선택하면 좌측과 같은 메뉴가 활성화 됩니다. 저는 AES-256을 선택하겠습니다. 

 

이 경우 기존에 업로드된 파일이 암호화 되지는 않고, 새로 업로드되는 자료들만 암호화됩니다. 

 

아래에서 기존 파일들을 암호화 하는 과정을 확인할 수 있습니다.

 

 

 

 

AES-256을 설정하면 좌측과 같은 화면을 확인할 수 있습니다. 

 

"권한" -> "버킷 정책" 중 "Action" 중 업로드 시 암호화된 혹은 암호화 하지 않은 파일에 대한 정책이 별도로 있을 경우 거부될 수 있다는 메세지가 있습니다. 기존에 설정된 내용이 없다면 무시하고 넘어가도 됩니다.

 

AES-256에 대한 공식 문서에는 다음과 같이 설명되어 있습니다. "서버측 암호화를 통해 유휴 데이터를 보호합니다. Amazon S3는 고유한 키로 각 객체를 암호화합니다. 또한 추가 보안 조치로 주기적으로 바뀌는 마스터 키를 사용하여 키 자체를 암호화합니다. Amazon S3 서버 측 암호화는 가장 강력한 블록 암호 중 하나인 256비트 고급 암호화 표준(AES-256)을 사용하여 데이터를 암호화합니다." (링크)

 

 

 

이 설정을 하지 않고 원하는 객체만 암호화 하려면 putObject 시 ServerSidEncryption옵션을 지정하여 암호화 요청을 할 수 있습니다. NodeJS의 샘플 코드로 예를 들어보겠습니다. 

    const AWS = require('aws-sdk');
        .
    .
    this.s3 = new AWS.S3();
    .
    .
	this.s3.putObject({Bucket:'your_bucket_name', Key:'your_key', 
    	Body:your_data, ServerSideEncryption: 'AES256', function(err,data){
			if(err){
				console.log('s3 put : ' + err);
			}
			else{
				console.log('Successfully uploaded!');
			}
		});
        
      .
      .

 

IAM 계정에 권한 설정 하기

 

이런 암호화 처리를 위해서는 접근하는 IAM 계정에 권한이 필요합니다. 

 

IAM에서 S3에 PUT, GET을 처리하는 계정에 연결권 정책의 Action에 aes:Decrypt와 aes:Encrypt를 추가해야 합니다. 

 

적용된 샘플은 아래와 같습니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1540362430000",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "aes:Decrypt",
                "aes:Encrypt"
            ],
            "Resource": [
                "arn:aws:s3:::your_bucket_name"
            ]
        }
    ]
}

 

버킷의 기존 파일들 암호화 하기

 

이제 버킷에 있던 기존 파일들의 암호화를 변경해 보겠습니다. 

 

해당 버킷에서 암호화할 폴더 혹은 파일을 선택 후 "작업" -> "암호화 변경"을 선택합니다. 

 

그리고 AES-256을 선택 후 저장을 선택하면 선택된 객체 모두 암호화를 진행합니다. 암호화된 객체를 다운로드하면 복호화 하여 다운로드 하기 때문에 암호화 된 상태인지 확인할 수 없습니다. 암호화된 객체를 선택하여 개요 부분을 확인하면 아래와 같이 적용된 부분을 확인할 수 있습니다.

 

(* 서버 측 암호화 적용 결과)

 

KMS를 선택하면 원하는 키를 선택할 수 있습니다만.. 키 관리하고 https로만 처리되어야 하는 여러 번거로움 때문에 사용하지는 않았습니다. 

 

이상입니다. 생각보다 간단한데다 사용하는 코드에서도 별다른 처리가 필요없어 너무 손쉽게 끝나버린 "S3 Bucket 암호화 적용하기" 였습니다. 

Cognito 유저가 비공개 S3로 접근하려고 할 때... 


https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/reference_policies_examples_s3_cognito-bucket.html

=> 이 문서 중 sample rule script에서 Principal 관련된 코드가 빠져 있다. 실제 이 룰을 적용하려면 principal 에러가 발생한다. 문서 내용에는 언급되어 있지 않았다.



javascript로 aws cognito 연동 할 때, app 설정에서 "클라이언트 보안키 생성" 옵션을 해제해야 한다.


https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js


"When creating the App, the generate client secret box must be unchecked because the JavaScript SDK doesn't support apps that have a client secret."


클라이언트 보안 옵션은 기본으로 체크 되어 있어서 생각없이 넘어 갔는데, 켜져 있으면  "Unable to verify secret hash for client .." 라는 에러는 보게된다.

앱 클라이언트 첫 설정할 때 주의 필요!!!



 https://aws.amazon.com/ko/premiumsupport/knowledge-center/ec2-server-refused-our-key/


처음 인스턴스를 생성할 때 다운로드 했던 pem 파일을 찾지 못한다면... 사망!! 처음부터 다시 구축하던가 백업해둔 이미지로 다시 생성!!


1. 해당 인스턴스를 실행 중지 시키고.. 


2. 작업 > 인스턴스 설정 > 사용자 데이터 보기/변경을 선택 (인스턴스가 완전히 종료되지 않은 상태에서 변경 불가)


3. 아래 코드를 붙여넣기 한 다음


#cloud-config
ssh_deletekeys: false
ssh_authorized_keys:
cloud_final_modules:
  - [ssh, always]

4. puttygen을 연 후, load 버튼을 눌러 저장해둔 pem 파일을 연다.


5. 그러면 상단 텍스트 박스에 public key 텍스트가 출력되는데 ssh-rsa로 시작하는 텍스트부터 끝까지 복사를 한다.


6. 붙여넣기 했던 위 코드 중 ssh_authorized_keys:  바로 옆에 복사한 내용을 붙여넣는다. 


7. 저장 하고 인스턴스를 다시 시작한다.


8. 다시 연결되는 것을 확인할 수 있다. 


위 링크의 내용을 그대로 가져왔지만 붙여 넣기 할 때 주의가 필요함. 

EC2 대시보드 -> 인스턴스 -> 인스턴스 시작 


여기서는 Amazon Linux AMI를 선택하려 한다. 




인스턴스 유형은 t2.micro (역시 프리 티어 사용 가능 ) 선택 후 "검토 및 시작" 버튼. 


 (인스턴스 구성 및 스토리지 추가 등이 필요하면 각 상세 항목을 설정할 수 있다. 각 항목은 추후 필요할 때 다시 설정할 예정이니 여기서는 넘어가자.)



"시작" 버튼을 누르면 키페어에 대한 질의가 나온다. "새 키 페어 생성"을 눌러보자.


키 페어 이름을 넣은 후 "키 페어 다운로드"를 해서 pem 파일을 만들어두자. 원격 제어를 위해 접근하려면 꼭 필요하다. 





그리고 인스턴스를 시작하면 생성 후 가동된다. 


이제 터미널에서 접속하는 과정을 보자. 


일단 다운받은 pem 파일을 원하는 위치에 놓고 터미널에서 해당 폴더로 이동한다.


그리고 pem 파일 권한에 읽기 권한을 아래와 같이 부여한다.


chmod 400 yourfilename.pem



그리고 터미널에서 ssh로 바로 접속해보자.


ssh -i yourfilename.pem ec2-user@yourec2domain


이제 EC2로 바로 접속할 수 있다. 


참고로 EC2 인스턴스를 활성화 시킨 후 재부팅 등이 이루어지면 IP와 도메인이 변경된다. 고정 IP를 할당하자면 비용이 추가되니 필요에 따라 선택이 필요하다. 









AWS 문서를 따라 했지만 안되는 문제가 발생했다. (공식 문서)


아래와 같은 방법으로 처리해야 whitelist를 적용할 수 있다.


1. 해당 bucket의 권한 탭에서 "버킷 정책"을 선택. 아래와 같이 정보를 입력한다.


{

    "Version": "2012-10-17",

    "Id": "Policy......",

    "Statement": [

        {

            "Sid": "WhiteListfor",

            "Effect": "Deny",

            "Principal": "*",

            "Action": "s3:*",

            "Resource": "arn:aws:s3:::your-bucket-name/*",

            "Condition": {

                "NotIpAddress": {

                    "aws:SourceIp": "Ip address range to allow"

                }

            }

        }

    ]

}

2. 해당 버킷의 필요한 파일, 폴더에 "everyone 읽기 속성"을 부여한다.


이렇게 하면 원하는 IP 대역에서만 접근이 가능해진다.


+ Recent posts