참고 링크 : https://medium.com/@pentacent/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71
여러가지 방법들이 있으나,
지금 할 방법은 로컬 서버에 letsencrypt 설치 후, nginx 컨테이너에 해당 인증서를 볼륨으로 마운트시켜 사용하게 하는 방식.
이경우 certbot 컨테이너도 하나 더 추가해서 사용해야 한다.
해야하는 작업 순서 : docker-compose 수정, nginx 수정, nginx 컨테이너와 certbot 컨테이너 연결, ssl 인증서 발급 , 클라우드 서비스 포트 정책 수정
** 1. docker-compose 파일 수정**
- nginx 포트에 443 포트 추가
- certbot 컨테이너 추가
nginx:
image: nginx
container_name: nginx_service
volumes:
- ../volumes-nginx:/etc/nginx/conf.d
- ../volumes/.static_root/:/static/
ports:
- "80:80"
- "443:443"
depends_on:
- durumi
certbot:
image: certbot/certbot
2. nginx 설정파일 수정
- 마운트되는 nginx.conf 설정파일에 ssl 관련 내용 추가. server를 하나 더 만들어서 ssl 내용으로 바꿔주자.
- 이상태로 시작하면 ssl 인증 키 파일이 없어서 서버 시작 불가능하다.
server {
listen 80;
server_name localhost;
location / {
return 301 https://$host$request_uri;
}
location /static/ {
alias /static/;
}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
server {
location / {
proxy_pass http://durumi/;
}
location /static/ {
alias /static/;
}
listen 443 ssl;
server_name localhost;
}
3. nginx 컨테이너와 certbot 컨테이너 연결
- lets encrypt는 유효한 URL에 대해 인증서를 할당하기 위해 사용자가 요청한 URL이 유효한지 검사하는 작업을 거친다. ("challenge"라 불리는 요청 작업)
- 현 상태에서는 서버 시작이 불가하기 떄문에, 유효 URL 체크 작업이 힘듬. 따라서 이미지 마운트 기능을 통해 certbot 컨테이너에서 nginx컨테이너로 인증서 파일을 직접 넘겨주자.
- 아래 작업은 certbot 컨테이너에서 인증서를 발급받아 로컬 서버에 마운트 된 디렉토리에 저장하고, nginx 컨테이너는 동일한 디렉토리를 마운트해 동일한 인증서 파일을 공유하는 작업이라고 생각하면 됨.
- docker-compose 파일에서 certbot 부분 volumes 추가
certbot:
image: certbot/certbot
container_name: certbot_service
volumes :
- ../volumes/certbot/conf:/etc/letsencrypt
- ../volumes/certbot/www:/var/www/certbot
- docker-compose 파일에서 nginx 부분 volumes 추가
- ../volumes/certbot/conf:/etc/letsencrypt
- ../volumes/certbot/www:/var/www/certbot
- 동일 로컬 디렉토리 경로 volumes/certbot 을 지정해주는게 중요함.
- 마지막으로, nginx 컨테이너로 넘겨준 파일을 이용해 유효한 챌린지가 되게 해주자. (nginx.conf파일) 아래 내용을 80번 포트 섹션에 추가해주면 된다.
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
4. HTTPS 인증서 연결
- nginx 설정파일 수정하여 발급받은 ssl인증서를 사용할 수 있게 해줘야한다. nginx.conf 파일에서 443포트 설정한 server 부분에 해당 내용 추가해주자.
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
- 현재 우리 서버는 도메인을 localhost 로 연결하고 있는데, example.org 자리에 실제 도메인 주소를 넣어줘야 하는듯.
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
- 위 내용은 nginx.conf 파일 맨 끝에 그냥 추가해주면 될듯.
5. 인증서 발급을 위한 준비
-
이제 실제 인증서를 발급받을 순서지만 그 전에, nginx컨테이너에서 바로 인증서를 추가할수는 없으므로
- 더미 증명서를 발급받은 다음
- nginx서버를 시작
- 더미를 삭제하고,
- 실제 증명서를 요청하는 작업
이 필요하다. 원본 문서에서 제공한 자동화 스크립트를 활용하였다.
적용시엔 domain , data_path, email을 적절하게 수정해서 사용해야함. docker-compose 파일과 동일 경로에 넣고 실행해주면 된다.
#!/bin/bash if ! [ -x "$(command -v docker-compose)" ]; then echo 'Error: docker-compose is not installed.' >&2 exit 1 fi domains="expamle.org" rsa_key_size=4096 data_path="../volumes/certbot" email="email@gmail.com" # 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:1024 -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
6. 자동 인증서 갱신
- 해도 되고 안해도 되는 부분이긴 한데, lets encrypt로 발급받은 인증서는 갱신기한이 존재한다. certbot에서 자동으로 갱신 작업을 하게끔 명령어를 설정해주자.
- docker-compose 파일에서 certbot 부분에 해당 내용추가한다. 12시간마다 갱신 여부를 판별하게 하는 동작.
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
- 동일하게 nginx 부분에 해당 내용 추가한다. nginx에서 6시간마다 설정을 다시 로드하게 하는 동작.
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
7. 최종 결과
nginx.conf
upstream durumi {
ip_hash;
server containger_name:port; # 서버의 컨테이너 명
}
server {
listen 80;
server_name localhost;
location / {
return 301 https://$host$request_uri;
}
location /static/ {
alias /static/;
}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
server {
listen 443 ssl;
server_name localhost;
location / {
proxy_pass http://example.org/;
}
location /static/ {
alias /static/;
}
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
}
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
docker-compose 일부
nginx:
image: nginx
container_name: nginx_service
volumes:
- ../volumes-nginx:/etc/nginx/conf.d
- ../volumes/.static_root/:/static/
- ../volumes/certbot/conf:/etc/letsencrypt
- ../volumes/certbot/www:/var/www/certbot
ports:
- "80:80"
- "443:443"
depends_on:
- durumi
command : "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
certbot:
image: certbot/certbot
container_name: certbot_service
volumes :
- ../volumes/certbot/conf:/etc/letsencrypt
- ../volumes/certbot/www:/var/www/certbot
entrypoint : "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
8. 실행
세팅이 끝났으니 실제로 스크립트를 돌려보면, 해당 도메인에 대해 challenge를 실패했다는 문구가 나올것이다.
도메인에 연결되는 서버를 내려놨으니 당연한 것. nginx에서 ssl 관련 부분을 일단 제외하고, docker-compose up로 서버를 올려둔 다음에 해당 명령어 실행해봐야겠다.
뭐가 문젠지 알았다. 도메인과 연결되는 nginx 컨테이너는 켜져 있어야 URL에 대한 challenge가 되는것.
docker-compose up nginx 로 nginx만 켜둔 상태에서 위 스크립트를 실행하면 정상적으로 인증서 발급이 완료된다.
nginx realoading 과정에서 에러가 발생할수도 있지만 큰 문제는 아님.
마지막으로, 클라우드 서비스( 나의 경우는 네이버 클라우드) 에서 ACG 정책을 수정하여 443 포트로 접속이 가능하게 설정을 풀어줘야 한다. 안그러면 당연히 연결이 불가.
뿌듯