자체 서명 인증서로 로컬 개발환경에서 HTTPS 적용하기

Published on

개요

로컬 개발환경에서 Self-Signed 인증서 사용하여 HTTPS 서버를 테스트 하고 싶을 때 사용할 수 있는 방법 이다.

예를 들어 Next.js web server 가 localhost:3000 으로 구동되면, https://demo.vrerv.com 으로 접속할 수 있게 하는 방법이다.

방법

  1. 사용하고자 하는 도메인에 대한 IP 를 로컬 hosts 파일에 등록한다.
  2. 루트 인증서를 생성한다.
  3. 도메인 인증서를 생성한다.
  4. nginx 를 설치하고, 인증서를 적용한다.
  5. nginx virtual host 를 설정한다.
  6. 루트 인증서를 OS 신뢰저장소에 등록한다.
  7. nginx 를 구동한다. (도커로 구동할 경우 --add-host 옵션으로 도커 컨테이너 안의 서버가 로컬 서버를 바라보는 ip 를 지정해줘야 한다.)

여기서는 설치 편의상 nginx도커로 설치하나, 로컬에 바로 설치하는 편이 ip 문제에서 좀 더 자유롭다.

데모

아래 repository 를 통해 본 내용을 스크립트로 쉽게 구동할 수 있다.

구성의 예

flowchart LR A["로컬 Frontend Web"] API1["로컬 Backend API-1"] API2["로컬 Backend API-2"] API3["로컬 Backend API-3"] V0["Nginx Virtual Host web"] V1["Nginx Virtual Host API-1"] V2["Nginx Virtual Host API-2"] V3["Nginx Virtual Host API-3"] D["Docker Nginx"] E["DNS, Hosts File"] F["브라우저"] F -- https://demo.vrerv.com --> E --> D --> V0 --> A F -- https://api-01.vrerv.com --> E --> D --> V1 --> API1 F -- https://api-02.vrerv.com --> E --> D --> V2 --> API2 F -- https://api-03.vrerv.com --> E --> D --> V3 --> API3

디렉토리 구조

현재 디렉토리 아래에 다음과 같은 디렉토리 구조로 만들어 관련 파일을 저장한다. docker 구동시 해당 디렉토리 위치를 인수로 넘겨 구동하게 되므로, 만약 위치를 바꾸면 docker 구동시 옵션을 바꿔 줘야 한다. nginx 를 로컬 OS 에 설치할 경우는 해당 디렉토리 위치를 적절히 설정해줘야 한다.

  • ./nginx/ssl - self signed 인증서 저장
  • ./nginx/conf.d - nginx virtual host 설정

인증서 생성

인증서 생성시 openssl 을 사용한 명령을 직접 사용하면 되나, 편의를 위한 스크립트가 있으므로 해당 스크립트를 사용하여 생성할 수 있다. 최종적으로 도메인 인증서가 ./nginx/ssl 아래에 생성되어야 한다. (nginx virtual host 설정에 따라 다른 위치에 생성해도 된다.)

git clone https://github.com/zablik/ssl_cert_generator.git
cd ssl_cert_generator

루트 인증서 생성

루트 인증서는 한번만 만들어 인증서 신뢰 저장소에 등록하면 해당 루트 인증서로 생성되는 모든 도메인 인증서는 신뢰할 수 있는 인증서로 취급된다.

./root_ca.sh

../root 아래에 생성된 rootCA 인증서를 루트 인증서 OS의 신뢰 저장소에 등록한다.

도메인 인증서 Common Name 으로 생성

./domain.sh vrerv.com "*.vrerv.com"

../vrerv.com 아래에 생성된 도메인 인증서를 ./nginx/ssl 디렉토리 아래로 복사한다.

Hosts 파일 편집

hosts 파일에 사용하고자 하는 도메인에 대한 IP 를 등록한다. 모두 로컬 설치된 nginx 로 보내게 될 것이므로, 127.0.0.1 로 설정한다.

  • Linux/Mac OS X - /etc/hosts 의 파일을 수정한다.

  • Windows - C:/Windows/System32/drivers/etc/hosts 파일을 수정한다.

    • nodepad 등의 앱을 관리자 권한을 실행한다.
    • 파일 오픈 선택후 팝업에서 기본 ".txt" 파일만 선택되는 것을 "모든 파일" 을 볼수 있도록 필터를 조절한다.
    • hosts 파일을 연다. 아래 내용을 추가한다.
  • 만약 특정 서버만 로컬로 보고 싶다면 예를 들어 백엔드중 api-1만 로컬로 보게 하고 싶다면, 다른 두 백엔드 서버는 "#" 으로 주석처리하면 된다.

127.0.0.1       demo.vrerv.com
127.0.0.1       api-1.vrerv.com
127.0.0.1       api-2.vrerv.com
127.0.0.1       api-3.vrerv.com

Nginx 설치

nginx 를 OS 자체에 설치하면 더 편리하나, 아래는 docker 로 설치하는 방법을 설명한다.

  • Docker 를 먼저설치한다. - Windows 의 경우 WSL 도 필수 설치한다.
  • docker 로 nginx 를 구동한다.

