Develop/Docker

[Docker] Certbot컨테이너를 통한 HTTPS 프로토콜 적용

루시킴 2021. 10. 14. 14:48

현재 웹서버 Nginx는 http 서버로서의 역할을 하고 있기 때문에 HTTP(기본 포트번호 80)로 들어오는 요청을 HTTPS(기본 포트번호 443)로 리다이렉션 하는 작업을 해줘야한다.

즉, Nginx에서 Certbot을 통해 SSL을 발급받아 Https적용해야한다.

 

Let's Encrypt와 Certbot이란?

  • Let's Encrypt는 무료 SSL인증서를 발급해주는 곳이고, Certbot는 Let's Encrypt 인증서를 자동으로 발급 및 갱신을 해주는 프로그램이다.
  • Certbot을 이용해 인증서를 받기위해선 서비스를 운용하는 서버 & 서비스할 도메인 주소 가 필수적으로 필요하다.
  • Certbot으로 받은 인증서(Encrypt)는 3개월 유효기간이 있으므로 주기에 맞추어 갱신이 필요하다.

 

Docker-compose로 flask+gunicorn+react+nginx 모든 서비스를 한번에 올리는 상황이었기 때문에 기존에 구성했던 docker-compose.prod.yml파일과 nginx.conf를 수정해야한다.

 

진행해야할 작업 순서
Cloud network 방화벽 설정 (443포트 열기) => docker-compose 파일 수정 => nginx.conf파일 수정 => ssl 인증서 발급 => 자동 인증서 갱신

 

1. docker-compose파일 수정

  • nginx 서비스에 443 포트 추가
  • certbot 컨테이너 추가
  • certbot 컨테이너에서 인증서를 발급받아 로컬 서버에 마운트 된 디렉토리에 저장하고, nginx 컨테이너는 동일한 디렉토리를 마운트해 동일한 인증서 파일을 공유받을수 있도록 volume설정 추가
nginx:
        build: ./nginx
        container_name: nginx
        ports:
            - "80:80"
            - "443:443"  # nginx에 443 포트 추가
        restart: "on-failure"
        volumes: 
            # - ./nginx/nginx.conf:/etc/nginx/conf.d
            - build_folder:/var/www/Frontend
            - ./certbot/conf:/etc/letsencrypt  #동일한 디렉토리를 마운트
            - ./certbot/www:/var/www/certbot
        command : "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
        depends_on:
            - flask-app
            - react-app
certbot:  #certbot 컨테이너 추가 
        image: certbot/certbot
        restart: unless-stopped
        container_name: certbot
        volumes: 
            - ./certbot/conf:/etc/letsencrypt    #nginx컨테이너에 certbot컨테이너 연결
            - ./certbot/www:/var/www/certbot
        depends_on:
            - nginx
        entrypoint : "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

2. nginx.conf파일 수정

  • https를 위한 서버를 하나더 생성하여 ssl내용으로 수정
  • 기존 nginx.conf에서 80포트를 listen하던 서버 설정을 새로만든 서버 블록으로 옮김 (react와 flask 관련 설정을 443포트 서버에서 처리)
  • 80포트에선 들어오는 모든 요청을 443포트로 리다이렉트 시킴
    upstream api {
        server flask-app:5000;
    }
    #http
    server {
        listen 80;
        server_name weirdmuseum.ml;  #우리 도메인 이름으로 설정
        server_tokens off;
    
        # Allow only for register SSL (Certbot)
        location /.well-known/acme-challenge/ {
            allow all;
            root /var/www/certbot;
        } 
    
        # Http로 들어온 요청을 Https로 Redirect
        location / {
            return 301 https://$host$request_uri;
        }
    }
    #https
    server {
        listen 443 ssl;
        server_name weirdmuseum.ml;
        server_tokens off;
    
        ssl_certificate /etc/letsencrypt/live/weirdmuseum.ml/fullchain.pem;  # ssl 인증서 사용
        ssl_certificate_key /etc/letsencrypt/live/weirdmuseum.ml/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
        location / {
            root /var/www/Frontend;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html?q=$uri&$args;
        }
    
        location /api/ {
            proxy_pass http://api;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            add_header 'Access-Control-Allow-Origin' *;
        }
    }​
     

3. SSL인증서 발급

  • 이상태 에서 docker-compose up을 하면 실행되지 않는다.
  • 문제 : 현재 설정에서 nginx서버를 올리기 위해선 SSL인증서가 필요한데, SSL인증서를 발급받기위해는 nginx서버가 올라가있어야 되는 모순인 상황이다.
  • 해결방법 : 더미증명서를 발급 => nginx서버 시작 => 더미증명서 삭제 및 실제 증명서 요청
  • 이 방법을 자동화시킨 스크립트 파일을 이용하면 된다. 적용시, domain , data_path, email을 우리것으로 바꾸면된다.
  • 해당 스크립트 파일에 실행가능한 권한을 부여후 실행하면 위의 과정을 통해 인증서 발급이 되며 docker-compose의 정의해놓은 서비스들이 컨테이너에 띄워진다.

4. 자동 인증서 갱신

  • 아까 말했듯이, Let's Encrypt에서 발급받은 인증서는 30일후에 만료되기 때문에 자동 인증서 갱신이 필요하다.
  • 아래의 코드를 docker-compose파일에 추가해주면 끝
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" # certbot에서 12시간마다 갱신여부 판별 
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" # nginx에서 6시간마다 설정을 다시 로드

여기까지 하면 Nginx + Certbot를 이용해 HTTPS 프로토콜 적용이 가능하다.