How do I open my backend NodeJS app for HTTPS?

267 views Asked by At

I would like to open backend NodeJS app for the HTTPS traffic as well. I created the key.pem and cert.pem file (self assigned) inside the backend/secrets app with the command:

$ openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem

Then changed the main.ts file as below:

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ConfigService } from './modules/config/config.service';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './modules/app/app.module';
import { AppLogger } from './modules/app/app.logger';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as cors from 'cors';
import { TransformInterceptor } from './modules/common/interceptors/TransformInterceptor';
import * as express from 'express';
import { ErrorFilter } from './modules/errors/error.filter';
import * as https from 'https'; //ES 6
import fs from 'fs';
async function bootstrap() {
  const logger = new AppLogger();
  logger.info(`NodeJs Version ${process.version}`);
  logger.info(JSON.stringify(process.env));
  const server = express();
  const app = await NestFactory.create(AppModule, new ExpressAdapter(server), {
    logger,
  });
  app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
  const apiVersionPrefix: string = process.env.API_VERSION || 'api';
  app.setGlobalPrefix(apiVersionPrefix);
  app.useGlobalInterceptors(new TransformInterceptor());
  const options = new DocumentBuilder()
    .setTitle('Glee2')
    .setDescription('Glee2 API')
    .setVersion('1.0')
    .addTag('customTag')
    .setBasePath(apiVersionPrefix)
    .addBearerAuth() // here is an intentional compile error. Remove the "x" and the backend should compile.
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup(`api/${apiVersionPrefix}`, app, document);
  const config: ConfigService = app.get('ConfigService');
  const whitelist = config.CORS_WHITELIST;
  const corsOptions = {
    origin(origin, callback) {
      const isOriginAllowed = whitelist.indexOf(origin) !== -1;
      const allowAccessAnyway = whitelist.length === 0;
      if (isOriginAllowed || allowAccessAnyway) {
        callback(null, true);
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
  };
  const httpsOptions = {
    key: fs.readFileSync('./secrets/key.pem'),
    cert: fs.readFileSync('./secrets/cert.pem'),
  };
  app.use(cors(corsOptions));
  app.useGlobalFilters(new ErrorFilter());
  await app.listen(config.PORT);
  await https.createServer(httpsOptions, server).listen(443);
  logger.log(`Listening on port ${config.PORT}.`);
}
bootstrap();

In the AWS EC2 instance, open the port for 443. Port 3030 for the HTTP is working well and return the response:

$ curl -s http://34.201.57.229:3030/api/status

However, the query with the HTTPS is not working and doesn't return anything.

$ curl -s https://34.201.57.229:443/api/status

$ curl -v https://34.201.57.229:443/api/status

*   Trying 34.201.57.229...
* TCP_NODELAY set
* Connection failed
* connect to 34.201.57.229 port 443 failed: Connection refused
* Failed to connect to 34.201.57.229 port 443: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 34.201.57.229 port 443: Connection refused

The issue is port 443 is open for all traffic

enter image description here

How do I make the backend working with HTTPS?

1

There are 1 answers

6
emptyhua On

You can try the code below:

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ConfigService } from './modules/config/config.service';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './modules/app/app.module';
import { AppLogger } from './modules/app/app.logger';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as cors from 'cors';
import { TransformInterceptor } from './modules/common/interceptors/TransformInterceptor';
import * as express from 'express';
import { ErrorFilter } from './modules/errors/error.filter';
import * as https from 'https'; //ES 6
// modified here
import * as http from 'http'; //ES 6
import fs from 'fs';
async function bootstrap() {
  const logger = new AppLogger();
  logger.info(`NodeJs Version ${process.version}`);
  logger.info(JSON.stringify(process.env));
  const app = await NestFactory.create(AppModule, new ExpressAdapter(server), {
    logger,
  });
  app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
  const apiVersionPrefix: string = process.env.API_VERSION || 'api';
  app.setGlobalPrefix(apiVersionPrefix);
  app.useGlobalInterceptors(new TransformInterceptor());
  const options = new DocumentBuilder()
    .setTitle('Glee2')
    .setDescription('Glee2 API')
    .setVersion('1.0')
    .addTag('customTag')
    .setBasePath(apiVersionPrefix)
    .addBearerAuth() // here is an intentional compile error. Remove the "x" and the backend should compile.
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup(`api/${apiVersionPrefix}`, app, document);
  const config: ConfigService = app.get('ConfigService');
  const whitelist = config.CORS_WHITELIST;
  const corsOptions = {
    origin(origin, callback) {
      const isOriginAllowed = whitelist.indexOf(origin) !== -1;
      const allowAccessAnyway = whitelist.length === 0;
      if (isOriginAllowed || allowAccessAnyway) {
        callback(null, true);
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
  };
  const httpsOptions = {
    key: fs.readFileSync('./secrets/key.pem'),
    cert: fs.readFileSync('./secrets/cert.pem'),
  };
  app.use(cors(corsOptions));
  app.useGlobalFilters(new ErrorFilter());

  // modified here
  const httpServer = http.createServer(app);
  const httpsServer = https.createServer(httpsOptions, app);

  httpServer.listen(config.PORT);
  httpsServer.listen(443);
  // modified end

  logger.log(`Listening on port ${config.PORT}.`);
}
bootstrap();