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.

Tutorial : Full RESTfull API con AWS, Terraform, y Serverless Framework

Hace ya algunos años que he querido hacer un tutorial como este, en dónde de manera sencilla pueda explicar los distintos pasos de la creación de un API REST, o al menos como lo he aprendido a hacer basado en experiencia y errores.

Una de las razones del por qué nunca comencé con este proyecto es debido a que crear un API puede ser tan complejo como uno quiera, y nunca encontré el tiempo para realizarlo, por lo que decidí lanzar este tutorial por partes e iré publicando las distintas partes a medida que las vaya realizando.

En este tutorial crearemos un Full RESTfull API con seguridad basada en tokens utilizando AWS, Swagger, Terraform, DynamoDB, SSM, y Serverless Framework quién generará los recursos del API Gateway y Lambdas.

 

Estado Proyecto En Progreso
Fecha Inicio 17/05/2020
Fecha Actualización 17/05/2020
Fecha de Término
Capítulos listos 2/10
Link Youtube Playlist https://www.youtube.com/playlist?list=PLCjIDwuXOgwR64ScpUf6WLnW6j2jdkREX

 

Indice

  1. Entendiedo la necesidad. API de manejo de datos de usuarios y los datos requeridos.
  2. Conociendo las herramientas que se utilizarán y el por qué las usaremos.
  3. Diseñando la API utilizando Swagger. Antes de crear el API debemos saber que información recibirá y que información retornará.
  4. Generando la base de datos DynamoDB, y el recurso relacionado de SSM utilizando Terraform.
  5. Generando los endpoints utilizando Serverless Framework.
  6. Generando el CRUD de la aplicación. – Create, Read, Update, Delete –
  7. Asegurando nuestra API.

Extra

  1. Bloqueo de recursos en Terraform para impedir eliminaciones accidentales.
  2. Endpoint que lista los usuarios con paginación.
  3. Sincronizando DynamoDB con Redshift para realizar operaciones analiticas.

Contenido

Puedes encontrar el playlist con los vídeos en el siguiente link : https://www.youtube.com/playlist?list=PLCjIDwuXOgwR64ScpUf6WLnW6j2jdkREX

1.- Entendiendo la necesidad.

A pesar de la creencia popular nadie desarrolla por desarrollar, a menos que esté aprendiendo. Es muy importante entender la necesidad que existe por detrás de lo que se está creando para no tener duplicidad en el trabajo realizado ni atrasos inesperados. La solución siempre se debe de diseñar antes del proceso de desarrollo.

 

2.- Conociendo las herramientas

En este capítulo conversamos un poco de las herramientas que estaremos utilizando para este proyecto.

  1. Serverless Framework
  2. Terraform
  3. Swagger
  4. API Gateway
  5. Amazon Lambda
  6. Amazon DynamoDB
  7. SSM – Parameter Store.

3.- Diseñando la API (Work in Progress)

4.- Generando los recursos / DynamoDB (Work in Progress)

5.- Generando los endpoints usando Serverless. (Work in Progress)

6.- Generando el CRUD de la aplicación (Work in Progress)

7.- Asegurando el API (Work in Progress)

 

 

Jugando con el #PHP SDK de Amazon Web Services ( #AWS ) V3 para Simple Queue Service (#SQS)

Amazon SQS Ejemplo PHP SDK V3

Simple introducción a Amazon Simple Queue Service usando Composer, PHP y la versión 3 del SDK de Amazon.  Amazon SQS es un servicio de mensajería para la comunicación entre distintas plataformas y/o dispositivos.

Origen: Basic SDK Usage

Código del Proyecto : Jugando con PHP SDK y SQS

Hace un tiempo que quería empezar a usar SQS para separar la lógica de envío de correos de mi plataforma, en especial desde que tuve problemas en su momento con el servidor SMTP de Mandrill lo que finalizaba en un error para el usuario. En este momento cambie a usar el sistema de mails de Amazon, pero siempre quedé con la intención de separar el procesamiento de emails en caso de tener problemas con esto nuevamente.

Me llamó mucho la atención que los ejemplos de Amazon para el SDK simplemente están basados en S3, por l oque no fue tan sencillo comenzar con SQS como lo hubiera imaginado, casi todos los ejemplos con SQS usaban las librerías antiguas.

Amazon SQS, no solo sirve para poder enviar emails de manera asyncrónica con la aplicación, la verdad es que puedes hacer lo que se te ocurra. Por ahora empezaremos un proyecto desde cero en el cual crearemos mensajes y luego los procesaremos.

Amazon SQS Ejemplo PHP SDK V3
Amazon SQS Ejemplo PHP SDK V3

Leer más

Desarrollando Aplicaciones Web con PHP7.0 y Docker

Docker es un software de manejo de contenedores, mientras que PHP7.0 es la última versión de este lenguaje de programación que incluye mejoras que hacen que la velocidad de su funcionamiento sea 50% más veloz que su predecesor. En este tutorial crearemos un ambiente de desarrollo usando Docker con lo cual no tendremos que instalar ningún otro software ni librería para funcionar.

Origen: ctala/apache2_php7_awsebs – Docker Hub

Docker

Ya hace un tiempo que quería probar como funcionaba Docker para el desarrollo de aplicaciones para no tener que instalar todo nuevamente en mi máquina. Hace unos días ya llegó mi nueva laptop por lo que ya no tengo excusas para no trabajar y se me ocurrió la brillante idea de procastinar aprendiendo y creando imagenes de Docker en vez de iniciar directamente con lo que debía hacer. El resultado fue satisfactorio por lo que estoy muy contento, así que aprovecho de compartir no solo como desarrollar en PHP7.0 usando Docker, si no que además utilizaremos una imagen que creé para este cometido que incluye todo lo necesario para poder desarrollar sin problemas en PHP7.0, además de herramientas que hacen que el proceso sea más sencillo.

