Search

Node.js Express SSL(Let's Encrypt) 세팅

Node.js Express SSL - Let's Encrypt
Node.js 개발 테스트를 위해 서버를 구축하는 중에 https가 아니라서 socket.io 통신이 안되었다 그래서 Let's Encrypt로 하기 위해 방법을 찾아보았다

설치 환경

Ubuntu 20.04
Node.js v14.+

준비사항

도메인이 미리 준비되어야 한다.
도메인에 A 레코드로 인증서 설치할 서버 아이피를 등록해야 한다.
서버에 아파치 같은 웹서버가 돌아가고 있다면 중지시킴 (80포트)

SSL 인증서 발급

우분투 서버에 Let's Encrypt SSL 인증서를 받는 certbot을 설치
sudo snap install --classic certbot
Bash
복사
내 경우 Node.js기반이기 때문에 standalone 형식을 이용
sudo certbot certonly --standalone
Bash
복사
연락받을 이메일 주소와 약관 동의에 'Y' 해주고 개인 도메인 등을 입력해 주면 발급이 진행이 되고 성공처리가 된다
If you really want to skip this, you can run the client with --register-unsafely-without-email but you will then be unable to receive notice about impending expiration or revocation of your certificates or problems with your Certbot installation that will lead to failure to renew. Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): [이메일 주소] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must agree in order to register with the ACME server. Do you agree? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: Y - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: Y Account registered. Please enter the domain name(s) you would like on your certificate (comma and/or space separated) (Enter 'c' to cancel): [도메인] Requesting a certificate for [도메인] Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/[도메인]/fullchain.pem Key is saved at: /etc/letsencrypt/live/[도메인]/privkey.pem This certificate expires on 2022-02-11. These files will be updated when the certificate renews. Certbot has set up a scheduled task to automatically renew this certificate in the background. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - If you like Certbot, please consider supporting our work by: * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate * Donating to EFF: https://eff.org/donate-le - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Bash
복사
도메인을 입력 후 진행이 안될경우 80포트가 켜져 있는지 확인해 본다 netstat -tnlp
발급이 완료되면 /etc/letsencrypt/live/[도메인]/ 해당 경로 안에 인증서 파일들이 있음을 확인할 수 있다.

Express 적용하기

보통 app.js에서 listen이 1개지만 제 경우는 Node서버와 socket.io 2곳에서 listen을 한다.
하지만 방법은 동일하므로 코드는 아래처럼 수정하였다.

src/config/sslKeys.mjs

import fs from 'fs'; let sslKeys = {}; if (process.env.NODE_ENV === 'production') { // SSL 인증서 키 sslKeys = { ca: fs.readFileSync('/etc/letsencrypt/live/[도메인]/fullchain.pem'), key: fs.readFileSync('/etc/letsencrypt/live/[도메인]/privkey.pem'), cert: fs.readFileSync('/etc/letsencrypt/live/[도메인]/cert.pem'), }; } export default sslKeys;
TypeScript
복사
2곳의 listen에 동일하게 적용하고자 별도의 파일로 빼서 저장하였다.

bin/www.js

'use strict'; import '../src/env.mjs'; import https from 'https'; import sslKeys from '../src/config/sslKeys.mjs'; import app from '../app.mjs'; const PORT = process.env.PORT || 5000; if (process.env.NODE_ENV === 'production') { // server https.createServer(sslKeys, app).listen(443, () => { console.log(`Node.js : 443 포트에서 서버가 가동되었습니다!`); }); } else { // server app.listen(PORT, () => { console.log(`Node.js : ${PORT} 포트에서 서버가 가동되었습니다!`); }); }
TypeScript
복사
bin/www.js에 실행되는 listen에 개발모드와 production모드 구별하여 실행되도록 하였다.
process.env.NODE_ENV의 경우 .env 파일에 저장되어 있는 환경변수 정보이다.

app.mjs

'use strict'; import express from 'express'; import cors from 'cors'; import home from './src/routes/home/index.mjs'; import { socketIO } from './src/socket/socketIO.mjs'; const app = express(); // 소켓 설정 socketIO(app); // middleware app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use('/', home); export default app;
TypeScript
복사
app.mjs에서 socketIO.mjs 호출하면서 app이라는 변수로 express를 인자로 넘긴다.

src/socket/socketIO.mjs

import http from 'http'; import https from 'https'; import sslKeys from '../config/sslKeys.mjs'; import { Server } from 'socket.io'; const PORT_SOCKET = process.env.PORT_SOCKET || 3500; const socketIO = app => { let httpServer; if (process.env.NODE_ENV === 'production') { // server httpServer = https.createServer(sslKeys, app).listen(PORT_SOCKET, () => { logger.info(`socket.io : ${PORT_SOCKET} 포트에서 SSL로 연결되었습니다!`); }); } else { httpServer = http.createServer(app).listen(PORT_SOCKET, () => { logger.info(`socket.io : ${PORT_SOCKET} 포트에 연결되었습니다!`); }); } // socket 서버 const io = new Server(httpServer, { cors: { origin: '*', methods: ['GET', 'POST'], }, }); io.on('connection', socket => { // 접속자 수 socket.emit('usercount', { count: io.engine.clientsCount }); }); return io; }; export { socketIO };
TypeScript
복사
socket.io의 경우에도 크게 다르지 않은 걸 확인할 수 있다.

crontab을 이용하여 인증서 자동 갱신 하기

crontab 설치

# cron 설치 sudo apt update -y sudo apt install -y cron # cron 시작 sudo service cron start # cron systemctl 활성화 sudo systemctl enable cron.service # cron systemctl 등록 확인 sudo systemctl list-unit-files | grep cron sudo service cron status
Bash
복사

crontab 설정

# crontab에 등록하기 sudo crontab -e # 2개월마다 1일에 인증서를 갱신하고 pm2로 서버를 재시작 하겠다는 뜻 0 0 1 */2 * /usr/bin/certbot renew --renew-hook="sudo pm2 restart app" # 잘 등록되었는지 목록 확인 sudo crontab -l # crontab 실행 로그 확인 view /var/log/syslog
Bash
복사
참고
아래 블로그 내용을 상당 부분 참고하였으며 공유해주신 분에게 감사 인사를 올리는 바이다.
ⓒ VeriCras 2024
vericras@gmail.com