API Privada con Serverless Framework, AWS y VPCs.

Api Gateway Private Endpoint

Generalmente cuando hablamos de APIs pensamos en la forma como otros desarrolladores se pueden conectar a nuestros sistemas, productos, o servicios. Sin embargo, cuando empiezas a desacoplar tus sistemas te das cuenta que el utilizar APIs para el desarrollo interno puede ser igual o más útil para el negocio.

Este ejemplo sencillo muestra como poder crear un API privada usando Serverless Framework y Amazon Web Services. Ojo, pestaña, y ceja, lo que estoy haciendo con este ejemplo no es hacer privada un API a través de autentificación y autorización, eso siempre se puede agregar sobre el API como una capa de seguridad aún mayor, en este caso estamos haciendo privada la conexión para que solo pueda ser accedida desde nuestra VPC a través de un AWS PrivateLink.

Ya han pasado un poco más de dos años desde que AWS lanzó esta  funcionalidad, sin embargo no había tenido la oportunidad y necesidad de utilizarla anteriormente. Pueden leer el Post original de AWS acá : https://aws.amazon.com/blogs/compute/introducing-amazon-api-gateway-private-endpoints/

El código de este ejemplo puede ser encontrado en GitHub : https://github.com/ctala/serverless-private-api-endpoint

Requerimientos

  1. Cuenta en AWS.
  2. Serverless Framework Instalado
  3. AWS CLI Instalado. Esto nos permitirá hacer el deployment de la aplicación de manera directa.
  4. Tener una Red Privada con la cual queramos acceder al servicio (VPC)
  5. Tener una máquina a la que podamos acceder para poder probar el servicio.

Creando el VPC Endpoint

Para generar el endpoint a utilizar, nos dirigimos al menú de VPCs de la consola de AWS, seleccionamos el sub-menú de endpoints en el costado izquierdo, y luego crear uno nuevo. Nos debería aparecer algo como lo que se muestra a continuación.

VPC Endpoint
VPC Endpoint

Si en busqueda escribimos execute-api, nos debería aparecer por defecto la opción que necesitamos para que este endpoint pueda ejecutar la API que crearemos. A continuación seleccionamos el VPC, las subnets, y las security groups correspondientes y damos click en continuar.

El Id de Endpoint resultante lo usaremos en nuestra configuración del Serverless.

Resource Policy

Antes de configurar el serverless, debemos definir quién puede y/o no puede tener acceso a nuestra red privada. En este caso, copiaremos el resource policy de ejemplo de AWS que hace Whitelist de la VPC que puede ejecutarlo (Se puede encontrar bajo Resources Policies en el menú de cualquier API bajo API Gateway.). La idea es que generemos este mismo Resource Policy usando la configuración del Serverless Framework.

Código del Serverless

El siguiente código es el que genera el Resource Policy mostrado anteriormente y levanta un Lamda con su API Gateway respectivo con un endpoint privado, además del endpoint por defecto del Serverless Framework con Hello World.

service: sls-test-private-endpoint
frameworkVersion: '2'

provider:
name: aws
runtime: nodejs12.x
region: us-west-2
stage: dev
endpointType: PRIVATE
vpcEndpointIds:
- vpce-0631ee46a323b75e4

#El Resource Policy que generamos primero bloqueará todo el tráfico que no sea de la/las VPCs listadas a bajo aws:sourceVpc, luego permitimos
# el acceso de todo lo demás.
resourcePolicy:
##Bloqueo de lo que no corresponde al VPC
- Effect: Deny
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
Condition:
StringNotEquals:
aws:sourceVpc:
- vpc-03602a6783bdefb87
##Permiso a lo demás. Acá también podemos bloquiar por segmento de IP.
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*

functions:
hello:
handler: handler.hello
events:
- http:
path: /
method: get

 

Probando la conexión.

 

Acá queremos hacer dos cosas, la primera es revisar que no podamos acceder a la URL desde nuestros computadores. Ya que el endpoint es un GET simplemente probamos utilizando la URL resultante del deployment, por ejemplo : https://80nyvq2u70.execute-api.us-west-2.amazonaws.com/dev/ . Si bien la URL mostrada es la original, no puede ser accedida debido a que está dentro de una API privada.