Prerequisitos :

  • Tener Docker ya instalado.
  • Los comandos que mostraré son en base a un HOST linux, lo que no quiere decir que la imagen que ocuparemos no funcione con otro HOST.

Leer más

Actualizar Elastic BeanStalk Enviroment para usar PHP7.0 con eb-cli

AWS Elastic Beanstalk es un servicio fácil de utilizar para implementar y escalar servicios y aplicaciones web.

Ahora veremos como hacer el upgrade de la versión de PHP de un servicio ( enviroment ) ya corriendo.

Antes de hacerlo :

  1. Ya debes de estar familiarizado con lo que es ElasticBeanstalk.
  2. Debes de tener los comandos de consola de EB instalasdos ( EB-CLI ).
  3. Asumiremos el upgrade desde una máquina con consola linux.

Creando la configuración inicial.

Antes que todo nos situamos en la carpeta de la aplicación de la cual haremos el upgrade y ejecutamos el comando eb init. Si ya lo habíamos hecho anteriormente usaremos la opción de «interactive» como fue mi caso. Resaltaré en negrita la selección del menú correspondiente.

ctala@BeaTriX-CTMGroup:~/REPOS/tbk-aas-server$ eb init --interactive

Select a default region
1) us-east-1 : US East (N. Virginia)
2) us-west-1 : US West (N. California)
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-south-1 : Asia Pacific (Mumbai)
7) ap-southeast-1 : Asia Pacific (Singapore)
8) ap-southeast-2 : Asia Pacific (Sydney)
9) ap-northeast-1 : Asia Pacific (Tokyo)
10) ap-northeast-2 : Asia Pacific (Seoul)
11) sa-east-1 : South America (Sao Paulo)
12) cn-north-1 : China (Beijing)
(default is 3): 

Select an application to use
1) APP1
2) APP2
3) APP3
4) [ Create new Application ]
(default is 3): 3
Select the default environment. 
You can change this later by typing "eb use [environment_name]".
1) APP3-devel
2) APP3-prod
(default is 1): 2

Select a platform.
1) Node.js
2) PHP
3) Python
4) Ruby
5) Tomcat
6) IIS
7) Docker
8) Multi-container Docker
9) GlassFish
10) Go
11) Java
(default is 1): 2

Select a platform version.
1) PHP 5.4
2) PHP 5.5
3) PHP 5.6
4) PHP 7.0
5) PHP 5.3
(default is 1): 4

Con esto lo que hacemos es decirle a beanstalk como funcionar. Si se creara la aplicación desde cero partiría de inmediato con php7, pero como no es nuestro caso lo que haremos primero es guardar la configuración de nuestro enviroment según lo que tenemos en la nube.

Creando y editando la configuración del enviroment.

ctala@BeaTriX-CTMGroup:~/REPOS/tbk-aas-server$ eb config save app3-prod --cfg app3-prod-config

Configuration saved at: /home/ctala/REPOS/app3/.elasticbeanstalk/saved_configs/app3-prod-config.cfg.yml

Una vez guardada la configuración por defecto la editaremos según lo que necesitemos. En nuestro caso buscamos la línea que define el Stack a ocupar y la editamos.

Cambiaremos esto :

SolutionStack: 64bit Amazon Linux 2016.03 v2.1.3 running PHP 5.6

Por esto

SolutionStack: 64bit Amazon Linux 2016.03 v2.1.3 running PHP 7.0

Si, solo cambiaremos la parte donde sale la versión de PHP. En este caso es sencillo ya que el SolutionStack existe y se llama de esa manera.

Actualizando la configuración en la nube.

Ya teniendo nuestra nueva configuración lo que debemos hacer es subirla a amazon y luego cargarla.

ctala@BeaTriX-CTMGroup:~/REPOS/app3$ eb config put app3-prod-config
ctala@BeaTriX-CTMGroup:~/REPOS/app3$ eb config app3-prod --cfg tbk-aas-server-prod-config 
Printing Status: INFO: Environment update is starting.                                
INFO: Updating environment app3-prod's configuration settings. 
INFO: Created Auto Scaling launch configuration named: awseb-e-4g3mx5ymyq-stack-AWSEBAutoScalingLaunchConfiguration-16CD5LSR5ERIN INFO: Auto Scaling group update progress: Rolling update initiated. Terminating 1 obsolete instance(s) in batches of 1, while keeping at least 1 instance(s) in service. Waiting on resource signals with a timeout of PT30M when new instances are added to the autoscaling group. 
INFO: Auto Scaling group update progress: Temporarily setting autoscaling group MinSize and DesiredCapacity to 2. 
INFO: Environment health has transitioned from Ok to Info. Configuration update in progress (running for 30 seconds). 
INFO: Auto Scaling group update progress: New instance(s) added to autoscaling group - Waiting on 1 resource signal(s) with a timeout of PT30M. 
INFO: Added instance [i-051d72f3a5ba93b63] to your environment.      INFO: Still waiting for the following 1 instances to become healthy: [i-051d72f3a5ba93b63]. 
INFO: Deleted Auto Scaling launch configuration named: awseb-e-4g3mx5ymyq-stack-AWSEBAutoScalingLaunchConfiguration-10200GU7CE7MB 
INFO: Successfully deployed new configuration to environment.

Ahora así de simple hemos actualizado el enviroment para que use la versión de php7 sin tener downtime y manteniendo los servicios relacionados como pueden ser las bases de datos.

Espero que les sirva !