게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌25 - NodeJS - 4년 전 등록 / 일 년 전 수정

nginx와 let's encrypt로 SSL 적용하기(+자동 갱신)

리눅스에서 letsencrypt를 직접 설치합시다. snap을 통해서 설치하는 게 제일 쉽습니다(https://snapcraft.io/certbot) 

snap과 nginx

snap과 nginx를 통해서 letsencrypt를 설치합니다. 예시는 다음과 같습니다.

sudo snap install certbot --classic
sudo apt-get install nginx
sudo certbot --nginx

자신의 이메일을 입력하고, 약관에 동의한 후 원하는 도메인을 입력하면 인증서가 발급됩니다. 이후 서버를 실행하면 됩니다. 참고로 와일드카드 도메인에 대한 인증서를 발급받는 경우 dns 인증이 필요해서 dns쪽 설정을 중간에 할 수도 있습니다. 

기존 서버를 종료하기 싫다면 --webroot옵션으로 해도 됩니다. 이 때는 80번 포트를 http 모듈이 사용하고 있어야 하며, express.static이 가리키는 폴더(public 같은 것)를 --webroot-path로 지정해주어야 합니다. 하지만 그냥 서버를 끄고 standalone 옵션을 하는 것을 더 추천합니다. 

실행 후에는 /etc/letsencrypt/live 디렉터리에 인증서가 저장됩니다. 도메인주소와 동일한 한 이름의 폴더가 생기고 그 안에 fullchain.pem과 privkey.pem이 존재하면 됩니다.

nginx는 /etc/nginx/nginx.conf에 설정 파일이 있습니다. 만약 여기에 include /etc/nginx/sites-enabled/*; 이라는 문구가 있다면 /etc/nginx/sites-enabled 폴더에 default라는 파일명으로 설정이 존재할 겁니다. 이 때는 default를 수정하시면 됩니다. default가 존재하는데 nginx.conf를 수정하면 default가 nginx.conf를 덮어쓸 수도 있습니다.

설정은 대부분 알아서 잡아주지만 안 되는 경우 직접 수정해야합니다. 혹시 설정파일에 sites-enabled/*를 include하는 설정이 있다면 sites-enabled안에 들어있는 파일을수정하세요. listen 80에서는 301로 리다이렉트하고 listen 443에서는 location과 ssl_certificate 와 ssl_certificate_key만 제대로 지정되면 됩니다.

다음 코드를 필요한 도메인에 맞게 수정해서 쓰시면 됩니다.

/etc/nginx/sites-enabled/default

server {
        server_name nodebird.com www.nodebird.com;
        return 301 https://nodebird.com$request_uri;
}
server {
        listen 443 ssl;
        server_name nodebird.com;
        ssl_certificate /etc/letsencrypt/live/nodebird.com-0002/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/nodebird.com-0002/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        location / {
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header X-Real-IP $remote_addr;
                proxy_read_timeout 60; # 초단위
                proxy_connect_timeout 60;
                proxy_send_timeout 60;
                send_timeout 60;
                proxy_pass http://127.0.0.1:3060;
                proxy_redirect off;
       }
}

# managed by Certbot 주석이 붙은 줄은 건들지 마세요.

proxy_set_header 부분은 Header들을 넘겨주는 코드입니다. X-Forwarded-Proto는 https를 할 때 필요하고, Upgrade나 Connection은 웹소켓을 할 때 필요합니다. X-Real-IP와 X-Forwarded-For는 IP를 알아내고 싶을 때 있으면 좋습니다.

timeout 시리즈는 nginx가 얼마나 서버의 응답을 기다려줄지 적는 세팅입니다. 현재 60초가 지나면 504 Gateway Timeout 에러가 발생하도록 되어 있습니다.

참고로 /etc/nginx/nginx.conf는 다음과 같습니다.

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        gzip on;

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
} 

다음은 nginx 관련 명령어인데 참고해주세요. /etc/nginx/nginx.conf가 바뀔 때마다 재시작이 필요합니다. 시작이나 재시작 시 에러가 있다면 보통 문법 에러인 경우가 많습니다. status로 확인 가능합니다.

// 시작
$ sudo service nginx start
$ sudo systemctl start nginx
$ sudo /etc/init.d/nginx start

// 재시작
$ sudo service nginx restart
$ sudo systemctl restart nginx
$ sudo /etc/init.d/nginx restart

// 중지
$ sudo service nginx stop
$ sudo systemctl stop nginx
$ sudo /etc/init.d/nginx stop

// 상태
$ sudo service nginx status
$ sudo systemctl status nginx

// 설정 reload
$ sudo service nginx reload
$ sudo systemctl reload nginx
$ sudo nginx -s reload

// 설정파일 문법 체크
$ sudo nginx -t

자동 갱신하기

cron을 사용하여 자동갱신 하면 쉽습니다.

/etc/cron.d/certbot 파일을 다음과 같이 수정합니다. 파일이 없다면 생성합니다(vim으로)

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root certbot -q renew --nginx --renew-hook 'service nginx reload'

이렇게 하면 매일 두 번씩 갱신을 시도합니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

5개의 댓글이 있습니다.
일 년 전
선생님 제가 이해한것이 맞는지 말씀해주실수 있으십니까 (프론트와 백은 각각 vercel과 lightsail에 배포된상황이며 ssl이슈로 통신이 안되는 상태, 도메인은 구매하지 않았으며 프론트는 vercel에서 제공하는 url을 사용하고 백은 고정 ip주소와 포트번호를 사용하는 상황)
1. lightsail의 ssh를 켜서 저의 프로젝트로 cd명령어를 사용해서 이동한후 위와같이 명령어를 입력
( return 301 https://nodebird.com$request_uri; 이부분에서 전 https가 아직 아니지만 https를 붙여주면 되는겁니까)
( server_name nodebird.com www.nodebird.com; 여기서도 server_name ip주소:포트번호 www.ip주소:포트번호 이렇게 입력하면 되는건가요)
2. lightsail을 재시작한다
3.리액트에서 백의 "https://ip:포트번호"요청을 보내고 받는다.

너무 저질스러운 질문인듯한데 죄송합니다.
일 년 전
원칙적으로는 https를 붙이려면 도메인이 있어야 합니다. 현재 lightsail이 ip만 사용한다면 https를 붙일 수 없습니다. server_name에서는 도메인만 입력합니다. 포트번호는 입력하지 않습니다.
일 년 전
안녕하세요 질문이 있습니다. nginx를 사용안하고 nodejs에 직접 ssl 인증서와 도메인을 적용 시킬려면 sever.listen할때 발급 받은 ssl인증서의 도메인으로 server.listen(port, hostname, () => logger.info(hostname:port)); 이런식으로 작성하면 되나요?
일 년 전
http 모듈 대신 https나 http2 모듈을 사용하시면 됩니다. 그러면 createServer 함수에 인증서 넣는 옵션이 있습니다.
https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener
일 년 전
빠른 답변 너무 감사드립니다.구글링 해도 잘나오지 않아 질문 드립니다.
말씀해주신 답변처럼 아래와 같이 코드를 구성하였습니다.

//ssl 인증서 도메인: aabbcc.com
sslKeys = {
key: fs.readFileSync(`${process.cwd()}/config/ssl/aabbcc.com_Nopasskey.pem`),
cert: fs.readFileSync(`${process.cwd()}/config/ssl/aabbcc.com_cert.pem`)
};

let port = 8008;
const server = https.createServer(sslKeys, app);
server.listen(port, () => logger.info(SERVER_START_MSG));

여기서 궁금한게 이렇게 코드를 작성하였을때
- https://123.123.12.123:8008/user/test 는 정상작동함 (https://www.sslcert.co.kr/tools/online-check-ssl-installation 이 사이트에서 https://123.123.12.123:8008 검색하였을때 정상적으로 나옴..)
- https://aabbcc.com:8008/user/test 는 작동하지 않음

도메인이 제대로 적용되지 않는데 무슨 문제 일까요?...

추가 문의: https://123.123.12.123:8008/user/test 이렇게 크롬 주소창에 요청했을때 reponse 값은 정상적으로 날라오나 https에 빨간줄로 그어져있고 주의 요함이라 표시됩니다. 주소를 클릭했을때 인증서가 올바르지 않다고 뜨나 눌러보면 제가 발급받은 인증서 정보가 보이긴 합니다...

항상 좋은 강의 잘보고 있습니다.
일 년 전
aabbcc.com 도메인을 123.123.12.123 ip에 제대로 연결하셨나요? 도메인 레코드에서 A 레코드로 연결하셔야 합니다. https는 ip를 지원하지 않는 경우가 많아 https://123.123.12.123은 인증서가 올바르지 않다고 뜨는 게 정상입니다.
일 년 전
답변 너무 감사드립니다. 도메인쪽 레코드 설정 값 문제였네요!
3년 전
안녕하세요 그런데 질문이있어요 마지막에 자동 갱신은 매일 날짜가 갱신이 되는건가요 아니면 3달이 지나면 갱신이 되나요? 저걸 생성하고 세줄 입력했는데 인증서 유효기간이 변하지않는데 적용이 안된걸까요? 아니면 3달 지나봐야 알수있을까요.??
3년 전
저 명령어는 매 12시간마다 갱신 요청을 합니다. 다만 갱신은 만료 1달 전부터인가 할 수 있을겁니다. 그래서 2달 뒤에나 확인하실 수 있습니다.
3년 전
아하 그렇군요! 제대로 적용이 안된건지 궁금했는데 감사합니다.
3년 전
제로초님 제가 이해한것이 맞는지 잘 모르겠습니다. nginx conf파일을 들어가보면 server에 listen설정하는곳있잖아요? 그게 만약listen 80으로 설정해두고 proxy_pass http://127.0.0.1:3065;으로 해두면 80번으로 요청을 보내도 3065포트번호로 접근이 가능하다는 의미인가요?.. 또한 실제서비스에서 127.0.0.1이렇게 로컬 주소를 쓰는 이유가 무엇인지 궁금합니다 실제서비스에서는 인스턴스 ip를 사용하는것이 아닌가욥??
3년 전
80번으로 요청 보낼 때 3065번으로 요청을 전달합니다. 127.0.0.1은 내 서버에서 자기 자신을 가리키는 것이고, 인스턴스 ip는 서버 외부에서 인스턴스에 접근할 때를 가리키는 겁니다.
4년 전
정리된 좋은 자료 감사합니다.

한참 헤매고 있었는데 도움 많이 되었습니다.
서버 재시작 과정에서 bind 관련 오류가 자꾸 생겼는데 다른 분들도 같은 오류 생기면

lsof -i tcp:80 // 또는 443
kill -9 프로세스아이디

sudo fuser -k 80/tcp

등으로 프로세스 종료 확인해 보세요.