Para probar realmente que podemos acceder a esta API desde la VPC, lo que tenemos que hacer es llamarla desde una máquina que esté en esta. En este caso un simple comando Curl nos puede ayudar desde la consola de la máquina virtual.

curl -v https://80nyvq2u70.execute-api.us-west-2.amazonaws.com/dev/

En este caso, si todo salió según lo esperado, obtendremos la respuesta del serverless incluyendo todos los headers agregados desde AWS.

curl -v https://80nyvq2u70.execute-api.us-west-2.amazonaws.com/dev/
* Trying 10.0.16.153:443...
* TCP_NODELAY set
* Connected to 80nyvq2u70.execute-api.us-west-2.amazonaws.com (10.0.16.153) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=*.execute-api.us-west-2.amazonaws.com
* start date: Apr 29 00:00:00 2020 GMT
* expire date: Apr 13 12:00:00 2021 GMT
* subjectAltName: host "80nyvq2u70.execute-api.us-west-2.amazonaws.com" matched cert's "*.execute-api.us-west-2.amazonaws.com"
* issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
* SSL certificate verify ok.
> GET /dev/ HTTP/1.1
> Host: 80nyvq2u70.execute-api.us-west-2.amazonaws.com
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Server
< Date: Wed, 23 Sep 2020 23:13:15 GMT
< Content-Type: application/json
< Content-Length: 2402
< Connection: keep-alive
< x-amzn-RequestId: e247cbde-523a-4867-89be-9588d3654b70
< x-amz-apigw-id: TV51yHB-PHcF2Mg=
< X-Amzn-Trace-Id: Root=1-5f6bd68b-2ef4b5a7424ef64695dd27cd;Sampled=0
< 
{
"message": "Go Serverless v1.0! Your function executed successfully!",
"input": {
"resource": "/",
"path": "/",
"httpMethod": "GET",
"headers": {
"Accept": "*/*",
"Host": "80nyvq2u70.execute-api.us-west-2.amazonaws.com",
"User-Agent": "curl/7.68.0",
"x-amzn-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
"x-amzn-tls-version": "TLSv1.2",
"x-amzn-vpc-id": "vpc-03602a6783bdefb87",
"x-amzn-vpce-config": "1",
"x-amzn-vpce-id": "vpce-0631ee46a323b75e4",
"X-Forwarded-For": "10.0.3.28"
},
"multiValueHeaders": {
"Accept": [
"*/*"
],
"Host": [
"80nyvq2u70.execute-api.us-west-2.amazonaws.com"
],
"User-Agent": [
"curl/7.68.0"
],
"x-amzn-cipher-suite": [
"ECDHE-RSA-AES128-GCM-SHA256"
],
"x-amzn-tls-version": [
"TLSv1.2"
],
"x-amzn-vpc-id": [
"vpc-03602a6783bdefb87"
],
"x-amzn-vpce-config": [
"1"
],
"x-amzn-vpce-id": [
"vpce-0631ee46a323b75e4"
],
"X-Forwarded-For": [
"10.0.3.28"
]
},
"queryStringParameters": null,
"multiValueQueryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "2qf3us2l93",
"resourcePath": "/",
"httpMethod": "GET",
"extendedRequestId": "TV51yHB-PHcF2Mg=",
"requestTime": "23/Sep/2020:23:13:15 +0000",
"path": "/dev/",
"accountId": "607613765343",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "80nyvq2u70",
"requestTimeEpoch": 1600902795396,
"requestId": "e247cbde-523a-4867-89be-9588d3654b70",
"identity": {
"cognitoIdentityPoolId": null,
"cognitoIdentityId": null,
"vpceId": "vpce-0631ee46a323b75e4",
"principalOrgId": null,
"cognitoAuthenticationType": null,
"userArn": null,
"userAgent": "curl/7.68.0",
"accountId": null,
"caller": null,
"sourceIp": "10.0.3.28",
"accessKey": null,
"vpcId": "vpc-03602a6783bdefb87",
"cognitoAuthenticationProvider": null,
"user": null
},
"domainName": "80nyvq2u70.execute-api.us-west-2.amazonaws.com",
"apiId": "80nyvq2u70"
},
"body": null,
"isBase64Encoded": false
}
* Connection #0 to host 80nyvq2u70.execute-api.us-west-2.amazonaws.com left intact
}