docker nginx 컨테이너 안에서 컨테이너 밖의 서버들에 접속하기 위해서는 --add-host 옵션을 통해 "dockerhost" 를 docker 에서 바라보는 IP로 지정해줘야 한다. 이 IP 는 도커를 설치하면서 자동 설정되는 네트워크에 의해 결정되는데 리눅스/맥 과 윈도우즈는 해당 IP 를 찾는 방법이 조금 다르다.

Linux/Mac OS X 의 경우 아래와 같이 구동한다. 아래 도커 명령은 pwd 명령을 사용함으로 현재 디렉토리가 위치가 아래에 ./nginx 디렉토리가 있어야 한다.

도메인 별 서버는 nginx virtual host 로 개별 파일에 설정한다.

예를 들면, demo.vrerv.com 은 ./nginx/conf.d/demo.vrerv.com.conf 파일에 아래와 같이 설정한다. (파일명은 정하기 나름)

아래 설정에서 proxy_pass http://dockerhost:3000; 부분에 docker 로 설치시 --add-host dockerhost:127.0.0.1 옵션을 줘서 호스트명과 일치 시킨다. 만약 nginx 를 docker 가 아닌 OS 자체에 설치하였다면, proxy_pass http://localhost:3000 로 설정한다.

server {
    listen 80;
    listen [::]:80;

    server_name demo.vrerv.com;

    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name demo.vrerv.com;

    ssl_certificate /etc/nginx/ssl/vrerv.com.crt;
    ssl_certificate_key /etc/nginx/ssl/vrerv.com.key;

    location / {
        proxy_pass http://dockerhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

nginx 구동

docker run --rm --name nginx -d \
    --add-host dockerhost:`ifconfig en0 | grep inet | grep -v inet6 | awk '{print \$2}'` \
    -v `pwd`/nginx/conf.d:/etc/nginx/conf.d \
    -v `pwd`/nginx/ssl:/etc/nginx/ssl \
    -p 80:80 \
    -p 443:443 \
    nginx

Windows 에서는 WSL 콘솔 (Ubuntu 또는 다른 설치된 리눅스 배포판 이름으로 보이는 명령창) 에서 ifconfig 를 실행하여 나오는 IP 를 사용한다. Windows 의 경우 WSL 콘솔 (Ubuntu 또는 다른 설치된 리눅스 배포판 이름으로 보이는 명령창) 에서 docker 를 구동하기 위해 다음을 실행한다.

docker run --rm --name nginx -d --add-host dockerhost:{IP를여기에입력} -v `pwd`/nginx/conf.d:/etc/nginx/conf.d -v `pwd`/nginx/ssl:/etc/nginx/ssl -p 80:80 -p 443:443 nginx

https://demo.vrerv.com 을 호출하면 모두 localhost 로 구동됨을 알 수 있다. 다만, 인증서가 오류가 날것이다. 따라서 아래 인증서 등록 과정을 해야 한다.

루트 인증서 OS 의 신뢰 저장소에 등록

./ssl/rootCA.crt 를 아래와 같이 OS 의 신뢰된 루트 인증서 목록에 추가해야 한다.

  • Mac OS X - 키체인을 구동 해당 인증서를 드래그해서 가져다 놓는다. 드롭된 인증서를 더블 클릭하여 "항상 신뢰"로 설정한다.
  • Windows - ./ssl/rootCA.crt 를 더블 클릭한다. "인증서 설치" 누른다. "로컬 컴퓨터" 선택 후 다음. "모든 인증서를 다음 저장소에 저장" 선택후 "찾아보기" 에서 "신뢰할 수 있는 루트 인증 기관" 선택후 다음. "마침".

Node 인증서 처리

  • Node 에서는 루트 인증서를 OS 등록된 것을 사용하지 않는 것으로 보인다. 따라서 아마 SSR 로 서버사이드 호출시 인증서 에러가 날것이다. 해결법은 Node 자체 관리 저장소에 루트 인증서를 등록하면 되지만, 복잡함으로 그냥 인증서 검증 패스 옵션을 .env 에 설정한다.
# 인증서 유효성 검사 안함 - 개발시만 사용, 운영시 주석 처리
NODE_TLS_REJECT_UNAUTHORIZED=0

구동 확인

https://demo.vrerv.com 을 호출하면 모두 localhost 로 구동됨을 알 수 있다. 만약 서버 포트가 변경되면 conf.d/ 에 각 서버의 백엔드 virtual host 설정에서 포트를 바꿔줘야 한다. 물론 도메인 변경시도 마찬가지이다.

문제

Docker 로 Nginx 를 구동할 경우 아래의 문제가 있다.

안타깝게도 docker container 안에서 host pc 를 보는 IP 가 바뀔수도 있다. 만약 바뀌면 docker container 를 지우고 다시 띄워햐 한다. 지우고 다시 띄우기 위해 docker 구동시 --rm 옵션을 줘서 docker kill시 자동으로 컨텥이너 삭제가 되도록 하는 편이 좋다.