본문 바로가기

카테고리 없음

[AWS] 서버리스 아키텍쳐_인증과 권한 & Lambda

user-profile Lambda Function

"use strict";

 

var request = require('request');

 

exports.handler = function (event, context, callback) {

    console.log(event);

 

    if (!event.authToken) {

        callback('Could not find authToken');

        return;

    }

 

    if (!event.accessToken) {

        callback('Could not find access_token');

        return;

    }

 

    var id_token = event.authToken.split(' ')[1];

    var access_token = event.accessToken;

 

    var body = {

        'id_token': id_token,

        'access_token': access_token

    };

 

    var options = {

        url: 'https://' + process.env.DOMAIN + '/userinfo',

        method: 'GET',

        json: true,

        body: body

    };

 

    request(options, function (error, response, body) {

        console.log("Response0: " + JSON.stringify(response));

 

        if (!error && response.statusCode === 200) {

            console.log("Response1: " + JSON.stringify(response));

            callback(null, body);

        } else {

            callback(error);

        }

    });

};

 

user-profile Test

로그인 후 Local Storage에 저장된 값을 이용해서 아래와 같은 형태의 테스트 데이트를 생성

 

{

  "accessToken": "w7agqIkUzCofd9nUelOxgd1zogqPpw9V",

  "authToken": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJnaXZlbl9uYW1lIjoi7LC966C0IiwiZmFtaWx5X25hbWUiOiLrsJUiLCJuaWNrbmFtZSI6Im15YW5qaW5pIiwibmFtZSI6IuuwleywveugtCIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHamdzSnZXRUtxbmVZZVpCNUdlVGhxdjVBeUlnSHhrSlV2SHQxUVBhbmM9czk2LWMiLCJsb2NhbGUiOiJrbyIsInVwZGF0ZWRfYXQiOiIyMDIwLTEwLTI2VDAwOjI0OjQ5LjE3N1oiLCJlbWFpbCI6Im15YW5qaW5pQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczovL25hYW5qaW5pLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDExNTk3Mjg0MTI1MjM5Njc5NzQ1NyIsImF1ZCI6ImVTdnlSaUxnaDhzamNiUnRVS0E5SnJIak5pTTdORklIIiwiaWF0IjoxNjAzNjcxODg5LCJleHAiOjE2MDM3MDc4ODksImF0X2hhc2giOiJCeFRsZzZldVhXYVVwbjhlMER5QkZRIiwibm9uY2UiOiJ-LVNta3ltRTVmWW9kbGFiRWp-WWtaRV9JaUpKNDQuZSJ9.mioxKcb1K57p4UWDOAc9UJ_KYhTwTrTvW1LTk5_anGo"

}

 





API Gateway 생성 후 테스트하면 아래와 같은 오류가 발생

 

⇒ 응답 헤더에 Access-Control-Allow-Origin 헤더가 정의되지 않았기 때문에 발생

⇒ API Gateway에서 CORS 활성화로 해결




⇒ 응답 헤더 중 Access-Control-Allow-Headers 값에 accesstoken이 존재하지 않기 때문에 발생

⇒ API Gateway CORS 활성화 기능에서 추가





⇒ 클라이언트의 요청이 API Gateway를 통해서 람다 함수까지는 전달되었음

⇒ 람다 함수가 처리하는 과정에서 오류가 발생

 

