본문 바로가기

배운내용 정리

Nginx + Certbot을 통한 Https 및 TimeZone 적용

기존에 배포한 서버에서 Nginx + Certbot을 통해 서비스에 https를 적용해 보려고 한다.

Nginx

  • 경량 웹서버 
  • event-driven 구조로 동작하여 하나 또는 고정된 개수의 프로세스만 생성 따라서 동시성으로 처리 적은 자원으로 효율적
  • Nginx는 master process와 요청을 처리하는 worker process로 구성 
  • Nginx는 event-driven모델 사용하여  worker process 사이에 요청을 효율적으로 분배하기 위해 OS에 의존적인 메커니즘 사용
  • Worker process의 개수는 설정 파일에서 적용 및 정의된 프로세스 개수와 사용가능한 CPU 코어 숫자에 맞게 자동으로 조정

Certbot

  • Https를 사용하려면 CA(인증기관)에서 가져온 인증서 필요
  • certbot은 개방형 무료 인증기관 Let's Encrypt에서 인증서를 자동으로 발급해 주는 무료 오픈소스 도구이다.

Https를 적용하기 위해서는 3개의 과정이 필요하다.

Certbot 인증서 발급과 인프라 구성에 따른 nginx.conf파일이 필요하다.

본 프로젝트 인프라에서는 docker를 사용해서 컨테이너를 올려 인프라를 구성하고 있기 때문에 docker를 기준으로 설명을 진행할 것이다.

추가로 프로젝트에서 게시글의 위치 정보에 따른 시간 동기화가 필요하기 때문에 TimeZone을 설정도 같이 진행한다.

 

1. docker-compose.yml파일 작성

2. nginx.conf 작성

3. 인증서 발급 스크립트 작성

 

1. docker-compose.yml

nginx와 certbot의 컨테이너를 실행하기 위해 docker-compose.yml 파일을 작성한다.

참고한 자료를 바탕으로 아래의 코드와 같이 작성했다. 

일단 - /usr/share/zoneinfo/Asia/Seoul:/etc/localtime:ro는 Timezone에 관련된 설정이므로 생략하고 일단 보면 된다.

설정 시 주의할 점은 nginx와 certbot 컨테이너에 설정된 volumes에서 certbot 폴더 경로가 같아야 된다. 

version: '3'
services:
  nginx:
    image: nginx:latest
    restart: unless-stopped
    volumes:
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
      - ./build:/usr/share/nginx/html
      - /usr/share/zoneinfo/Asia/Seoul:/etc/localtime:ro 
    ports:
      - "80:80"
      - "443:443"

  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;'"

 

2. nginx.conf

nginx를 사용하기 위해서는 config 파일을 작성해야 한다.

docker-compose.yml 파일에서 volumes 포함된 파일경로에 작성해 준다. 

위치 ./conf/nginx.conf

