Poner una API en producción es complicado, principalmente porque son varios aspectos los que hay que cuidar. Uno de esos es la seguridad. Cuando exponemos un API siempre queremos utilizar HTTPS para mantener la seguridad de los usuarios y evitar que el tráfico pueda ser registrado por terceros.

En este artículo veremos como exponer una aplicación NestJS vía HTTPS. Para generar el certificado utilizaremos el siempre confiable Let’s Encrypt. Si ya cuentas tanto con certificados y una aplicación NestJS, puedes dirigirte directo al final del artículo para que veas como se implementa en código.

Antes de continuar, solo mencionar que para poder hacer funcionar esto necesitarás lo siguiente:

  • Una servidor con Node instalado
  • Un dominio registrado, el cual debe ser apuntado a nuestra máquina

En mi caso, utilicé un VPS de DigitalOcean. Y para apuntar el dominio solo es necesario crear un registro DNS de tipo A que apunte a la IP de tu servidor.

Paso 1 – Instalar certbot

Primero lo primero, ingresa a tu servidor e instala certbot:

add-apt-repository universe
apt update
apt install certbot software-properties-common nodejs npm git

certbot es la herramienta de Let’s Encrypt para generar certificados SSL. Por otro lado aprovechamos de instalar Node y Git ya que nos será de utilidad más tarde.

Si usas un sistema distinto de Ubuntu 20.04, puedes consultar la guía de instalación de certbot.

Paso 2 – Generar certificado con certbot

Una vez instalado certbot, podrás generar el certificado. Si ya tienes tu dominio apuntando a tu servidor, solo debes ejecutar certbot e indicarle el dominio desde el cual tu servidor es apuntado:

certbot certonly --standalone

Si todo sale bien, certbot nos dirá la ruta donde quedó nuestro certificado.

Primero solicitará tu email para alertar cuando tu certificado esté por caducar.
Dependiendo de la ruta de tu aplicación, deberás mover y reasignar permisos sobre estos archivos. Para no complicarnos, dejaremos estos archivos donde están.

Paso 3 – Generar API NestJS

En este paso vamos a generar un nuevo proyecto NestJS, si ya cuentas con uno, continúa al paso 4, si no, ejecuta los siguientes comandos:

npm install @nestjs/cli -g
nest new nestjs-https-sample

El proyecto base viene con un controlador de ejemplo (app.controller.ts), por lo que no añadiremos nada nuevo a este proyecto.

Paso 4 – Integrar certificado en NestJS

Ahora que ya tenemos todo listo, solo hace falta leer nuestro certificado al arrancar la aplicación NestJS.

Para eso nos vamos al fuente principal main.ts:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

Este código es el responsable de arrancar nuestra aplicación y es donde agregaremos nuestro certificado.

Volviendo un poco atrás, en el paso 2, certbot nos creó una serie de archivos, entre los cuales:

  • fullchain.pem -> es nuestro certificado
  • privkey.pem -> es nuestra llave privada

Es importante que ambos archivos sean cargados. Utilizaremos la misma ruta que nos dejó el paso 2. Veamos cómo debe quedar finalmente el fichero main.ts:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as fs from 'fs';

// definimos la ruta
const crPath = '/etc/letsencrypt/live/testapi.leiva.io/fullchain.pem';
const pkPath = '/etc/letsencrypt/live/testapi.leiva.io/privkey.pem';
const options: any = {};

// validamos si los archivos existen
if (fs.existsSync(crPath) && fs.existsSync(pkPath)) {
  // cargamos los archivos sobre las options
  options.httpsOptions = {
    cert: fs.readFileSync(crPath),
    key: fs.readFileSync(pkPath)
  }
}

async function bootstrap() {
  // le pasamos las options al NestFactory
  const app = await NestFactory.create(AppModule, options);
  await app.listen(8443);
}
bootstrap();

En los comentarios se detallan los pasos que se han de agregar para cargar el certificado:

  1. Definir ruta del archivo de certificado
  2. Definir ruta del archivo de llave privada
  3. Validar si existen
  4. Cargar ambos archivos sobre HttpsOptions
  5. Pasar estas options al NestFactory

La clase NestFactory es la encargada de levantar nuestra aplicación, a esta podemos pasarle una serie de parámetros entre los cuales se encuentra el objeto HttpsOptions, sobre el cual podemos definir dos atributos correspondientes al archivo de certificado (cert) y de llave privada (key).

El validar si los archivos de certificado existen o no, nos permitirá levantar el servicio aún cuando no estén presentes, haciendo un fallback a HTTP plano en tal caso. Esto no siempre será el caso ideal sobre todo en producción, por lo que puedes añadir un control adicional mediante flags para evitar que esto suceda.

Por otro lado, una versión más simplificada sería:

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    httpsOptions: {
      cert: fs.readFileSync('/etc/letsencrypt/live/testapi.leiva.io/fullchain.pem'),
      key: fs.readFileSync('/etc/letsencrypt/live/testapi.leiva.io/privkey.pem')
    }
  });
  await app.listen(8443);
}
bootstrap();

1, 2, 3, probando…

Ahora vamos a desplegar en nuestro servidor. Aprovechando que dejé el código en Github, puedes hacer tu también el mismo ejercicio:

git clone https://github.com/felipeleivav/nestjs-https-sample.git
cd nestjs-https-sample
npm i
npm run start

Deberás modificar la ruta de tus certificados editando el archivo main.ts previo a ejecutar npm run start.

Si cargamos la ruta de nuestra API desde el navegador, vemos que ahora aparece con el candadito de seguridad 🙂

Conclusiones

NestJS es un framework relativamente nuevo, pero que tiene hartas características interesantes. En este caso aprovechamos las HttpsOptions que nos da NestFactory para definir de forma sencilla el certificado con el que levantará nuestra app.