Simulando AWS KMS con LocalStack y Node.js: Una Guía para Desarrolladores

Cristian Cazares - Oct 21 - - Dev Community

Simular servicios de AWS de manera local es una práctica esencial para ahorrar tiempo y costos en el desarrollo. En esta guía, realizada junto a @eslashier, te mostraremos cómo utilizar LocalStack, una herramienta que emula servicios de AWS, y cómo integrarlo con Node.js para trabajar con AWS KMS (Key Management Service). Configurarás un entorno local eficiente que te permitirá desarrollar y probar aplicaciones sin depender de una conexión a la nube.

A continuación, te guiamos paso a paso para instalar y configurar LocalStack con Node.js, permitiéndote emular AWS KMS de manera local. Este proceso te ayudará a crear claves de cifrado y realizar pruebas sin necesidad de conectarte a los servicios reales de AWS, optimizando tu entorno de desarrollo.

1. Instalar Docker
LocalStack se ejecuta dentro de un contenedor de Docker. Si aún no tienes Docker instalado, descarga e instala Docker desde docker.com.

2. Instalar LocalStack
Existen varias formas de instalar LocalStack, como utilizando pip, Homebrew, o descargando binarios directamente. Sin embargo, una de las maneras más rápidas y sencillas es a través de Docker. Con Docker, puedes ejecutar LocalStack en un contenedor sin complicaciones adicionales. Para ello, simplemente ejecuta este comando en la terminal:

docker run --rm -it -p 4566:4566 -p 4571:4571 localstack/localstack
Enter fullscreen mode Exit fullscreen mode

Este comando lanzará LocalStack y expondrá los servicios simulados de AWS en los puertos locales, listos para usarse en tu entorno de desarrollo. Si deseas explorar más opciones de instalación, puedes consultar la documentación oficial de LocalStack.

De esta manera, ofreces una opción rápida y proporcionas el enlace para que los usuarios exploren otras alternativas de instalación.

3. Configurar AWS CLI para LocalStack
Una vez que tienes LocalStack corriendo, el siguiente paso es configurar el AWS CLI para que apunte a tu instancia local. Para esto, usaremos el comando aws configure, pero dado que LocalStack no requiere credenciales reales, puedes usar valores ficticios. Ejecuta el siguiente comando en tu terminal para configurar AWS:

aws configure
Enter fullscreen mode Exit fullscreen mode

Cuando se te pida ingresar las credenciales, introduce los siguientes valores:

  • AWS Access Key ID: test

  • AWS Secret Access Key: test

  • Default region name: us-east-1

Para asegurarte de que estas configuraciones persistan en tu entorno, puedes exportarlas como variables de entorno ejecutando los siguientes comandos:

export AWS_ACCESS_KEY_ID="test"
export AWS_SECRET_ACCESS_KEY="test"
export AWS_DEFAULT_REGION="us-east-1"
Enter fullscreen mode Exit fullscreen mode

Este paso garantiza que cualquier interacción con AWS a través de LocalStack utilice las configuraciones locales y no intente conectarse a los servicios en la nube.

Con esta configuración, ya puedes usar el AWS CLI para realizar operaciones con LocalStack simulando servicios de AWS de manera local.

4. Crear una Clave KMS Asimétrica en LocalStack
Ahora que tienes configurada tu CLI de AWS y estás conectado a LocalStack, el siguiente paso es crear una clave KMS asimétrica. Para esto, utilizaremos el algoritmo RSA 2048, que es ideal para operaciones de cifrado y descifrado. Este tipo de clave es importante cuando necesitas cumplir con requisitos de seguridad más estrictos, ya que proporciona un nivel adicional de robustez criptográfica.

Ejecuta el siguiente comando en la terminal para crear la clave KMS asimétrica:

aws --endpoint-url=http://localhost:4566 kms create-key --description "Mi clave asimétrica RSA" --key-usage ENCRYPT_DECRYPT --customer-master-key-spec RSA_2048
Enter fullscreen mode Exit fullscreen mode

Detalles importantes:

  • –key-usage ENCRYPT_DECRYPT: Esto especifica que la clave se utilizará tanto para cifrar como para descifrar.
  • –customer-master-key-spec RSA_2048: Define que la clave será asimétrica, usando el estándar RSA con una longitud de 2048 bits.

Para obtener más detalles sobre cómo configurar claves KMS asimétricas y sus diferentes usos, puedes consultar la documentación oficial de AWS KMS.

Este paso asegura que estés configurando la clave KMS de manera correcta y siguiendo las mejores prácticas, emulando un entorno AWS real en LocalStack.

5. Configurar Node.js para Usar AWS KMS en LocalStack

Esta configuración divide la lógica en diferentes archivos para mantener el código organizado y modular. A continuación, se explican las partes clave:

  • Archivo: config.ts

Contiene la configuración para conectar con LocalStack y el Key ID generado:

