IT System Management/System

Let's Encrypt 무료 웹 TLS 인증서 발급받아서 사용하기 (TLS-ALPN-01 프로토콜 사용)

iseop 2025. 5. 17. 15:44

RFC 8737

Automated Certificate Management Environment (ACME) TLS Application‑Layer Protocol Negotiation (ALPN) Challenge Extension

 

1. Let's Encrypt의 ALPN-01 검증을 통과하려면 이 스펙을 구현한 구현체가 필요하다.

그 중 하나가 dehydrated이다.

https://github.com/lukas2511/dehydrated

 

GitHub - dehydrated-io/dehydrated: letsencrypt/acme client implemented as a shell-script – just add water

letsencrypt/acme client implemented as a shell-script – just add water - dehydrated-io/dehydrated

github.com

 

2. 깃허브에서 필요한 스크립트와 설정 파일들을 내려받는다. 디렉터리 위치는 달라도 된다.

# 최종 발급될 인증서 저장 위치 생성
mkdir /etc/nginx/certs/

# ALPN 챌린지를 위해 dehydrated가 사용하는 위치 생성
mkdir /var/www/dehydrated/

# dehydrated 설치 및 응답기가 사용하는 디렉터리 생성
mkdir -p /etc/dehydrated/alpn-certs/
cd /etc/dehydrated/

# dehydrated 다운로드
wget https://raw.githubusercontent.com/lukas2511/dehydrated/master/dehydrated
chmod u+x dehydrated

# 설정 파일 다운로드
wget https://raw.githubusercontent.com/lukas2511/dehydrated/master/docs/examples/config
echo "mydomain.net" > domains.txt

 

3. domains.txt에 본인의 도메인 주소를 적는다.

오직 도메인 주소만 적는다.

 

4. 설정 파일이 좀 긴데, 필요한 부분을 설정해야 한다.

본인은 nginx를 사용할 것이라 CERTDIR을 이렇게 설정했다. CERTDIR은 최종적으로 발급된 인증서가 저장될 위치이다. {BASEDIR} 등의 설정이 가능한데, 복잡성을 줄이고 싶다면 절대 경로를 추천한다.

CHALLENGETYPE="tls-alpn-01"
ALPNCERTDIR="/etc/dehydrated/alpn-certs"
CERTDIR="/etc/nginx/certs"

 

5. 이제 Let's Encrypt에 ALPN 요청과 응답을 주고받기 위한 ALPN 응답기가 필요하다. 아래 링크로 가서 샘플 코드를 복사하여 저장한다.

필자는 alpn-responder.py라고 저장했다.

https://github.com/dehydrated-io/dehydrated/blob/master/docs/tls-alpn.md#example-responder

import ssl
import socketserver
import threading
import re
import os

ALPNDIR="/etc/dehydrated/alpn-certs"
PROXY_PROTOCOL=False
FALLBACK_CERTIFICATE="/etc/ssl/certs/ssl-cert-snakeoil.pem"
FALLBACK_KEY="/etc/ssl/private/ssl-cert-snakeoil.key"

''' 중략 '''

if __name__ == "__main__":
    HOST, PORT = "0.0.0.0", 10443
    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler, bind_and_activate=False)
    server.allow_reuse_address = True
    try:
        server.server_bind()
        server.server_activate()
        server.serve_forever()
    except:
        server.shutdown()

여기서 Let's Encrypt의 검증을 받으려면 443 포트로 진행해야 한다. 따라서 10443을 443으로 바꿔준다.

또한, 우분투 24.04 LTS에는 /etc/ssl/certs/ 디렉터리에 ssl-cert-snakeoil 이라는 셀프사인 인증서가 없기 때문에 따로 설치해 주어야 한다.

sudo apt install ssl-cert

 

 

6. 응답기를 실행한다. 443 포트를 사용하므로 sudo로 실행하여야 한다.

chmod u+x alpn-responder.py
sudo ./alpn-responder.py &> /dev/null &

 

7. dehydrate를 아래처럼 실행한다. 처음 실행하면 사용자 등록 및 동의사항을 물어본다.

./dehydrated --register --accept-terms

 

8. 다시 dehydrate를 아래처럼 실행한다.

./dehydrated -c -f config

 

9. ALPN-01 챌린지를 통과하면 아래처럼 서명된 인증서와 개인키, 인증서 체인 파일이 설정한 위치에 저장된다.

필자는 설정 오류로 6번 만에 통과했다. 한 시간에 5회 이상 시도하면 30분 밴 당한다.

me@server:/etc/nginx/certs/mydomain.net$ ls -l *.pem
-rw------- 1 root root 1234 May 16 07:46 cert-0000000000.pem
lrwxrwxrwx 1 root root   12 May 16 07:46 cert.pem -> cert-0000000000.pem
-rw------- 1 root root 1234 May 16 07:46 chain-0000000000.pem
lrwxrwxrwx 1 root root   12 May 16 07:46 chain.pem -> chain-0000000000.pem
-rw------- 1 root root 1234 May 16 07:46 fullchain-0000000000.pem
lrwxrwxrwx 1 root root   12 May 16 07:46 fullchain.pem -> fullchain-0000000000.pem
-rw------- 1 root root  123 May 16 07:46 privkey-0000000000.pem
lrwxrwxrwx 1 root root   12 May 16 07:46 privkey.pem -> privkey-0000000000.pem

fullchain.pem이랑 privkey.pem만 가져다 쓰면 된다. privkey는 정말 잘 관리해야 한다.

 

10. Nginx 설정하기

server {
    listen 443 ssl;
    server_name mydomain.com;
    ssl_certificate /etc/nginx/certs/mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/mydomain.com/privkey.pem;
    ...

 

11. 그러면 갱신할 때는 어떻게 하나요?

[6. 응답기 실행] → [8. hydrated 실행]  [nginx -t && nginx -s reload] 순으로 하면 됩니다.

갱신도 잘 됩니다.

 

12. 왜 쉬운 HTTP validation 안쓰고 어렵게 하나요?

SKT 공유기 사용중이라 TCP 80 포트포워딩이 안 돼서 그랬습니다.