Zugriffssteuerung für WebSocket-APIs mithilfe von AWS Lambda-REQUEST-Genehmigern - Amazon API Gateway

Zugriffssteuerung für WebSocket-APIs mithilfe von AWS Lambda-REQUEST-Genehmigern

Eine Lambda-Genehmigerfunktion in WebSocket-APIs ist bis auf die folgenden Ausnahmen mit der für REST-APIs vergleichbar:

  • Sie können für die $connect-Route nur eine Lambda-Genehmiger-Funktion verwenden.

  • Sie können keine Pfadvariablen (event.pathParameters) verwenden, da der Pfad fest ist.

  • event.methodArn unterscheidet sich von seinem REST API-Äquivalent, da es über keine HTTP-Methode verfügt. Im Fall von $connect endet methodArn mit "$connect":

    arn:aws:execute-api:region:account-id:api-id/stage-name/$connect
  • Die Kontextvariablen in event.requestContext unterscheiden sich von denen für REST-APIs.

Das folgende Beispiel zeigt eine Eingabe für einen REQUEST-Genehmiger für eine WebSocket-API:

{ "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" } }

Die folgende Beispiel-Lambda-Genehmigerfunktion ist eine WebSocket-Version der Lambda-Genehmigerfunktion für REST-APIs in Weitere Beispiele für Lambda-Genehmigerfunktionen:

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(null, generateDeny('me', event.methodArn)); } } // 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: response = generateDeny('me', event['methodArn']) print('unauthorized') return json.loads(response) # 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)

Um die voranstehende Lambda-Funktion als REQUEST-Genehmiger-Funktion für eine WebSocket-API zu konfigurieren, befolgen Sie dasselbe Verfahren wie für REST-APIs.

Um die $connect-Route für die Verwendung dieses Lambda-Genehmigers in der Konsole zu konfigurieren, wählen Sie die $connect-Route oder erstellen diese. Wählen Sie im Abschnitt Route request settings (Einstellungen der Routenanforderung) die Option Edit (Bearbeiten) aus. Wählen Sie im Dropdown-Menü Authorization (Autorisierung) Ihren Autorisierer aus und wählen Sie dann Save changes (Änderungen speichern) aus.

Zum Testen des Genehmigers müssen Sie eine neue Verbindung erstellen. Wenn der Genehmiger in $connect geändert wird, hat dies keine Auswirkungen auf den bereits verbundenen Client. Wenn Sie eine Verbindung mit Ihrer WebSocket-API herstellen, müssen Sie für alle konfigurierten Identitätsquellen Werte angeben. Beispielsweise können Sie eine Verbindung durch Senden einer gültigen Abfragezeichenfolge und eines gültigen Headers unter Verwendung von wscat wie im folgenden Beispiel herstellen:

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

Bei dem Versuch, ohne einen gültigen Identitätswert eine Verbindung herzustellen, erhalten Sie eine 401-Antwort:

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