Jace Docs

Dynu & Caddy

Dynu와 Caddy를 조합하여 안정적인 DNS-01 챌린지 환경을 구축하는 단계별 가이드입니다.

DuckDNS에서 겪으셨던 DNS 조회 오류(SERVFAIL)를 방지하기 위해, Caddy가 직접 Dynu API를 통해 인증을 수행하도록 설정하는 것이 핵심입니다.


1단계: Dynu 도메인 및 API 키 준비

  1. 도메인 생성: Dynu Control Panel에서 원하는 호스트네임(예: jace-dev.dynu.com)을 만듭니다.
  2. API 키 발급: API Credentials 메뉴로 가서 API Key를 복사해둡니다. (이 키가 있어야 Caddy가 자동으로 DNS 설정을 변경할 수 있습니다.)

2단계: Dynu 플러그인이 포함된 Caddy 빌드

기본 Caddy 바이너리에는 Dynu 지원 기능이 없습니다. xcaddy를 사용해 플러그인을 넣어 빌드해야 합니다.

# xcaddy 설치 (Go가 설치되어 있어야 합니다)
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# Dynu 플러그인을 포함하여 Caddy 빌드(~/go/bin/xcaddy)
xcaddy build --with github.com/caddy-dns/dynu

빌드가 완료되면 현재 폴더에 caddy 실행 파일이 생깁니다.


3단계: Caddyfile 작성

환경 변수를 활용해 로컬, 테스트, 배포 환경을 하나로 관리하는 구조입니다.

{
    # 전역 설정: 이메일은 Let's Encrypt 알림용
    email your-email@example.com
}

# Snippet: Dynu DNS 인증 설정
(dynu_auth) {
    tls {
        dns dynu {
            api_token {$DYNU_API_KEY}
        }
    }
}

# 메인 사이트 설정
{$SITE_ADDR:localhost} {
    import dynu_auth
    
    # 리버스 프록시 (개발 중인 포트에 맞춰 수정)
    reverse_proxy localhost:3000
    
    log {
        output file ./access.log
    }
}

4단계: 환경별 실행 방법

🏁 로컬 개발 (localhost)

DNS 인증이 필요 없으므로 그냥 실행합니다.

./caddy run

🚀 호스트 테스트 및 배포 (Dynu)

발급받은 API 키와 도메인을 환경 변수로 넘겨줍니다. 80번 포트가 막혀있어도 DNS-01 방식으로 인증서가 발급됩니다.

export DYNU_API_KEY="여러분의_API_키"
SITE_ADDR="jace-dev.dynu.com" ./caddy run

💡 왜 이 방식이 더 좋은가요?

  1. 안정성: http-01(80포트) 대신 dns-01 방식을 쓰기 때문에, 어제 겪으신 DuckDNS의 네임서버 불안정이나 통신사 포트 차단 문제에서 자유롭습니다.
  2. 보안: 서버의 80번 포트를 외부로 개방할 필요가 전혀 없습니다.
  3. 와일드카드 지원: 나중에 *.jace-dev.dynu.com 같은 와일드카드 인증서가 필요해져도 설정 변경 없이 바로 사용 가능합니다.

caddy-dynu

에러 메시지 module not registered: dns.providers.dynu는 현재 실행 중인 caddy 바이너리에 Dynu 플러그인이 포함되어 있지 않다는 뜻입니다.

Jace님이 이전에 xcaddy로 빌드하셨더라도, 실행할 때 시스템에 미리 설치되어 있던 기본 caddy를 실행했거나 빌드된 파일을 제대로 참조하지 못했을 가능성이 큽니다.

1. 현재 실행 파일 확인하기

먼저, 현재 사용 중인 caddy가 어떤 모듈을 가지고 있는지 확인해 보세요.

./caddy list-modules | grep dynu

만약 아무것도 출력되지 않는다면, 그 파일은 Dynu 기능이 없는 일반 버전입니다.


2. 해결 방법: 다시 빌드하고 정확한 파일 실행

xcaddy로 새로 만든 바이너리를 확실하게 사용해야 합니다.

1단계: 다시 빌드 (출력 파일명 지정) 혼동을 피하기 위해 이름을 caddy-dynu로 지정해서 빌드해 보겠습니다.

xcaddy build --with github.com/caddy-dns/dynu --output caddy-dynu

2단계: 모듈 포함 여부 재확인