Y eso sería todo. Con estos pasos sencillos podemos tener nuestra API privada funcionando para ser accedida desde los recursos que estén dentro de nuestra VPC.

¡Mantengamos el contacto!

Me encantaría que te mantuvieras al día con el contenido que estoy generando. Recuerda que no es solo el blog, son las redes sociales, libros, y distintos pódcast 😎.

Hago todo lo posible para no hacer Spam,

¡MANTENGÁMONOS EN CONTACTO!

Me encantaría que te mantuvieras al día con el contenido que estoy generando. Recuerda que no es solo el blog, son las redes sociales, libros, y distintos pódcast 😎.

Hago todo lo posible para no hacer Spam,

Mejorando el performance de SQS para millones de escrituras

Solución Procesar SQS Fargate Aurora

Hace ya casi un año que Amazon publicó que estaban desarrollando el soporte de Amazon SQS como «Event Source» para una función Lambda, y desde que la solución estuvo disponible buscaba una escusa para poder encontrar un mini proyecto para probarla.

Para el desarrollo interno de la empresa tengo un «Bot» que obtiene de manera diaria un CSV con alrededor de 1.000.000 de filas. Para todos los que estamos acostumbrados a trabajar con archivos grandes, 1M de datos no suena tan complejo, por lo que a pesar de tener mejores opciones decidí que era el momento de hacer una prueba de fuego parseando este archivo y enviando el resultado directamente a la cola fila por fila para luego ser procesado a través de una función lambda.

Este post es para comentar sobre la estructura final del proyecto, y como se fue modificando para poder funcionar de manera correcta.

Leer más

Introducción a Serverless, Amazon Lambda, Express, Node.js y DynamoDB

Aprende a realizar un REST API usando Serverless, Amazon Lambda, Express, Node.js y DynamoDB. Además usaremos un par de herramientas que hará mucho más sencilla su desarrollo.

Source: Deploy a REST API using Serverless, Express and Node.js

 

Hace un tiempo que me fasciné con la tecnología Serverless, hasta el punto que gran parte de mis desarrollos y sistemas ya están funcionando con ella. Fue a tal nivel que me vi obligado a aprender lenguajes de programación de los cuales nunca me había hecho el tiempo de aprender, en este caso NodeJS ya que nunca me gustó mucho Javascript. Para mi sorpresa ES6, en lo que está basado actualmente la nueva versión de javascript, se adecua un montón a la forma como estoy acostumbrado a programar por lo que el aprendizaje fue más sencillo.

Aprovecho de escribir este tutorial ya que por un lado un amigo – Marcelo A. –  me dijo que ya no estoy escribiendo tanto en mi blog, por lo cual he perdido tráfico, además de poder ayudar a otro amigo – Ernesto M.-  con una breve introducción de estas tecnologías.

En este ejemplo crearemos una API REST – solo usaremos el create, list, and get como ejemplos- de productos que se conectará a una tabla en DynamoDB. (Pueden ver el código del ejemplo en GitHub)

Si bien a continuación hay una lista de requerimientos que deberías tener para poder entender lo más posible este ejemplo, puedes perfectamente copiar el código desde el repositorio y probarlo directamente. Yo estaré programando directamente en Linux, pero lo que mostraré debería ser transversal.

 

Requerimientos :

  • Tener una cuenta en Amazon Web Services.
  • Tener instalado la herramienta de Serverless.
  • Tener instalado NodeJS y npm.
  • Tener un IDE ( Netbeans, Atom, Sublime, etc ) con el cual poder editar el código de manera adecuada y entendible.

Leer más

Share to...