Control del acceso a las API de WebSocket con autorizadores REQUEST de AWS Lambda
La función de autorizador de Lambda en las API de WebSocket es similar a la de las API de REST, con las siguientes excepciones:
-
Solo puede utilizar una función de autorizador de Lambda para la ruta $connect
.
-
No se pueden utilizar variables de ruta (event.pathParameters
), ya que la ruta es fija.
-
event.methodArn
es diferente de su equivalente en la API de REST, ya que no tiene ningún método HTTP. En el caso de $connect
, methodArn
termina por "$connect"
:
arn:aws:execute-api:region
:account-id
:api-id
/stage-name
/$connect
-
La variables de contexto en event.requestContext
son diferentes de las de las API de REST.
En el siguiente ejemplo se muestra una entrada a un autorizador REQUEST
para una API de WebSocket:
{
"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"
}
}
El siguiente ejemplo de función de autorizador de Lambda es una versión de WebSocket de la función de autorizador de Lambda para las API de REST de la sección Ejemplos adicionales de funciones del autorizador de Lambda:
- 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)
Para configurar la función de Lambda anterior como una función de autorizador REQUEST
para una API de WebSocket, siga el mismo procedimiento que para las API de REST.
Para configurar la ruta $connect
de forma que utilice este autorizador de Lambda en la consola, seleccione o cree la ruta $connect
. En la sección Configuración, elija Editar. Seleccione su autorizador en el menú desplegable Autorización y, a continuación, elija Guardar cambios.
Para probar el autorizador, tiene que crear una conexión nueva. El cambio de autorizador en $connect
no afecta a los clientes que ya están conectados. Al conectarse a la API de WebSocket, tiene que proporcionar valores para todas las fuentes de identidad configuradas. Por ejemplo, puede conectarse enviando una cadena de consulta y un encabezado válidos mediante wscat
, como se muestra en el siguiente ejemplo:
wscat -c 'wss://myapi.execute-api.us-east-1.amazonaws.com/beta?QueryString1=queryValue1' -H HeaderAuth1:headerValue1
Si intenta conectarse sin un valor de identidad válido, recibirá una respuesta 401
:
wscat -c wss://myapi.execute-api.us-east-1.amazonaws.com/beta
error: Unexpected server response: 401