./caddy-dynu list-modules | grep dynu

여기서 dns.providers.dynu가 리스트에 나오면 성공입니다.

3단계: 새 파일로 실행 기존 caddy 대신 방금 만든 caddy-dynu를 실행하세요.

export DYNU_API_KEY="여러분의_키"
./caddy-dynu run --config Caddyfile

3. 왜 이런 일이 생기나요? (추측)

  • PATH 문제: 터미널에서 그냥 caddy라고 치면 /usr/bin/caddy/usr/local/bin/caddy에 있는 순정 버전이 우선 실행될 수 있습니다.
  • 파일 덮어쓰기 실패: xcaddy build를 할 때 기존에 caddy가 실행 중이었다면 파일이 교체되지 않았을 수 있습니다.

💡 팁

새로 만든 caddy-dynu 파일을 아예 시스템 경로로 옮겨두면 앞으로 caddy라고만 쳐도 플러그인 버전이 실행됩니다.

sudo mv ./caddy-dynu /usr/local/bin/caddy
caddy list-modules | grep dynu  # 확인

이제 다시 실행해 보시면 tls 블록의 import dynu_auth 부분이 에러 없이 정상적으로 파싱될 것입니다!


서브도메인 사용

로그를 분석해 보니, 서브도메인을 사용할 수 없는 것이 아니라 서브도메인 인증 과정에서 "이전 기록"이 방해를 주고 있는 상태입니다.

1. 현상 분석

로그의 핵심 문구는 이겁니다:

Incorrect TXT record "ly8r..." found at _acme-challenge.docs.jace1.mywire.org

  • 성공: jace1.mywire.org (루트)는 인증서 발급에 성공했습니다.
  • 실패: docs.jace1.mywire.org (서브)는 Let's Encrypt가 확인하러 갔을 때, **현재 Caddy가 심은 값이 아닌 예전 값(또는 다른 값)**이 발견되어 거절당했습니다.

2. 왜 이런 일이 생기나요?

  1. DNS 전파 지연 (DNS Propagation): Caddy가 Dynu API를 통해 새로운 TXT 레코드를 심었지만, Let's Encrypt 서버가 조회했을 때 Dynu의 네임서버(ns1.dynu.com 등)들이 아직 이전 값을 들고 있거나 업데이트된 값을 반환하지 않은 경우입니다.

  2. 중복 레코드: Dynu 관리 페이지에 _acme-challenge.docs.jace1이라는 이름의 TXT 레코드가 이미 수동으로 입력되어 있거나, 이전 시도에서 지워지지 않고 남아있을 수 있습니다.

3. 해결 방법

방법 A: Dynu 관리 페이지에서 직접 정리 (가장 확실)

  1. Dynu 제어판DNS Records 메뉴로 들어갑니다.
  2. TXT 타입의 레코드 중 이름에 _acme-challenge가 포함된 것들을 모두 삭제하세요.
  3. 깨끗한 상태에서 Caddy를 다시 실행합니다.

방법 B: Caddy의 전파 대기 시간 늘리기

Dynu의 네임서버 업데이트가 느릴 수 있으므로, Caddy가 인증을 요청하기 전에 DNS가 퍼질 때까지 더 오래 기다리도록 설정할 수 있습니다. (다만, caddy-dns/dynu 플러그인 버전에 따라 옵션 지원 여부가 다를 수 있습니다.)

방법 C: 와일드카드 인증서 사용 (추천)

매번 서브도메인마다 개별 인증을 하는 대신, *.jace1.mywire.org로 하나만 받는 방식입니다.

# Caddyfile 수정 제안
*.jace1.mywire.org, jace1.mywire.org {
    import dynu_auth
    
    # 도메인에 따라 프록시 분기
    @docs host docs.jace1.mywire.org
    handle @docs {
        reverse_proxy localhost:4009
    }
    
    handle {
        reverse_proxy localhost:4000
    }
}

💡 결론

서브도메인 당연히 사용 가능합니다! 지금 에러는 단지 "예전 TXT 레코드가 아직 DNS 상에 남아 있어서" 발생하는 일시적인 충돌입니다.

  1. Dynu 사이트에서 TXT 레코드를 다 지우고 다시 해보시거나,
  2. 5~10분 정도 뒤에 다시 실행해 보세요. (자동으로 해결되는 경우가 많습니다.)

On this page