떠먹여주는 Nginx Docker에 무료 SSL 적용, 주기적으로 갱신하면서!

NaGyeong Park·2023년 4월 20일
5
post-thumbnail

프로젝트 OAO가 끝난 이후, 무료 인증서는 90개월만 유효하기 때문에 Zero SSL로 적용한 인증서가 끝났다는 메일이 왔었다^^. 그래서 프로젝트 때 하지 못했던 Cert bot으로 SSL 인증서를 적용하고 자동으로 갱신해주는 작업을 했다.
(그 당시 docker의 'd'도 모르는 나는 SSL 인증을 하면서 docker 명령어의 달인이 되었던 기억이 있다. 지금은 다 까먹었지만...)

Nginx and Let’s Encrypt with Docker in Less Than 5 Minutes 을 참고하여 블로그 글을 작성하였다.

yml파일과 docker가 그렇지만, 조금이라도 오타가 나거나 하면 꽤 귀찮아진다. 그런 의미로 이 블로그 글에 내 예시도 같이 넣어두었다.

글의 순서는 준비물, 1단계, 2단계로 이루어져있다.

준비물

  • docker : 없으면 설치
  • docker compose : 없으면 설치
  • 미리 사놓은 도메인 주소
  • 리모트 서버의 80번 포트와 443 포트 열어놓기!

1단계, SSL 인증서 초기 발급

주기적으로 갱신받기 전에, SSL 인증서를 초기에 발급받아야한다.

docker-compose.yml 작성

일단 docker-compose를 할 디렉토리에서 docker-compose.yml을 작성한다.

vi docker-compose.yml

docker-compose.yml 파일

[CUSTOM]에 작성한 폴더 경로는 폴더를 따로 생성해주지 않아도 compose하면서 생성되기에 그냥 편하게 작성하면 된다. 나는 참고한 자료에서 data경로를 사용해서 아래와 같이 작성해줬다.

이때 주의할 점은 nginx container에 적어준 certbot volumes 폴더 경로와 certbot container에 적어준 certbot volumes 폴더 경로가 같아야한다는 것이다.(당연함)

또한 Certbot이 인식하는 이름은 docker-compose.yml 등 이다. 기존에 docker-compose.production.yml, docker-compose.dev.yml 등에 compose 내용을 작성했던 사람은 docker-compose.yml로 이름을 변경해주는게 좋다

version: '3.9'
services:
  nginx:
    image: nginx:latest
    volumes:
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
      - ./[CUSTOM]/certbot/conf:/etc/letsencrypt
      - ./[CUSTOM]/certbot/www:/var/www/certbot
    restart: always
    ports:
      - 80:80
      - 443:443
  certbot:
    image: certbot/certbot
    restart: unless-stopped
    volumes:
      - ./[CUSTOM]/certbot/conf:/etc/letsencrypt 
      - ./[CUSTOM]/certbot/www:/var/www/certbot

예시 파일

version: '3.9'
services:
  nginx:
    image: nginx:latest
    volumes:
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    restart: always
    ports:
      - 80:80
      - 443:443
  certbot:
    image: certbot/certbot
    restart: unless-stopped
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt 
      - ./data/certbot/www:/var/www/certbot

nginx.conf 작성

nginx를 사용하기 위해선 nginx의 Configuration 파일을 작성해줘야한다.
dockercompose.yml파일이 있는 폴더에 conf폴더를 만들어주고 nginx.conf 파일을 작성하겠다.

mkdir conf
vi conf/nginx.conf

nginx.conf 파일

