Conectarse a RDS por medio de un Proxy desde una función Lambda
Tags: aws
Published: 2022-06-14 21:01:39

Muchas de las aplicaciones que creamos necesitan una fuente de datos o base de datos relacional.Cuando la aplicación crece vamos a tener una carga significativa a nuestra base de datos, por lo que vamos a realizar mas peticiones y se van a crear mas conexiones a ella, lo que puede llegar a saturar la base de datos en cuanto a cantidad de conexiones. 

Para dar un mejor manejo a las conexiones AWS tiene el componente **Proxy** que se encarga de administrar las conexiones a la base de datos de manera eficiente y evita que podamos llegar a tener errores por el alto numero de conexiones.

En este post vamos a crear un Proxy para que sirva como punto de acceso a nuestra base de datos. El acceso a la base de datos lo vamos a realizar por medio de una función **Lambda**.

Los Pasos a realizar van a ser los siguientes:

* Creación de una base de datos (RDS).
* Creación de un Secreto (Secrets Manager).
* Creación del Proxy.
* Creación de la función **Lambda**.

Iniciemos con la creación de la base de datos, para esto buscamos el servicio **RDS**. Ya en la pagina principal de RDS damos click en Databases y Create Database.

![Principal RDS]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/principal%20RDS.png?raw=true)

Podemos observar que hay dos formas para crear nuestra base de datos: _Standard create _y _Easy create_. Las dos formas nos permiten crear la base de datos solo que la forma _Easy create_ no necesitamos realizar muchas configuraciones, para nuestro caso vamos a utilizar la forma _Standard create_. Aunque pueden usar _Easy create_.

Seleccionamos el motor de base de datos PostgreSQL y dejamos la version que se carga por defecto.

![Select Engine]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/select%20engine.png?raw=true)

Seleccionamos el template free tier para que no genere "costos" (Recuerden eliminar todos los servicios creados al terminar el ejercicio). Esta selección solo se debe utilizar para pruebas ya que son bases de datos pequeñas que no tienen alta disponibilidad ni sus datos se replican.

![Select Template]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/select%20template.png?raw=true)

Ahora asignamos un identificador para la instancia de base de datos, asignamos nombre un usuario y su contraseña.

![Settings]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/settings%20rds1.png?raw=true)

Ahora debemos seleccionar el tipo de instancia que queremos para nuestra base de datos dejamos seleccionado la que viene por defecto que es t3.micro que es una instancia de uso general (En las referencias se encuentra el link hacia los tipos de instancia que maneja AWS).Para el almacenamiento dejamos un disco de propósito general de 20GB. Los demás valores los dejamos por defecto.

![Instance Configuration]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/instance%20configuration.png?raw=true)

El siguiente paso es seleccionar la configuración de red y el lugar de donde queremos que se tenga acceso a nuestra base de datos. Para ambientes productivos siempre es mejor dejar el Public access **_No_**, ya limita el acceso a que sea desde la VPC en la cual se crea nuestra base de datos.

![Conectivity]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/conectivity.png?raw=true)


En la sección _Aditional Configuration_, vamos a encontrar dentro de la sección _Database options_ una lista con el nombre _DB parameter group_. Los parameters groups nos sirven para establecer la configuración del motor de base de datos, siempre es recomendable tener una parameter group diferente al que esta por defecto para entornos de desarrollo y producción.
Para finalizar damos click en el boton _Create Database_.

![Create Database]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/create%20database1.png?raw=true)

En la siguiente pantalla vamos a ver el listado de base de datos que tenemos en esta región. Vamos a dar click en el nombre de la base de datos que acabos de crear para ver el endpoint para conectarnos a ella.

![Create Database]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/list%20databases.png?raw=true)

![EndPoint]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/endpoint.png?raw=true)

Nuestro siguiente paso es crear el Proxy para nuestra base de datos. Para esto vamos al menu lateral izquierdo y seleccionamos _Proxies_ y damos click en el botón Create _Proxy_.

![Create Proxy]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/Proxies1.png?raw=true)

Ahora debemos asignar un nombre al Proxy y seleccionar el tipo de motor de base de datos a utilizar para el Proxy.

![Name Proxy]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/name%20proxy.png?raw=true)

Seguido a esto debemos seleccionar la base de datos a la cual queremos crearle el proxy, seleccionamos la base de datos que lanzamos en pasos anteriores.

![Select Database Proxy]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/select%20database%20proxy.png?raw=true)

En la sección de _Conectivity_ nos pide un secreto (Nos sirve para almacenar información privada o credenciales de bases de datos). Damos click en el enlace _create a new secret_ y nos llevara a la pagina principal de secrets manager, damos click en _Store a new secret_.

![Page Secret]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/page%20secret.png?raw=true)

En esta pantalla debemos seleccionar el tipo de secreto, ingresar el usuario y contraseña que definimos al crear la base de datos y seleccionar la base de datos que va a utilizar este secreto.

![Choose secret]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/choose%20secret%20type.png?raw=true)

Ahora debemos dar un nombre y una descripción a nuestro secreto.

![Name secret]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/name%20secret.png?raw=true)

Para este caso vamos a dejar la configuración por defecto, sin embargo para entornos de producción como buena practica debemos rotar nuestros secretos . 

![Rotate secret]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/rotate%20secret.png?raw=true)

Para finalizar vamos a ver un resumen de la configuración de nuestro secreto y la forma en que podemos acceder a él por medio de algunos lenguajes de programación. Damos click en _Store._

![Store secret]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/store%20secret.png?raw=true)

