Docker와 Nginx를 이용해 Let's Encrypt로 SSL 인증서 적용하기

Docker Compose, Certbot을 활용한 HTTPS 설정 자동화 방법

들어가기 전에

Docker와 Nginx 환경에서 Let's Encrypt를 활용해 SSL/TLS 인증서를 적용한 경험을 기록하고자 해요. 최근 진행한 프로젝트에서 HTTPS 적용이 필요했고, 이를 Docker 환경에서 구성한 방법이에요.

프로젝트에 HTTPS가 필요했던 이유?

다음 3가지 이유가 컸는데요.

기존 환경 소개(Docker, Nginx)

  • Docker를 사용해 각 서비스를 독립적인 컨테이너로 운영

  • Nginx 서버가 각 서비스로 리버스 프록시 역할

  • Docker Compose로 컨테이너와 네트워크 구성

왜 Let`s Encrypt?

SSL 인증서 비교

3가지 옵션을 두고 선정을 했고 주요 특징과 목적에 따라 고려해보았어요.

옵션
특징
비용
목적

외부 CA(ex. AWS)

갱신 주기 1년, EV 인증서

유료

Cloud Platform 종속, 확장가능한 서비스

Let`s Encrypt

갱신 주기 90일

무료

개인 서버

자체 서명(openssl)

인증서 사용해도 브라우저 경고

무료

개발

https를 쓰는 이유와 부합하고 특별히 AWS와 같은 Platform에 종속되어있지 않아 저의 경우엔 Let`s Encrypt를 사용했는데요. 사용 목적에 따라 선정하시면 좋을 것 같아요.

적용 순서

1. Nginx 기본 구성

nginx.conf 파일 구성

events {
  worker_connections  1024;
}

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

  server {
     listen 80;
     server_name [DOMAIN]; // 등록한 도메인

     location /.well-known/acme-challenge/ {
       allow all;
       root /var/www/certbot;
     } 
  }
}

2. Docker Compose 구성

docker-compose.yml 파일 구성

volumes에 들어가는 경로는 저의 서버 폴더 경로로 임의로 지정했습니다(nginx/*)

version: '3'
services:
    nginx:
    container_name: nginx
    image: nginx
    restart: always
    ports:
      - '80:80'  # HTTP 
      - '443:443' # HTTPS
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/letsencrypt:/etc/letsencrypt
      - ./nginx/www:/var/www/certbot
    environment:
      - TZ=Asia/Seoul

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./nginx/letsencrypt:/etc/letsencrypt
      - ./nginx/www:/var/www/certbot
    depends_on:
      - nginx

3. certbot을 활용한 인증서 발급

https://github.com/wmnnd/nginx-certbot/blob/master/init-letsencrypt.sh 를 활용해서 인증서를 발급받았습니다.

curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh > init-letsencrypt.sh
chmod +x init-letsencrypt.sh
vi init-letsencrypt.sh // 도메인, 이메일, 파일 디렉토리 수정
sudo ./init-letsencrypt.sh // 인증서 발급

아래 수정할 init-letsencrtypt.sh의 일부입니다. doamin과 data_path, email을 입력해주시고 script를 사용하였어요

domains=(example.org www.example.org)
rsa_key_size=4096
data_path="./nginx/letsencrypt"
email="jeong9132@gmail.com" # Adding a valid address is strongly recommended
staging=0 # 개발용(1, rate limit를 우회합니다), 운영(0)

최종적으로 {data_path}에 pem 파일들이 생성됩니다.

4. Nginx SSL/TLS 구성

443 port의 server block을 추가하고 ssl pem값을 등록하여 주었어요. 마지막으로 80번으로 들어온 요청에 대해 443으로 리다이렉트(301) 시켜주었습니다.

events {
  worker_connections  1024;
}

http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  
  server {
    listen 443 ssl;
    server_name [DOMAIN] // 등록한 도메인
    
    # https://nginx.org/en/docs/http/configuring_https_servers.html
    ssl_certificate /etc/letsencrypt/live/[DOMAIN]/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/[DOMAIN]/privkey.pem;

    location / {
      proxy_pass http://web;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
    }
  }

  server {
    listen 80;
     server_name [DOMAIN]; // 등록한 도메인

     location /.well-known/acme-challenge/ {
       allow all;
       root /var/www/certbot;
     } 
     
     location / {
       return 301 https://$host$request_uri;
     }
  }
}

결과

(추가) certbot 갱신

{파일경로}는 본인의 파일 경로를 넣어주세요

저는 한국 시간으로 자정으로 매일 실행하고싶어서 아래와 같은 명령어를 등록했어요.

crontab -e
0 15 * * * cd {파일 경로} && docker compose run --rm certbot renew --quiet && docker compose restart nginx

다음 단계.

배운점과 개선점

  • Docker 환경에서의 SSL 적용

  • Let's Encrypt의 짧은 갱신 주기(90일)로 인한 자동화 필요성

  • 클라우드 환경(AWS)과 온프레미스 환경에 따른 적절한 SSL 솔루션 선택

  • 트래픽 증가에 따른 SSL 암/복호화 시스템 부하 개선점 고민

참고 자료

Last updated