Home
프로필

SSL 인증서 적용하기(Nginx)

image

프로젝트에 입힐 도메인을 namecheap에서 샀다. 다른 업체보다 염가인데, 이유인 고로 SSL이 기본으로 제공되지 않기 때문이다. 그런데 이거 직접 입히면 되지 않나? 막연히는 알고 있어서 백엔드를 구현하는 김에 같이 연습하기로 했다. 일단 도메인을 EC2에 연결했고(AWS Route53) 이제 그다음.

💡 Nginx

Nginx는 Reverse Proxy다. 리버스 프록시란 일종의 가교인데, 클라이언트와 서버 사이에 위치하여 클라이언트의 요청은 서버로, 서버의 응답은 클라이언트에게 전달하는 역할을 한다. 이때 클라이언트는 Nginx를 서버로 인식하고 서버는 Nginx를 클라이언트로 인식한다. 이런 구조를 통해 서버의 정보를 숨기고 보안을 강화할 수 있다. 실로 많은 일이 이뤄지는 중간지대일 터이나, 나는 시작이니 SSL 구현에만 눈높이를 맞췄다.

그러니까 지금 하는 일 도메인의 메인 주소로 요청이 들어온다고 생각해보자. SSL이 적용되지 않은 이 요청을 Nginx는 받아, SSL을 입힌 다음 사이트를 https에서 움직이게 한다. 인증서 발급에는 Let's Encrypt(무료)를 사용한다.

💡 Nginx 설치

이 분의 포스트를 참고하여 Docker로 설치했다1. Nest.js, Posgress도 Docker 컨테이너로 운용하고 있는 상황이라 Nginx도 같이 묶는 것이 합리적으로 보였다.


docker run -d --name nginx --network server \
-p 80:80 \
-p 443:443 \
-p 8000:8000 \
-v nginx-settings:/etc/nginx \
-v certbot-volume:/etc/letsencrypt \
-v certbot-volume:/var/www/html \
nginx

별도로 설정하지 않는 이상 docker의 기본 네트워크는 bridge다. 나는 의미를 드러낼 수 있는 server 네트워크를 만들어 위 세개의 컨테이너를 격리시켰다. 포트는 80(http), 443(https)은 필수로 열어야 하고, 테스트를 대비해 8000을 추가로 열어뒀다. 그리고 세팅값을 저장할 수 있도록 별도의 볼륨도 할당해두었다.

볼륨은 거울상이다. 상호연결돼 있어서 한쪽의 변경은 다른 한쪽의 변경을 이끈다. 통상적으로 볼륨에 접근하기보다 컨테이너를 실행하여 설정값을 바꾼다고 한다. 나는 멋모르고 거꾸로 했는데, cd 명령이 docker exec -it보다 익숙했기 때문이다... 이제는 안그럼

💡 SSL 인증서 발급

  1. 발급을 위한 라우팅 설정 Nginx 컨테이너에 들어가서 설정을 해준다.

sudo docker exec -it nginx bash
// 접속 후
cd /etc/nginx/conf.d \
sudo apt-get install vim \
vi httpToHttps.conf

컨테이너 내부에 vi/nano 둘다 없어서 vim을 설치해 파일을 작성했다.


server {
listen 80;
server_name 내도메인.com;
location /.well-known/acme-challenge/ {
allow all;
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}

http://내도메인.com 으로 들어오는 요청에 대한 라우팅이다. .well-known이라고, 정해져있는 경로를 통해 let's encrypt 인증이 들어오기 때문에 꼭 작성해줘야 한다. 저장을 하고 컨테이너를 나와 Certbot을 실행한다.


docker run -it --rm \
-v certbot-volume:/etc/letsencrypt \
-v certbot-volume:/var/www/html \
certbot/certbot certonly --webroot -w /var/www/html -d 내도메인.com

그러면 요청이 들어오면서 인증서 발급 절차가 실행된다. 이메일을 적고 동의한다고 응답하면 끝. SSL 인증서, 인증에 관한 정보들이 볼륨과 연동된 장소들을 통해 발급된다.

502 Bad Gateway 에러 때문에 고생했는데, EC2 보안허용 목록에서 80, 443 포트가 열려 있는지 확인하고 컨테이너 안에 들어가서 nginx -s reload을 꼭 실행해보자. 나는 좀 헤맸다ㅠ

  1. SSL 라우팅 설정 아까 위의 location에서 http로 들어오는 요청을 https로 리다이렉트 시켰다. 이제는 https로 들어오는 요청을 소화할 수 있도록 만들 차례다.
conf.d/내어플.conf

server {
listen 443 ssl;
server_name 내도메인.com;
ssl_certificate /etc/letsencrypt/live/내도메인.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/내도메인.com/privkey.pem;
# location 등 나머지 설정
}

이렇게 설정하면 들어온 요청에 대해 SSL 인증서를 적용하여 응답한다. location에서 서버나 클라이언트로 이어지는 라우팅 설정을 마저 작성한 뒤 reload를 해준다.

💡 자동화 설정

인증서 발급은 끝났지만 여기에는 치명적인 문제가 있다. 무료 인증라서 그런가 3개월마다 재발급을 해줘야 한다는 것. 다행히 모두의 염원처럼 자동화할 수 있는 방법이 있고 유닉스 계열 스케쥴러인 cron을 사용한다.

먼저 vim을 이용해 아래처럼 갱신을 요청하는 스크립트를 만든다. 그리고나서 이 스크립트를 주기적으로 실행하게 cron에 등록할 예정이다.

/usr/local/bin/renew_cert.sh

docker run -it --rm \
-v certbot-volume:/etc/letsencrypt \
-v certbot-volume:/var/www/html \
certbot/certbot renew
docker exec nginx nginx -s reload

실행 가능하도록 권한을 부여해준다.


chmod +x renew_cert.sh

그러고 나서 crontab -e로 cron을 실행한다. 어떤 편집기를 이용할 건지 고르라고 하는데 나는 익숙한 vim을 골랐다. 아니라면 nano가 좋을 것이다. 마지막 줄에 다음을 입력하고 저장한다.


0 0 12,25 * * /usr/local/bin/renew_cert.sh >> /var/log/renew-cert.log 2>&1

매월 12,25일 자정에 인증서 갱신 스크립트를 돌리겠다는 뜻이다. 도메인에 접속해보면 https로 연결되는 것을 확인할 수 있다!




참고문서

Footnotes

  1. 여러 도메인의 Https 갱신 자동화