nginx에서 certbot 인증서 갱신 실패할 경우

Ubuntu 14.04 LTS 환경을 기준으로 작성되었습니다.

현재 개발한 웹서비스들에 무료로 HTTPS를 적용하기 위해 Certbot 을 항상 사용하고 있다. Certbot 의 유일한 단점은 인증서가 3개월 뒤에 만료된다는 것이고, 따라서 만료되기 전에 갱신해주어야 한다.
하지만 개발자가 3개월마다 서버에 직접 접속하여 인증서를 갱신하는 것은 상당히 구린 일이다. 이를 해결하기 위해 많은 개발 블로그들에 cronjob 을 이용한 자동 인증서 갱신 방법들이 소개되어 있고, 나 또한 글들을 참고하여 자동 인증서 갱신을 설정해놓았다.
그러나 기대와 달리 인증서는 자동으로 갱신되지 않고, 3개월 뒤 인증서는 결국 만료되었다. 직접 서버에 접속하여 $ certbot-auto renew 인증서 갱신 명령어를 실행해보니 아래와 같이 에러가 나타나며 갱신이 실패하였다.

열심히 구글링을 해보았지만 속시원한 해결법은 나오지 않았고, 한참을 헤맨 끝에 HTTP를 통해 도메인 주소의 유효성을 검증하는 과정에서 HTTPS 주소로 리다이렉트 되었기 때문이라는 것을 알게 되었다.

현재 내가 설정한 nginx.conf 파일의 내용은 아래와 같다(Amazon EC2 서버의 ubuntu 환경에서 django project 배포하기 참고).

http {  
    include       mime.types;
    default_type  application/octet-stream;
    sendfile on;

    server {
        listen       80;
        server_name  jupiny.com;

        client_max_body_size 4G;
        keepalive_timeout 5;

        return 301 https://$server_name$request_uri;
    }

    server {
        listen  443 default_server ssl;
        server_name  jupiny.com;

        (...이하 생략...)
    }
}

여기서 return 301 https://$server_name$request_uri; 이 부분을 주석 처리하고 다시 $ certbot-auto renew 를 실행해보았더니 인증서가 성공적으로 갱신되었다. 하지만 이렇게 주석 처리를 하게 되면 HTTP 주소로 접속시 자동으로 HTTPS 주소로 리다이렉트 되지 않게 된다. 따라서 인증서를 갱신한 후에는 다시 원래대로 설정 파일을 돌려놓아야 한다.

필요한 과정을 다시 정리하면,

  1. nginx.conf 에서 return 으로 시작하는 부분 주석 처리
  2. $ sudo service nginx reload 실행
  3. $ certbot-auto renew 실행
  4. nginx.conf 에서 return 으로 시작하는 부분 주석 해제
  5. $ sudo service nginx reload 실행

이 과정을 그대로 쉘 스크립트에 옮기면 된다.

#!/bin/bash

# OS 환경에 맞는 sed 명령어 설정
case "$(uname)" in  
  Darwin|*BSD) sed="sed -E" ;; # OS X 또는 BSD 환경
  *) sed="sed -r" ;; # GNU 환경
esac

# 파일의 특정 문자열 치환 함수
replace_word() {  
  source=$1
  expr=$2

  tempfile="tempfile"
  sudo mv $source $tempfile
  cat $tempfile | $sed -e "$expr" | sudo tee $source
  sudo rm $tempfile
}

main() {  
  cd $HOME
  nginx_conf_file="/usr/local/nginx/conf/nginx.conf" # nginx.conf의 경로
  replace_word $nginx_conf_file "s/return/# return/"
  sudo service nginx reload

  ./certbot-auto renew --quiet --no-self-upgrade

  replace_word $nginx_conf_file "s/# return/return/"
  sudo service nginx reload
}

main  

파일에서 특정한 줄의 주석을 처리/해제 하는 부분은 sed 명령어를 이용하여 문자열을 치환하였다(리눅스 명령어 정리(5) - sed 참고).
nginx.cnf 파일이 관리자 권한이 필요한 경로에 있기 때문에 명령어 앞에 sudo 를 사용한 것을 볼 수 있다. 명령어의 출력 결과를 파일로 리다이렉트할 때 보통 > 를 사용하지만, 여기서는 관리자 권한으로 실행하기 위해 tee 명령어를 사용하였다.

이제 이 스크립트를 cronjob 으로 돌려주기만 하면 된다.

$ crontab -e
0 19 * * * sh ${HOME}/renew_ssl.sh # 한국 시간 새벽 4시  

하루에 한번씩 스크립트가 실행되고, 인증서 만료 1개월 이내일 경우 인증서가 갱신된다.


참고