Una vez creado el secreto regresamos a la ventana donde estábamos creando el proxy y seleccionamos el secreto (es posible que el secreto no aparezca en la lista y debamos actualizar la pagina e ingresar de nuevo los datos) que acabamos de crear y damos click en el botón Create proxy.

![Create Proxy]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/create%20proxy.png?raw=true)

El proxy puede tardar un tiempo en crearse, por lo que debemos esperar un poco a qué se cree.

![Creating Proxy]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/creating%20proxy.png?raw=true)

Hasta el momento hemos creado: La base de datos, un secreto para almacenar las credenciales a la base de datos y el proxy por el cual vamos acceder a la base de datos. Ahora vamos a iniciar la creación de la función Lambda que va a conectarse a la base de datos.

Vamos a la pagina principal de Lambda y damos click en _Create function_.

![Create function]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/create%20function.png?raw=true)

El siguiente paso es definir la configuración de nuestra función, iniciando por asignarle un nombre, el lenguaje de programación y dejamos seleccionado para que se cree un rol por defecto.

![Assign name function]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/assign%20name%20lambda.png?raw=true)

Como el proxy solo se puede usar desde dentro de una **VPC** debemos seleccionar la **VPC** a la cual va a tener acceso. Hay que tener en cuenta que debe ser la misma **VPC** que seleccionamos para la base de datos y el proxy (en este ejemplo estamos utilizando la **VPC** por defecto que trae AWS para esta region).Aparte de la **VPC** debemos seleccionar las subnets desde la cuales la función Lambda puede acceder al proxy. Por ultimo debemos seleccionar el **Security Group** que nos va a permitir el acceso al proxy por medio del puerto que usa Postgres. Terminada la configuración damos click en el botón _Create function_.

![Configure VPC Lambda]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/Lambda%20VPC.png?raw=true)

Como vamos a trabajar con Postgres desde Lambda, debemos utilizar el paquete [psycopg]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/psycopg2.zip) que nos ayuda a tener acceder a   Postgres. Si queremos utilizar paquetes de terceros o creados por nosotros en necesario usar un Layer.(en las referencias se indica como crear Layer).

Para agregar nuestra layer vamos a la parte inferior de la pantalla de la función Lambda y damos click en _Add Layer_.

![Add Layer]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/add%20layer.png?raw=true)

En la siguiente pantalla seleccionamos nuestra Layer y la versión. El numero de versión depende de las veces que la hayamos modificado el Layer.

![Select Layer]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/select%20layer.png?raw=true)

Podemos ver que nuestra Layer se agrego con éxito.

![Show Layer]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/view%20layer.png?raw=true)

Ya que configuramos nuestra Layer debemos asignar al rol de la función Lambda las siguientes políticas. Para esto vamos a la pestaña de _Configuration - Permissions_ damos click en el rol de la función.

![Rol Function]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/select%20rol%20lambda.png?raw=true)

Agregamos las siguientes políticas al rol: _SecretsManagerReadWrite_,_AmazonRDSFullAccess_ y una política personalizada como la siguiente:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:GetSecretValue",
                "secretsmanager:DescribeSecret",
                "secretsmanager:ListSecretVersionIds",
                "secretsmanager:ListSecrets"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```
![Policies Rol]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/policies%20%20rol%20lambda.png?raw=true)

Una vez que terminamos la configuración del rol de la función Lambda vamos agregar el siguiente código.

```
import json
import boto3
import psycopg2

client_secret = boto3.client('secretsmanager')

def lambda_handler(event, context):
    # TODO implement
    
    probar_conexion()
    return {
        'statusCode': 200,
        'body': json.dumps('Test Proxy!')
    }
    
def probar_conexion():
    conn = psycopg2.connect(host="proxy.com",database="postgres",user='postgres',password='12345678')
    print(conn)
    conn.close()
    
def probar_conexion_endpoint():
    response = client_secret.get_secret_value(SecretId='arn:aws:secretsmanager:us-east-1:385033107168:secret:secret-bd-proxy-4l9BKB')
    credentials = json.loads(response["SecretString"])
    conn = psycopg2.connect(host="proxy-database-post.proxy-cty4x2ccvn0y.us-east-1.rds.amazonaws.com",database="postgres",user=credentials["username"],password=credentials["password"])
    print(conn)
    conn.close()
```
La función _probar_conexion_ es la que vamos a utilizar para probar la conexión a nuestro proxy (Es necesario reemplazar los parametros: host,database,user,password por los que establecimos en la configuración de nuestra base de datos). Ahora debemos crear el tipo de prueba, desplegar la función (botón _Deploy_) y ejecutar la función (botón _Test_).


Como ejercicio pueden probar  la función _probar_conexion_endpoint_. Para esto es necesario configurar un EndPoint (Se puede encontrar en la pagina de la VPC) que este dentro de la VPC de la función Lamba y de la base de datos,  en su Security Group le debemos indicar que el Security Group de la función Lambda puede acceder a el.

![EndPoint]
(https://github.com/cecamilo00/posts/blob/circleci-project-setup/proxy/endpoint%20proxy.png?raw=true)

Me pueden encontrar en:

[Camilo Cabrales](https://www.linkedin.com/in/camilo-cabrales-31a0bb53/)


# Referencias

[Proxy](https://docs.amazonaws.cn/en_us/AmazonRDS/latest/UserGuide/rds-proxy-planning.html)

[Tipos de Instancia](https://aws.amazon.com/es/ec2/instance-types/)

[Lambda Layer](https://dev.to/cecamilo/lambda-layer-para-python-1478)

[Secrets Manager](https://dev.to/cecamilo/lambda-layer-para-python-1478)

[Parameters Group](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html)