이런 글 있는 줄 알았으면 삽질 덜 했지...

알아두면 쓸데없는 배경 설명

Let's Encrypt (이하 LE)웹서버를 위한 무료 SSL 인증서 서비스입니다. 무료니까 3개월마다 갱신해야 하고 (자동 갱신 툴 이용하면 괜찮...) 사고 났을 때 어떤 보상도 못 받지만, 무료니까...

그런데 지난 2018년 초에 서버 인증 방식 중 하나인 TLS-SNI-01 방식이 보안 취약점으로 인해 폐기 수순에 돌입했고, 2019년 2월 부로 LE 서비스에서도 완전히 지원 중단되었습니다.

문제는 TLS-SNI-01 방식이 당시 HTTPS 포트만으로 인증이 가능한 유일한 방식이었다는 점입니다. 만약 HTTPS 포트만 열려있고, 1. DNS 서버를 건드릴 수 없는데다, 2. HTTP(80번) 포트도 열 수 없는 상황이라면 답이 없죠.

그 대신 TLS-ALPN-01이라는 HTTPS 포트만으로 인증받는 새 표준이 작년 하반기에 준비되고, 각종 자동 인증 도구 및 LE에서 지원을 시작한 모양입니다. 문제는 가장 널리 쓰이는 Certbot 도구는 아직 지원을 안 한다는 것...

대신 Dehydrated 및 Nginx 최신 버전을 이용해 TLS-ALPN-01 방식으로 인증서를 받아와 보겠습니다.

목표는,

  • Certbot에서 Dehydrated로 인증 도구 갈아타기
  • 무중단 인증서 갱신을 위해, Nginx의 load balancer 이용하기로 함 (Dehydrated의 TLS-ALPN-01 + Nginx 설명 따름)

Nginx 설정 변경

  1. Nginx 공식 저장소의 mainline 버전으로 갈아타기

우분투 18.04 저장소의 nginx는 지원을 안 하는 것 같아서...

  • 공식 저장소 추가
  • 저장소 인증키 추가
  • 설치!
  • 프록시 설정

nginx.conf 하단에 한 줄 붙이고,

include /etc/nginx/tls_alpn_proxy.conf;

tls_alpn_proxy.conf 파일을 만들고 내용으로 아래와 같이...

stream {
  map $ssl_preread_alpn_protocols $tls_port {
    ~\bacme-tls/1\b 127.0.0.1:10443;
    default 127.0.0.1:4433;
  }

  server {
    listen 443;
    listen [::]:443;

    proxy_pass $tls_port;
    ssl_preread on;
  }
}

(TODO 왜 127.0.0.1 붙여야 하지...)

  1. 원래 서버 설정 고치기

원래 443 포트로 서빙하던 것을 4433 포트로 고쳐두고

  1. 설정 파일 문법 검사 후 적용

nginx -t
systemctl reload nginx

Dehydrated 설치 및 설정

  1. Dehydrated 설치

사실 그냥 셸스크립트라 설치라고 할 것이 없고...

GitHub 저장소 clone!

그리고 저장소의 tls-alpn-01 지원 글 참고해서 응답기 역할하는 Python 스크립트도 준비!

  1. 설정 폴더 및 파일 생성

$ mkdir /etc/dehydrated
$ cp <path/to/tool>/docs/example/config /etc/dehydrated/config

복사한 설정 파일을 열어서

CA -- 일단 테스트용 staging 서버로 변경
CHALLENGETYPE -- tls-alpn-01로 설정

/etc/dehydrated/domains.txt 파일 만들고 그 안에 서버 도메인 주소 입력!

  1. Nginx 설정...은 아까 했고...

  2. 잘 돌아가는지 테스트!

등록키 받아오고

$ <path/to/tool>/dehydrated --register --accept-terms

응답기 역할을 할 Python 스크립트 실행시켜두고

$ python3 <path/to/responder>

인증서 생성!(-c 또는 --cron 하면 알아서 생성/관리 작업 수행)

$ <path/to/tool>/dehydrated -c

  1. 실제 인증서 받아오기

설정 파일을 다시 열어서 staging 서버에서 실제 서버 주소로 되돌려놓고,

$ <path/to/tool>/dehydrated --register --accept-terms
$ <path/to/tool>/dehydrated -c

한 번 더 해주면 /etc/dehydrated/certs/<domain>/에 인증서가 똭!

아까 띄워둔 Python 응답기는 이제 꺼도 되겠죠.

  1. Nginx 설정 고쳐서 새 인증서 쓰도록 변경

Certbot과 파일 이름은 같고, 경로가 /etc/letsencrypt/live/<site>/에서 /etc/dehydrated/certs/<site>/로 바뀌었습니다.

$ nginx -t
$ systemctl reload nginx

하면 마무리됩니다.

Cron 작업에 추가하기

아래와 같은 스크립트를 만드세요.

#!/bin/bash

python3 <path/to/responder> &
PID_RESP=$!

<path/to/tool>/dehydrated -c -k <path/to/hook> | logger -t dehydrated

kill $PID_RESP
  • 위 스크립트에서 | logger -t dehydrated는 데비안/우분투 기준으로 시스템 로그에 메시지 남기려고 두었고, /etc/rsyslog.d/etc/logrotate.d/ 안의 파일을 수정해서 syslog 대신 별도 로그 파일에 기록되게 만들 수 있겠습니다.

위에 "hook" 이야기가 있는데, 인증서 갱신될 때 Nginx가 설정을 (=인증서를) 다시 가져오게 (reload) 해야 합니다.
그러니 아래와 같은 hook 스크립트를 만드세요. 공식 샘플 참고.

#!/usr/bin/env bash

deploy_cert() {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"

    echo "* Hook: reloading Nginx settings..."
    systemctl reload nginx
}

HANDLER="$1"; shift
if [[ "${HANDLER}" =~ ^(deploy_cert)$ ]]; then
  "$HANDLER" "$@"
fi

(사실 hook 스크립트에 startup_hook, exit_hook 이용해서 응답기 켜고 끄면 되긴 하겠네요...)

마지막으로 Certbot에 대해 했듯이 Cron 작업으로 만들면 되겠습니다.

$ crontab -e

0 4 * * 1 <path/to/cron_script> >/dev/null

끝!

곧 Certbot이 지원하게 되면 다 헛짓거리 되겠지만, 어쨌든 끝!

Apache는 다른 멋진 방법으로 잘 하실 수 있겠죠.

반응형

+ Recent posts