http{
  server {
    listen 80;
    server_name ${your_domain};
    server_tokens off;

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

 

인증서 발급받기 전  http에 대한 설정을 통해 nginx를 활성화시켜준다. 인증서가 없기 때문에 초기 실행부터 https설정이 포함될경우 에러가 발생하여 실행이 불가능하다.

 

이후 docker compose 실행하여 컨테이너를 활성화 시켜준다. 최신버전에서는 docker compose와 docker가 통합되어 사용되니 버전 확인 후 아래의 명령어를 실행

최신버전
docker compose up -d
구버전
docker-comopse up -d

만약 docker-compose.yml 파일명이 따로 지정해 줬다면 아래의 명령어를 사용한다. 이후에 sh파일 설정할 때 수정해야 할 부분이 많이 생기므로 웬만하면 docker-compose.yml로 파일명을 사용하기를 권장한다.

docker compose (your-docker-compose-file-name.yml) up -d

 

이후 docker ps  명령어를 사용하여 활성화한 nginx 컨테이너가 정상적으로 동작하는 걸 확인 후 넘어간다.

 

3. 인증서 발급 스크립트 작성

인증서 발급 스크립트는 docker compose를 사용하면 편리하게 발급받게끔 작성된 스크립트가 존재한다.

sudo apt-get update
sudo apt-get install git curl -y

curl -L <https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh> > init-letsencrypt.sh
chmod +x init-letsencrypt.sh

git 다운로드가 귀찮다면 아래의 스크립트를 복사해서 사용한다.

domains의 사용하는 도메인, data_path의 certbot 생성파일 위치(docker-compose.yml 기준 만약 volumes의 위치가 다르다면 그에 맞게 변경), eamil의 정보를 입력해 주면 된다. 

만약 docker의 정보가 최신버전이라면 docker-compose명령어를 docker compose로 변경하여 사용하면 된다. 

 

#!/bin/bash

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

domains=(example.org www.example.org)
rsa_key_size=4096
data_path="./data/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

 

정보를 기입하고 스크립트 파일을 실행

sudo ./init-letsencrypt.sh

실행 시 최초 인증서를 발급받는다.

인증서 발급 완료 후 기존에 작성했던 nginx.conf파일을 수정한다. 혹시 모르니 기존의 nginx.conf파일을 복사해 놓고 수정하는 것을 권장

(ex 기존 nginx.conf -> default.conf 변경 새로 nginx.conf파일 생성) 

 

아래와 같이 수정하면 된다.

server {
        listen 80;
        server_name [your_domain];
        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 [your_domain];
        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/[your_domain]/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/[your_domain]/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location / {
        ...
}

location아래는 클라이언트로부터 받은 요청을 해당 도메인의 서버로 프락시 정보에 관련된 설정이다.

예를 들어 https://yourdomain.com에 접근하면, Nginx는 이를 http://backend_server로 전달하게 된다. 

 

이후에 docker compose를 실행해 주면 https적용은 끝이 난다.

최신버전
docker compose up -d
구버전
docker-comopse up -d

 

Nginx + CertBot을 EC2에 적용하여 서비스하는 애플리케이션에 https 적용

 

이후 데이터의 동기화를 위한 TimeZone 세팅 시작

 AWS EC2의 TimeZone을 변경한다. 서비스의 로깅 및 모니터링 유지보수 관리차원의 이점을 위해서 서버의 TimeZone을 변경해야 한다. 우분투 기준으로 작성

 

1. 현재 타임존을 확인

timedatectl

2. 설정가능한 TimeZone 목록 확인

timedatectl list-timezones

3. TimeZone 설정(한국 기준)

sudo timedatectl set-timezone Asia/Seoul

4. 변경확인

timedatectl

 

 

이렇게 타임존을 변경하고 나서 각 컨테이너에 대한 TimeZone을 설정해야 한다. 서비스에서 사용하는 컨테이너 기준으로 기존에 작성했던 docker-compose.yml에서 설정하는 방법은 다음과 같다.

/usr/share/zoneinfo/Asia/Seoul:/etc/localtime:ro

위와 같은 설정을 통해 EC2 서버에 있는 TimeZone 세팅을 컨테이너의 TimeZone으로 복사하여 적용시키면 된다.

version: '3'
services:
  nginx:
    image: nginx:latest
    restart: unless-stopped
    volumes:
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
      - ./build:/usr/share/nginx/html
      - /usr/share/zoneinfo/Asia/Seoul:/etc/localtime:ro 
    ports:
      - "80:80"
      - "443:443"

  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;'"

다른 방법도 있다. 아래와 같은 방식으로 environment로 설정하는 방법도 있지만 구현했을 당시에는 이 방법이 되는 서비스가 있고 안 되는 서비스가 있어서 적용하기 어려웠다. 따라서 확실하게 변경하기 위해 volumes 할당할 때 서버의 TimeZone세팅을 복사하여 할당해 주는 방식이 확실하게 변경하는 방법이었다.

    environment: 
      TZ: "Asia/Seoul"

 

version: '3'
services:
  nginx:
    image: nginx:latest
    restart: unless-stopped
    volumes:
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
      - ./build:/usr/share/nginx/html
    ports:
      - "80:80"
      - "443:443"
    environment: 
      TZ: "Asia/Seoul"

 

추가적으로 서비스하는 애플리케이션도 마찬가지로 docker-compose.yml에서 TimeZone을 변경하려면 똑같이 세팅해 줘도 되지만 Dockerfile을 통해 TimeZone을 세팅할 수도 있다. 명령어를 보면 dokcer-compose.yml에서 volumes에서 서버의 TimeZone할당했던 방식과 비슷하다는 것을 알 수 있다. 

# 런타임 스테이지
FROM openjdk:21-jdk-slim

# 시간대 설정
ENV TZ=Asia/Seoul
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN mkdir /app

COPY your-service.jar /app/your-service.jar

# 컨테이너 실행 시 JAR 파일 실행
ENTRYPOINT ["java", "-jar", "/app/your-service.jar"]

 

'배운내용 정리' 카테고리의 다른 글

인프라 분산 구현  (0) 2024.05.01
Github Actions + Docker compose 버전관리 + nginx  (0) 2024.04.20
Github Action CI/CD + Docker  (0) 2024.04.02
JDBC  (0) 2023.11.13
Spring IoC와 DI  (0) 2023.11.10