// config.ts
export const awsConfig = {
  endpoint: "http://localhost:4566", // Endpoint de LocalStack
  region: "us-east-1", // Región de LocalStack
  signatureVersion: "v4",
  accessKeyId: "test", // Access Key ID para LocalStack
  secretAccessKey: "test", // Secret Access Key para LocalStack
};

export const keyId = "17680c77-4b4f-4ac1-8581-ba973d09a781"; // Key ID generada

export const paramsSecurity = {
  KeyId: keyId,
  GrantTokens: [keyId],
};
Enter fullscreen mode Exit fullscreen mode
  • Archivo: format.ts

Módulo que formatea la clave pública en formato PEM:

// format.ts
export function formatPublicKey(publicKeyDer: any): string {
  try {
    const publicKeyBase64 = publicKeyDer.toString("base64");
    return `-----BEGIN PUBLIC KEY-----\n${publicKeyBase64.match(/.{1,64}/g)?.join("\n")}\n-----END PUBLIC KEY-----\n`;
  } catch (error) {
    console.error("Error al formatear la clave pública:", error);
    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Clase: EncryptManager

Esta clase maneja la conexión con AWS KMS y las operaciones de cifrado y descifrado:

// encrypt-decrypt.ts
import { KMS } from "aws-sdk";
import * as crypto from "crypto";
import { awsConfig, keyId, paramsSecurity } from "../config/config";
import { formatPublicKey } from "../helper/format";

export class EncryptManager {
  kms = new KMS({ ...awsConfig });

  async getPublicKeyFromKms(): Promise<string> {
    try {
      const data = await this.kms.getPublicKey(paramsSecurity).promise();
      if (data.PublicKey) return formatPublicKey(data.PublicKey);
      throw new Error("La clave no es pública");
    } catch (error) {
      console.error("Error obteniendo la clave pública:", error);
      throw error;
    }
  }

  public encryptKms(plaintextData: string, publicKeyUser: string): string {
    try {
      const publicKey = crypto.createPublicKey({ key: publicKeyUser, format: "pem", type: "spki" });
      const encryptedText = crypto.publicEncrypt(
        { key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: "sha256" },
        Buffer.from(plaintextData, "utf-8")
      );
      return encryptedText.toString("base64");
    } catch (error) {
      console.error("Error durante la encriptación:", error);
      throw error;
    }
  }

  public async decryptKms(encryptedData: string): Promise<string | undefined> {
    try {
      const params = { CiphertextBlob: Buffer.from(encryptedData, "base64"), KeyId: keyId, EncryptionAlgorithm: "RSAES_OAEP_SHA_256" };
      const response = await this.kms.decrypt(params).promise();
      return response.Plaintext?.toString("utf-8");
    } catch (error) {
      console.error("Error durante la desencriptación:", error);
      throw error;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Archivo: index.ts

Ejecuta el proceso de cifrado y desencriptación.

// index.ts
import { EncryptManager } from "./service/encrypt-decrypt";

export class Runner {
  service = new EncryptManager();

  async run(): Promise<void> {
    try {
      console.log("--- Inicio de la encriptación ---");
      const publicKeyPem = await this.service.getPublicKeyFromKms();
      console.log(`Clave pública obtenida:\n${publicKeyPem}`);
      const encrypted = this.service.encryptKms("mensaje de prueba", publicKeyPem);
      console.log(`Mensaje encriptado: ${encrypted}`);
      const decrypted = await this.service.decryptKms(encrypted);
      console.log(`Mensaje desencriptado: ${decrypted}`);
      console.log("--- Fin de la encriptación ---");
    } catch (error) {
      console.error(error);
    }
  }
}

const ctl = new Runner();
ctl.run().then(() => console.log("Proceso finalizado"));
Enter fullscreen mode Exit fullscreen mode

Esta estructura modular facilita la organización y mantenimiento del código. Ahora tienes todo listo para realizar operaciones de cifrado y descifrado con KMS en LocalStack usando Node.js.

Con esta guía has aprendido a configurar LocalStack junto con Node.js para trabajar con claves asimétricas de AWS KMS en un entorno local. Esta configuración modular te permitirá desarrollar y probar aplicaciones sin conectarte a la nube real de AWS, optimizando tiempos y reduciendo costos.

Si deseas explorar, mejorar o reutilizar este ejemplo, puedes encontrar el código completo en el siguiente repositorio de GitHub:
🔗 Repositorio en GitHub - kms-node

¿Qué sigue?
Puedes ampliar este proyecto para:

  • Integrar más servicios simulados de AWS en LocalStack (como S3 o Lambda).

  • Desarrollar flujos más complejos que incluyan autenticación con claves adicionales.

  • Desplegar tu aplicación en producción usando una infraestructura real basada en AWS.

Con esta base, tendrás todas las herramientas para simular y desarrollar de manera eficiente en un entorno controlado antes de escalar hacia la nube. ¡Buena suerte! 🚀

.