exports.handler = function (event, context, callback) {
    console.log(event);


    if (!event.authToken) {                     # authToken == id_token (auth0.com에서 발행한 JWT)
        callback('Could not find authToken');   # 여기서 출력되는 메시지
        return;
    }

 

⇒ API Gateway에서 Authorization 헤더로 전달된 값을 AuthToken 이름의 이벤트로 전달하되도록 맵핑을 설정

 




프로 파일 버튼을 클릭했을 때 사용자 정보가 조회, 출력되는 것을 확인



API Gateway의 권한 부여자를 이용해서 JWT 토큰 검증 후 유효한 토큰인 경우에만 람다를 실행할 수 있도록 권한을 부여

참조 ⇒ https://auth0.com/docs/tokens/json-web-tokens/validate-json-web-tokens

 

JWT 토큰을 검증하는 custom-authorizer 람다 함수를 생성

"use strict";

 

var jwt = require('jsonwebtoken');

 

var generatePolicy = function(principalId, effect, resource) {

    var authResponse = {};

    authResponse.principalId = principalId;

    if (effect && resource) {

        var policyDocument = {};

        policyDocument.Version = '2012-10-17';

        policyDocument.Statement = [];

        var statementOne = {};

        statementOne.Action = 'execute-api:Invoke';

        statementOne.Effect = effect;

        statementOne.Resource = resource;

        policyDocument.Statement[0] = statementOne;

        authResponse.policyDocument = policyDocument;

    }

    return authResponse;

};



exports.handler = function(event, context, callback) {

 

    console.log("### event", JSON.stringify(event));

    

    if(!event.authorizationToken) {

        console.log(JSON.stringify(event));

        callback('Could not find authToken');

        return;

    }



    var id_token = event.authorizationToken.split(' ')[1];

    

    var secretBuffer = new Buffer(process.env.AUTH0_SECRET);

    jwt.verify(id_token, secretBuffer, function(err, decoded){

        if(err) {

            console.log('Failed jwt verification: ', err,

            'auth: ', event.authorizationToken);

            callback('Authorization Failed');

        } else {

            callback(null, generatePolicy('user', 'allow', event.methodArn));

        }

    })

};



custom-authorizer 람다 함수 테스트

 

⇒ jwt.verify() 함수에서 오류를 유발하는 것을 확인

 

https://jwt.io 사이트를 통해서 토큰을 수작업으로 검증

 

 

 

auth0.com에서 서명에 사용한 알고리즘을 HS256으로 변경



로그인을 통해 새롭게 발급받은 idToken으로 테스트 케이스를 재 작성 후 테스트를 실행







사용자 프로필 버튼을 클릭했을 때 정상 여부 확인

브라우저에 LocalStorage 확인



CloudWatch에서 /aws/lambda/custom-authorizer 로그 그룹 확인



CloudWatch에서 /aws/lambda/user-profile 로그 그룹 확인



브라우저에서 로그를 확인







S3 버킷에 저장된 파일 목록을 반환하는 람다 함수를 생성, 배포

#1 Lambda-Dev-Oops 그룹에 Create-Function 권한을 추가

 

#2 프로젝트 환경 구성

C:\Users\myanj> cd /

 

C:\> cd serverless

 

C:\serverless> mkdir get-video-list

 

C:\serverless> cd cli-function

 

C:\serverless\get-video-list> npm init -y

Wrote to C:\serverless\get-video-list\package.json:

 

{

  "name": "get-video-list",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "keywords": [],

  "author": "",

  "license": "ISC"

}



C:\serverless\get-video-list\package.json 파일에 create, precreate 스크립트를 추가

  • precreate ⇒ 현재 디렉터리의 파일을 압축 (배포를 위해)

  • create ⇒ precreate 에서 생성한 압축 파일을 이용해서 람다 함수를 생성, 배포

{
  "name": "get-video-list",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "create": "aws lambda create-function --function-name get-video-list --handler index.hander --memory-size 128 --runtime nodejs4.3 --role arn:aws:iam::869011496078:role/lambda-s3-execution-role --timeout 3 --publish --zip-file fileb://Lambda-Deployment.zip", 
    "precreate": "zip -r Lambda-Deployment.zip * -x *.zip *.log",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

 

 

aws lambda create-function ⇐ 람다 함수 생성

--function-name get-video-list ⇐ 생성할 람다 함수 이름

--handler index.hander ⇐ 핸들러 함수를 지정 (P156)

--memory-size 128 ⇐ 메모리 크기를 지정 (기본 설정 > 메모리)

--runtime nodejs4.3 ⇐ 람다 함수 실행 환경 

--role arn:aws:iam::869011496078:role/lambda-s3-execution-role ⇐ 람다 함수 실행에 사용하는 롤

--timeout 3 ⇐ 람다 함수 실행 제한 시간

--publish ⇐ 생성과 동시에 배포 

--zip-file fileb://Lambda-Deployment.zip ⇐ 람담 함수 코드 포함된 압축 파일

 

자세한 명령어 사용법은 aws lambda create-function help 명령으로 확인 가능



#3 모듈 추가

https://www.npmjs.com/package/async

 

C:\serverless\get-video-list> npm install aws-sdk

npm notice created a lockfile as package-lock.json. You should commit this file.

npm WARN cli-function@1.0.0 No description

npm WARN cli-function@1.0.0 No repository field.

 

+ aws-sdk@2.778.0

added 13 packages from 65 contributors, removed 168 packages and audited 14 packages in 3.974s

found 0 vulnerabilities



C:\serverless\get-video-list> npm install async

npm WARN cli-function@1.0.0 No description

npm WARN cli-function@1.0.0 No repository field.

 

+ async@3.2.0

added 1 package from 1 contributor and audited 15 packages in 0.578s

found 0 vulnerabilities




async 모듈에 waterfall() 함수 사용법 테스트

var async = require("async");


async.waterfall([
    function(firstcallbackfunc) {
        console.log(`첫번째 함수`);
        firstcallbackfunc(null, "Peter", "Sam");
    }, 
    function(a1, a2, secondcallbackfunc) {
        console.log(`두번째 함수 ${a1}, ${a2}`);
        secondcallbackfunc(null, "Serverless");
    }, 
    function(a3, thirdcallbackfunc) {
        console.log(`세번째 함수 ${a3}`);
        thirdcallbackfunc(null, "Done")
    }
], function(err, result) {
    console.log(`최종 콜백 ${err}, ${result}`);
});



#4 람다 함수 코드를 작성

C:\serverless\get-video-list\index.js

'use strict';


var AWS = require('aws-sdk');
var async = require('async');


var s3 = new AWS.S3();


//  입력 포맷을 생성
function createBucketParams(next) {
    var params = {
        Bucket: process.env.BUCKET, 
        EncodingType: 'url'
    };
    next(null, params);
}


//  버킷의 데이터 목록을 조회
function getVideosFromBucket(params, next) {
    // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#listObjects-property
    s3.listObjects(params, function(err, data) {
        if (err) {
            next(err);
        } else {
            next(null, data);
        }
    });
}


//  조회 결과를 반환할 포맷으로 변형
function createList(data, next) {
    console.log(data);
    

    var urls = [];
    for (var i = 0; i < data.Contents.length; i ++) {
        var file = data.Contents[i];
        //  https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/substr
        if (file.key && file.key.substr(-3, 3) === 'mp4') {
            urls.push(file);
        }
    }

    var result = {
        baseUrl: process.env.BASE_URL,
        bucket: process.env.BUCKET,
        urls: urls            
    };
    next(null, result);
}

exports.hander = function(event, context, callback) {
    async.waterfall([
        createBucketParams, 
        getVideosFromBucket, 
        createList
    ], function(err, result) {
        if (err) {
            callback(err);
        } else {
            callback(null, result);
        }
    });
};

 

#5 람다 함수를 생성

C:\serverless\get-video-list> npm run create

:

{

    "FunctionName": "get-video-list",

    "FunctionArn": "arn:aws:lambda:us-east-1:869011496078:function:get-video-list",

    "Runtime": "nodejs12.x",

    "Role": "arn:aws:iam::869011496078:role/lambda-s3-execution-role",

    "Handler": "index.hander",

    "CodeSize": 8227909,

    "Description": "",

    "Timeout": 3,

    "MemorySize": 128,

    "LastModified": "2020-10-26T07:17:27.224+0000",

    "CodeSha256": "5OYjGX0EPT68t28/qVPTMxOD4ZvmifyrjWvUaxdCkTM=",

    "Version": "1",

    "TracingConfig": {

        "Mode": "PassThrough"

    },

    "RevisionId": "d1d5e399-721a-4616-b455-e3764532c618",

    "State": "Active",

    "LastUpdateStatus": "Successful"

}




#6 트러블슈팅

An error occurred (AccessDeniedException) when calling the CreateFunction operation: User: arn:aws:iam::869011496078:user/lambda-upload is not authorized to perform: iam:PassRole on resource: arn:aws:iam::869011496078:role/lambda-s3-execution-role

npm ERR! code ELIFECYCLE

npm ERR! errno 254

npm ERR! get-video-list@1.0.0 create: `aws lambda create-function --function-name get-video-list --handler index.hander --memory-size 128 --runtime nodejs4.3 --role arn:aws:iam::869011496078:role/lambda-s3-execution-role --timeout 3 --publish --zip-file fileb://Lambda-Deployment.zip`

npm ERR! Exit status 254

npm ERR!

npm ERR! Failed at the get-video-list@1.0.0 create script.

npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

 

⇒ lambda-upload 사용자에게 iam:PassRole 역할이 없기 때문에 발생

⇒ IAM에서 lambda-upload 사용자가 소속되어 있는 Lambda-DevOps 그룹에 iam:PassRole 권한을 추가




An error occurred (InvalidParameterValueException) when calling the CreateFunction operation: The runtime parameter of nodejs4.3 is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (nodejs12.x) while creating or updating functions.

npm ERR! code ELIFECYCLE

npm ERR! errno 254

npm ERR! get-video-list@1.0.0 create: `aws lambda create-function --function-name get-video-list --handler index.hander --memory-size 128 --runtime nodejs4.3 --role arn:aws:iam::869011496078:role/lambda-s3-execution-role --timeout 3 --publish --zip-file fileb://Lambda-Deployment.zip`

npm ERR! Exit status 254

npm ERR!

npm ERR! Failed at the get-video-list@1.0.0 create script.

npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

 

⇒ nodejs4.3 런타임을 제공하지 않기 때문에 발생

⇒ --runtime 파라미터의 값으로 사용 가능한 런타임을 지정

https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html

 

 "scripts": {
    "create": "aws lambda create-function --function-name get-video-list --handler index.hander --memory-size 128 --runtime nodejs12.x --role arn:aws:iam::869011496078:role/lambda-s3-execution-role --timeout 3 --publish --zip-file fileb://Lambda-Deployment.zip",
    "precreate": "zip -r Lambda-Deployment.zip * -x *.zip *.log",
    "deploy": "aws lambda update-function-code --function-name _____ --zip-file fileb://Lambda-Deployment.zip",
    "predeploy": "zip -r Lambda-Deployment.zip * -x *.zip *.log",
    "test": "echo \"Error: no test specified\" && exit 1"
  },




#7 람다 함수 생성 및 배포 여부 확인




#8 환경 변수 설정




#9 람다 함수 테스트

 

오류가 발생하는 경우, 수정한 람다 함수를 재배포가 용이하도록 package.json에 deploy, predeploy 스크립트를 추가

   "deploy": "aws lambda update-function-code --function-name arn:aws:lambda:us-east-1:869011496078:function:get-video-list --zip-file fileb://Lambda-Deployment.zip",
    "predeploy": "zip -r Lambda-Deployment.zip * -x *.zip *.log",