AWS Lambda REQUEST 권한 부여자를 통해 WebSocket API에 대한 액세스 제어 - Amazon API Gateway

AWS Lambda REQUEST 권한 부여자를 통해 WebSocket API에 대한 액세스 제어

WebSocket API에서 Lambda 권한 부여자 함수는 REST API와 유사하지만 다음과 같은 예외가 있습니다.

  • $connect경로에 Lambda 권한 부여자 함수만 사용할 수 있습니다.

  • 경로는 고정되어 있으므로 경로 변수(event.pathParameters)를 사용할 수 없습니다.

  • event.methodArn은 HTTP 메서드가 없으므로 상응하는 REST API와 다릅니다. $connect의 경우, methodArn"$connect"로 끝납니다.

    arn:aws:execute-api:region:account-id:api-id/stage-name/$connect
  • event.requestContext의 컨텍스트 변수는 REST API에 대한 변수와 다릅니다.

다음 예시는 WebSocket API에 대한 REQUEST 권한 부여자 입력을 보여줍니다.

{ "type": "REQUEST", "methodArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/default/$connect", "headers": { "Connection": "upgrade", "content-length": "0", "HeaderAuth1": "headerValue1", "Host": "abcdef123.execute-api.us-east-1.amazonaws.com", "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits", "Sec-WebSocket-Key": "...", "Sec-WebSocket-Version": "13", "Upgrade": "websocket", "X-Amzn-Trace-Id": "...", "X-Forwarded-For": "...", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "Connection": [ "upgrade" ], "content-length": [ "0" ], "HeaderAuth1": [ "headerValue1" ], "Host": [ "abcdef123.execute-api.us-east-1.amazonaws.com" ], "Sec-WebSocket-Extensions": [ "permessage-deflate; client_max_window_bits" ], "Sec-WebSocket-Key": [ "..." ], "Sec-WebSocket-Version": [ "13" ], "Upgrade": [ "websocket" ], "X-Amzn-Trace-Id": [ "..." ], "X-Forwarded-For": [ "..." ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "queryStringParameters": { "QueryString1": "queryValue1" }, "multiValueQueryStringParameters": { "QueryString1": [ "queryValue1" ] }, "stageVariables": {}, "requestContext": { "routeKey": "$connect", "eventType": "CONNECT", "extendedRequestId": "...", "requestTime": "19/Jan/2023:21:13:26 +0000", "messageDirection": "IN", "stage": "default", "connectedAt": 1674162806344, "requestTimeEpoch": 1674162806345, "identity": { "sourceIp": "..." }, "requestId": "...", "domainName": "abcdef123.execute-api.us-east-1.amazonaws.com", "connectionId": "...", "apiId": "abcdef123" } }

다음의 Lambda 권한 부여자 함수 예제는 Lambda 권한 부여자 함수의 추가 예제의 REST API에 대한 Lambda 권한 부여자 함수의 WebSocket 버전입니다.

Node.js
// A simple REQUEST authorizer example to demonstrate how to use request // parameters to allow or deny a request. In this example, a request is // authorized if the client-supplied HeaderAuth1 header and QueryString1 query parameter // in the request context match the specified values of // of 'headerValue1' and 'queryValue1' respectively. export const handler = function(event, context, callback) { console.log('Received event:', JSON.stringify(event, null, 2)); // Retrieve request parameters from the Lambda function input: var headers = event.headers; var queryStringParameters = event.queryStringParameters; var stageVariables = event.stageVariables; var requestContext = event.requestContext; // Parse the input for the parameter values var tmp = event.methodArn.split(':'); var apiGatewayArnTmp = tmp[5].split('/'); var awsAccountId = tmp[4]; var region = tmp[3]; var ApiId = apiGatewayArnTmp[0]; var stage = apiGatewayArnTmp[1]; var route = apiGatewayArnTmp[2]; // Perform authorization to return the Allow policy for correct parameters and // the 'Unauthorized' error, otherwise. var authResponse = {}; var condition = {}; condition.IpAddress = {}; if (headers.HeaderAuth1 === "headerValue1" && queryStringParameters.QueryString1 === "queryValue1") { callback(null, generateAllow('me', event.methodArn)); } else { callback("Unauthorized"); } } // Helper function to generate an IAM policy var generatePolicy = function(principalId, effect, resource) { // Required output: var authResponse = {}; authResponse.principalId = principalId; if (effect && resource) { var policyDocument = {}; policyDocument.Version = '2012-10-17'; // default version policyDocument.Statement = []; var statementOne = {}; statementOne.Action = 'execute-api:Invoke'; // default action statementOne.Effect = effect; statementOne.Resource = resource; policyDocument.Statement[0] = statementOne; authResponse.policyDocument = policyDocument; } // Optional output with custom properties of the String, Number or Boolean type. authResponse.context = { "stringKey": "stringval", "numberKey": 123, "booleanKey": true }; return authResponse; } var generateAllow = function(principalId, resource) { return generatePolicy(principalId, 'Allow', resource); } var generateDeny = function(principalId, resource) { return generatePolicy(principalId, 'Deny', resource); }
Python
# A simple REQUEST authorizer example to demonstrate how to use request # parameters to allow or deny a request. In this example, a request is # authorized if the client-supplied HeaderAuth1 header and QueryString1 query parameter # in the request context match the specified values of # of 'headerValue1' and 'queryValue1' respectively. import json def lambda_handler(event, context): print(event) # Retrieve request parameters from the Lambda function input: headers = event['headers'] queryStringParameters = event['queryStringParameters'] stageVariables = event['stageVariables'] requestContext = event['requestContext'] # Parse the input for the parameter values tmp = event['methodArn'].split(':') apiGatewayArnTmp = tmp[5].split('/') awsAccountId = tmp[4] region = tmp[3] ApiId = apiGatewayArnTmp[0] stage = apiGatewayArnTmp[1] route = apiGatewayArnTmp[2] # Perform authorization to return the Allow policy for correct parameters # and the 'Unauthorized' error, otherwise. authResponse = {} condition = {} condition['IpAddress'] = {} if (headers['HeaderAuth1'] == "headerValue1" and queryStringParameters["QueryString1"] == "queryValue1"): response = generateAllow('me', event['methodArn']) print('authorized') return json.loads(response) else: print('unauthorized') return 'unauthorized' # Help function to generate IAM policy def generatePolicy(principalId, effect, resource): authResponse = {} authResponse['principalId'] = principalId if (effect and resource): policyDocument = {} policyDocument['Version'] = '2012-10-17' policyDocument['Statement'] = [] statementOne = {} statementOne['Action'] = 'execute-api:Invoke' statementOne['Effect'] = effect statementOne['Resource'] = resource policyDocument['Statement'] = [statementOne] authResponse['policyDocument'] = policyDocument authResponse['context'] = { "stringKey": "stringval", "numberKey": 123, "booleanKey": True } authResponse_JSON = json.dumps(authResponse) return authResponse_JSON def generateAllow(principalId, resource): return generatePolicy(principalId, 'Allow', resource) def generateDeny(principalId, resource): return generatePolicy(principalId, 'Deny', resource)

앞의 Lambda 함수를 WebSocket API에 대한 REQUEST권한 부여자 함수로 구성하려면 REST API와 같은 절차를 따르세요.

콘솔에서 이 Lambda 권한 부여자를 사용하도록 $connect 라우팅을 구성하려면 $connect 라우팅을 선택하거나 만듭니다. 경로 요청 설정 섹션에서 편집을 선택합니다. 권한 부여 드롭다운 메뉴에서 권한 부여자를 선택한 다음 변경 사항 저장을 선택합니다.

권한 부여자를 테스트하려면 새 연결을 생성해야 합니다. $connect에서 권한 부여자를 변경해도 이미 연결된 클라이언트에는 영향을 미치지 않습니다. WebSocket API에 연결할 때 구성된 모든 자격 증명 소스에 값을 제공해야 합니다. 예를 들어 다음 예제와 같이 wscat를 사용하여 유효한 쿼리 문자열과 헤더를 보내 연결할 수 있습니다.

wscat -c 'wss://myapi.execute-api.us-east-1.amazonaws.com/beta?QueryString1=queryValue1' -H HeaderAuth1:headerValue1

유효한 자격 증명 값 없이 연결을 시도하면 401 응답을 받게 됩니다.

wscat -c wss://myapi.execute-api.us-east-1.amazonaws.com/beta error: Unexpected server response: 401