이런 글 있는 줄 알았으면 삽질 덜 했지...
알아두면 쓸데없는 배경 설명
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 설정 변경
- 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
붙여야 하지...)
- 원래 서버 설정 고치기
원래 443 포트로 서빙하던 것을 4433 포트로 고쳐두고
- 설정 파일 문법 검사 후 적용
nginx -t
systemctl reload nginx
Dehydrated 설치 및 설정
- Dehydrated 설치
사실 그냥 셸스크립트라 설치라고 할 것이 없고...
GitHub 저장소 clone!
그리고 저장소의 tls-alpn-01 지원 글 참고해서 응답기 역할하는 Python 스크립트도 준비!
- 설정 폴더 및 파일 생성
$ mkdir /etc/dehydrated
$ cp <path/to/tool>/docs/example/config /etc/dehydrated/config
복사한 설정 파일을 열어서
CA -- 일단 테스트용 staging 서버로 변경
CHALLENGETYPE -- tls-alpn-01로 설정
/etc/dehydrated/domains.txt
파일 만들고 그 안에 서버 도메인 주소 입력!
Nginx 설정...은 아까 했고...
잘 돌아가는지 테스트!
등록키 받아오고
$ <path/to/tool>/dehydrated --register --accept-terms
응답기 역할을 할 Python 스크립트 실행시켜두고
$ python3 <path/to/responder>
인증서 생성!(-c 또는 --cron 하면 알아서 생성/관리 작업 수행)
$ <path/to/tool>/dehydrated -c
- 실제 인증서 받아오기
설정 파일을 다시 열어서 staging 서버에서 실제 서버 주소로 되돌려놓고,
$ <path/to/tool>/dehydrated --register --accept-terms
$ <path/to/tool>/dehydrated -c
한 번 더 해주면 /etc/dehydrated/certs/<domain>/
에 인증서가 똭!
아까 띄워둔 Python 응답기는 이제 꺼도 되겠죠.
- 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는 다른 멋진 방법으로 잘 하실 수 있겠죠.