Certbot

한국 데비안 사용자 모임
둘러보기로 가기 검색하러 가기

80x15.png 이 문서는 크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.

요약

크론(cron)과 시스템디(systemd)를 사용하여, 레츠 인크립트(Let's Encrypt)의 SSL 인증서를 자동으로 갱신하는 과정을 다룹니다.


Certbot auto renewal process.png

시스템 환경

운영체제 시스템디(systemd) 버전 웹 서버(Nginx) 버전 서트봇(certbot) 버전
Debian GNU/Linux 9.1 (stretch) v232 v1.10.3 v0.28.0

서트봇은 레츠 인크립트(Let's Encrypt)의 SSL 인증서를 발급 및 갱신하는 프로그램입니다.

레츠 인크립트(Let's Encrypt)의 인증서를 자동으로 갱신하기

주의할 점

  • 데비안 9(stretch) 이상의 버전을 사용[테스팅(testing)과 불안정(unstable) 릴리스도 모두 포함]할 경우에는, 크론을 사용해서 인증서를 갱신하면 안 됩니다. 왜냐하면 시스템디(systemd)를 활용(certbot.timer와 certbot.service)하여 매일 2회에 걸쳐 자동으로 인증서를 갱신하기 때문입니다. (서트봇을 설치하면 자동으로 /lib/systemd/system/certbot.timer와 /lib/systemd/system/certbot.service 파일, 그리고 /etc/cron.d/certbot 파일이 생성됩니다. 만약 시스템이 시스템디를 사용한다면 이 파일들 중에서 cerbot.timer와 certbot.service를 사용해서 자동으로 인증서를 갱신합니다. 따라서 시스템 관리자는 인증서 갱신 설정 및 작업을 할 필요가 없습니다.) 만약 시스템 관리자가 별도로 크론탭에 인증서 갱신 명령어를 등록(예외 처리를 하지 않았을 경우)하면, 크론탭의 인증서 갱신 명령과 시스템디를 활용하여 인증서를 갱신하는 명령이 중복으로 실행됩니다. 인증서의 갱신 명령이 여러 번 실행된다고 해서, 시스템에 치명적인 문제를 유발하지는 않습니다. 다만 수행하지 않아도 되는 작업을 반복해서 수행하기 때문에 시스템의 관리 측면에서 볼 때 비효율적입니다. 만약 크론탭에 예외 처리를 하지 않은 명령을 아래와 같이 등록하면, 시스템디를 활용하여 인증서를 갱신하는 작업과 크론탭에 등록된 인증서를 갱신하는 작업이 반복되는 문제가 발생합니다.
0 0,12 * * * /usr/bin/certbot -q renew

그래서 certbot을 설치할 때 자동으로 생성된 /etc/cron.d/certbot 파일에는, 시스템디가 설치되어 있을 때 크론 작업을 수행하지 않도록 예외처리가 적용되었습니다.

# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc.  Renewal will only occur if expiration
# is within 30 days.
#
# Important Note!  This cronjob will NOT be executed if you are
# running systemd as your init system.  If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob.  For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew


  • 데비안 8(jessie) 이하의 버전을 사용할 경우에는 크론을 사용해서 SSL 인증서를 갱신해야 합니다. 아래 명령[1]을 실행하여, 크론탭에 인증서 갱신 작업을 등록하면 매일 2회에 걸쳐 자동으로 인증서를 갱신합니다. 크론탭에 매일 자정과 정오에 인증서 갱신 명령을 수행하도록 지정했지만, 임의의 지연 시간 때문에 인증서 연장 명령어가 정확히 자정과 정오에 실행되지는 않습니다.
$ echo "0 0,12 * * * root python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew" | sudo tee -a /etc/crontab > /dev/null

다음은 지금까지 설명한 내용을 유사코드(pseudo-code)로 간략히 작성한 것입니다. 자세한 내용은 플로 차트를 확인해 주십시오.

IF ((릴리스_유형 = stable AND 데비안_버전 ≥ 9.0) OR (릴리스_유형 = testing) OR (릴리스_유형 = unstable)) THEN

    IF (시스템디를 사용) THEN
       // 시스템디(systemd)를 사용하여, SSL 인증서의 갱신을 자동화한다.
       certbot.timer 실행
       certbot.service 실행    
   ELSE
         크론(cron)을 사용하여, SSL 인증서의 갱신을 자동화한다.
   ENDIF
   
ELSE
    크론을 사용하여, SSL 인증서의 갱신을 자동화한다.
ENDIF

systemd를 활용하여 인증서를 갱신하는 과정

/lib/systemd/system/certbot.timer

[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar(인증서_갱신_시각)부분에, 매일 2회(정오, 자정)에 걸쳐 인증서를 자동으로 갱신한다고 설정되었지만, 항상 정오와 자정에 인증서를 갱신하지는 않습니다. 왜냐하면 ‘RandomizedDelaySec(인증서_갱신_지연_시각)=43200’ 옵션이 있기 때문입니다. 이 옵션의 의미는 인증서_갱신_지연_시각을 항상 43200으로 취한다는 것이 아니라, 0 ~ 43200 범위 중에서 임의의 값(즉 0시간 ~ 12시간) 한 개를 선택한다는 의미합니다. 최종적으로 인증서를 갱신할 시각은 아래 공식으로 계산합니다.

인증서_갱신_시각 ← 인증서_갱신_시각 + 인증서_갱신_지연_시각


systemctl list-timers 명령어로 타이머 목록을 확인하면, certbot.timer가 ‘2019-06-30 10:25:47 KST’에 실행되었음을 알 수 있습니다. 그리고 이 다음에 certbot.timer가 실행할 시각은 ‘2019-06-30 15:13:03 KST’입니다. 매일 2회에 걸쳐서 인증서를 갱신한다는 것을 잊지 말아 주십시오.

# systemctl list-timers
NEXT                         LEFT          LAST                         PASSED       UNIT                         ACTIVATES
Sun 2019-06-30 10:39:00 KST  9min left     Sun 2019-06-30 10:09:01 KST  20min ago    phpsessionclean.timer        phpsessionclean.service
Sun 2019-06-30 15:13:03 KST  4h 43min left Sun 2019-06-30 10:25:47 KST  3min 43s ago certbot.timer                certbot.service
Mon 2019-07-01 00:24:11 KST  13h left      Sun 2019-06-30 07:09:48 KST  3h 19min ago apt-daily.timer              apt-daily.service
Mon 2019-07-01 01:21:15 KST  14h left      Sun 2019-06-30 01:21:15 KST  9h ago       systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2019-07-01 06:59:27 KST  20h left      Sun 2019-06-30 06:05:54 KST  4h 23min ago apt-daily-upgrade.timer      apt-daily-upgrade.service

5 timers listed.
Pass --all to see loaded but inactive timers, too.


certbot.timer가 다음에 실행할 시각이 왜 이렇게 되는지 알아보도록 하겠습니다. 우선 journalctl -u cerbot.timer 명령어로 certbot.timer의 로그를 살펴보도록 하겠습니다. 아래 로그를 보면 6월 30일 10시 25분 48초에 두 줄이 기록되었습니다. cerbot.timer가 실행되면 해당 시각의 로그는 두 줄이 기록됩니다. 이때 첫 번째가 아닌 두 번째의 로그에서 임의의 시간(random time) 값을 취합니다.

# journalctl -u certbot.timer
(..생략..)
Jun 29 05:29:28 drupal systemd[1]: certbot.timer: Adding 9h 8min 8.249536s random time.
Jun 29 05:29:28 drupal systemd[1]: certbot.timer: Adding 5h 57min 35.065975s random time.
Jun 29 17:57:38 drupal systemd[1]: certbot.timer: Adding 5h 13min 33.628385s random time.
Jun 29 17:57:38 drupal systemd[1]: certbot.timer: Adding 10h 25min 40.174131s random time.
Jun 30 10:25:48 drupal systemd[1]: certbot.timer: Adding 4h 30min 44.761944s random time.
Jun 30 10:25:48 drupal systemd[1]: certbot.timer: Adding 3h 13min 3.784704s random time.
Jun 30 15:13:12 drupal systemd[1]: certbot.timer: Adding 2h 1min 43.960225s random time.
Jun 30 15:13:12 drupal systemd[1]: certbot.timer: Adding 5h 13min 56.474331s random time.

다음 번에 인증서를 갱신할 시각이 ‘2019-06-30 15:13:03 KST’ 이라는 것은 아래 공식으로 계산할 수 있습니다.

인증서_갱신_시각(2019-06-30 15:13:03 KST) ← 인증서_갱신_시각(12시 00분 00초) + 인증서_갱신_지연_시각(3h 13min 3.784704s)


/lib/systemd/system/certbot.service

[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/python-certbot-doc/html/index.html
Documentation=https://letsencrypt.readthedocs.io/en/latest/
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true


이전 단계에서 certbot.timer를 통해 다음 번에 인증서를 갱신할 시각이 '2019-06-30 15:13:03 KST' 임을 알았습니다.

인증서_갱신_시각(2019-06-30 15:13:03 KST) ← 인증서_갱신_시각(12시 00분 00초) + 인증서_갱신_지연_시각(3h 13min 3.784704s)

현재 시스템의 시각이 인증서_갱신_시각(2019-06-30 15:13:03 KST)과 동일하다면, certbot.service는 '/usr/bin/certbot -q renew' 명령어를 실행합니다. 이때 인증서의 만료 날짜에서 현재 시스템의 날짜를 뺀 값이 30일 이내 일 때만 인증서가 갱신됩니다. 두 값의 차가 30일을 초과할 때는 인증서가 갱신되지 않습니다.

다음은 지금까지 설명한 내용을 유사코드(pseudo-code)로 간략히 작성한 것입니다. 자세한 내용은 플로 차트를 확인해 주십시오.

IF (인증서_만료_날짜 - 현재_시스템_날짜 ≤ 30) THEN
    인증서가 갱신됨
ELSE
    인증서를 갱신하지 않음
ENDIF


journalctl -u certbot.service 명령어로 certbot.service의 로그를 확인하면, 6월 30일 15:13:12에 서트봇이 시작되었음을 알 수 있습니다.

# journalctl -u certbot.service
(.. 생략 ..)
Jun 29 05:29:27 drupal systemd[1]: Starting Certbot...
Jun 29 05:29:28 drupal systemd[1]: Started Certbot.
Jun 29 17:57:37 drupal systemd[1]: Starting Certbot...
Jun 29 17:57:38 drupal systemd[1]: Started Certbot.
Jun 30 10:25:47 drupal systemd[1]: Starting Certbot...
Jun 30 10:25:48 drupal systemd[1]: Started Certbot.
Jun 30 15:13:11 drupal systemd[1]: Starting Certbot...
Jun 30 15:13:12 drupal systemd[1]: Started Certbot.


/var/log/letsencrypt/letsencrypt.log를 살펴보면 아래 내용이 기록되었습니다. '인증서_만료_날짜 - 현재_시스템_날짜'를 계산한 값이 30일을 초과하기 때문에 인증서가 갱신되지 않았습니다.

2019-06-30 15:13:12,103:INFO:certbot.renewal:Cert not yet due for renewal


각주