server {
     listen 80;

     server_name [구매한 도메인];

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

예시 파일

우리 프로젝트 oneatonce.com에 적용하기 위해서 아래와 같이 적어주었다.

server {
     listen 80;

     server_name oneatonce.com;

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

docker compose 실행

docker-compose -f docker-compose.yml up -d

그리고 container들의 상태를 확인해준다.

docker ps

docker-ps
잘 살아있는가? 그럼 거의 다한거다.
근데 STATUS가 Restart... 이런 식이면 아직 글렀다. 이럴땐

docker logs [CONTAINER ID]

를 통해서 log를 보면서 해결해보자. 나같은 경우는 ;를 안넣거나 디렉토리 경로를 잘못넣거나 했다.

인증서 발급 스크립트 다운로드

참 친절한 사람이 인증서를 발급받는 스크립트를 다운받을 수 있게 해줬다. 아래 명령어를 실행해서 다운받자.

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로 수정한다.

init-letsencrypt.sh 파일

아래 괄호부분에 도메인, 아까 Certbot 경로, 이메일을 넣어준다.

#!/bin/bash

if ! [ -x "$(command -v docker-compose)" ]; then
  echo 'Error: docker-compose is not installed.' >&2
  exit 1
fi

domains=([구매한 도메인] www.[구매한 도메인])
rsa_key_size=4096
data_path="./[CUSTOM]/certbot"
email="[이메일]" # Adding a valid address is strongly recommended
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits

if [ -d "$data_path" ]; then
  read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
  if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
    exit
  fi
fi


if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
  echo "### Downloading recommended TLS parameters ..."
  mkdir -p "$data_path/conf"
  curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
  curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
  echo
fi

echo "### Creating dummy certificate for $domains ..."
path="/etc/letsencrypt/live/$domains"
mkdir -p "$data_path/conf/live/$domains"
docker-compose run --rm --entrypoint "\
  openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
    -keyout '$path/privkey.pem' \
    -out '$path/fullchain.pem' \
    -subj '/CN=localhost'" certbot
echo


echo "### Starting nginx ..."
docker-compose up --force-recreate -d nginx
echo

echo "### Deleting dummy certificate for $domains ..."
docker-compose run --rm --entrypoint "\
  rm -Rf /etc/letsencrypt/live/$domains && \
  rm -Rf /etc/letsencrypt/archive/$domains && \
  rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
echo


echo "### Requesting Let's Encrypt certificate for $domains ..."
#Join $domains to -d args
domain_args=""
for domain in "${domains[@]}"; do
  domain_args="$domain_args -d $domain"
done

# Select appropriate email arg
case "$email" in
  "") email_arg="--register-unsafely-without-email" ;;
  *) email_arg="--email $email" ;;
esac

# Enable staging mode if needed
if [ $staging != "0" ]; then staging_arg="--staging"; fi

docker-compose run --rm --entrypoint "\
  certbot certonly --webroot -w /var/www/certbot \
    $staging_arg \
    $email_arg \
    $domain_args \
    --rsa-key-size $rsa_key_size \
    --agree-tos \
    --force-renewal" certbot
echo

echo "### Reloading nginx ..."
docker-compose exec nginx nginx -s reload

예시 파일

domains=(oneatonce.com www.oneatonce.com)
rsa_key_size=4096
data_path="./data/certbot"
email="example@oneatonce.com # Adding a valid address is strongly recommended

인증서 발급 스크립트 실행

sudo ./init-letsencrypt.sh

이때 에러가 나면 어떤 에런지 잘 보자. 나는 docker-compose 파일명이 docker-compose.production.yml이어서 스크립트가 실패했었다.

인증서 발급이 완료되면 https를 적용하러 가보자

2단계, HTTPS 적용

nginx.conf 수정

vi conf/nginx.conf로 수정해준다. http로 들어와도 https로 던져주고 SSL 인증서도 적용해주는 내용.

nginx.conf 파일

server {
        listen 80;
        server_name [도메인 이름];
        server_tokens off;

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

}
server {
        listen 443 ssl;
        server_name [도메인 이름];
        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/[도메인 이름]/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/[도메인 이름]/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location / {
        ...
}

예시 파일

server {
        listen 80;
        server_name oneatonce.com;
        server_tokens off;

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

}
server {
        listen 443 ssl;
        server_name oneatonce.com;
        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/oneatonce.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/oneatonce.com/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location / {
        ...
        }
}

docker-compose.yml 수정

이제 자동으로 갱신해서 발급해주는 명령어를 컨테이너 안에서 실행할 수 있게 추가해준다.

docker-compose.yml 파일

version: "3.9"
services:
  proxy:
    image: "nginx:latest"
    ports:
      - "80:80"
      - "443:443"
    restart: always
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./[CUSTOM]/certbot/conf:/etc/letsencrypt
      - ./[CUSTOM]/certbot/www:/var/www/certbot
    command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''

  certbot:
    image: "certbot/certbot"
    restart: unless-stopped
    volumes:
      - ./[CUSTOM]/certbot/conf:/etc/letsencrypt
      - ./[CUSTOM]/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

예시 파일

version: "3.9"
services:
  proxy:
    image: "nginx:latest"
    ports:
      - "80:80"
      - "443:443"
    restart: always
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''

  certbot:
    image: "certbot/certbot"
    restart: unless-stopped
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

docker compose 실행

docker-compose -f docker-compose.yml up -d

마지막으로 container들의 상태를 확인해준다.

docker ps

마무리

이 단계들을 모두 성공적으로 마무리 했다면 당신은 이제 SSL 인증서 재발급을 잊어도 좋다! 처음부터 이 방법으로 성공해서 나처럼 귀찮게 재갱신하는 일이 없기를 바란다.

profile
HAPPY 💌

0개의 댓글