프로젝트 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단계로 이루어져있다.
주기적으로 갱신받기 전에, SSL 인증서를 초기에 발급받아야한다.
일단 docker-compose
를 할 디렉토리에서 docker-compose.yml
을 작성한다.
vi 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를 사용하기 위해선 nginx의 Configuration 파일을 작성해줘야한다.
dockercompose.yml
파일이 있는 폴더에 conf
폴더를 만들어주고 nginx.conf
파일을 작성하겠다.
mkdir conf
vi 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 -f docker-compose.yml up -d
그리고 container들의 상태를 확인해준다.
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
로 수정한다.
아래 괄호부분에 도메인, 아까 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를 적용하러 가보자
vi conf/nginx.conf
로 수정해준다. http로 들어와도 https로 던져주고 SSL 인증서도 적용해주는 내용.
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 / {
...
}
}
이제 자동으로 갱신해서 발급해주는 명령어를 컨테이너 안에서 실행할 수 있게 추가해준다.
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 -f docker-compose.yml up -d
마지막으로 container들의 상태를 확인해준다.
docker ps
이 단계들을 모두 성공적으로 마무리 했다면 당신은 이제 SSL 인증서 재발급을 잊어도 좋다! 처음부터 이 방법으로 성공해서 나처럼 귀찮게 재갱신하는 일이 없기를 바란다.
감사합니다,, 덕분에 손 쉽게 시큐리티 설정을 할 수 있었어요!