본문으로 건너뛰기
피드

Caddy 인증서 만료 원인은 systemd-resolved와 NextDNS 조합이었다

devops 약 10분

Matrix 홈서버 앞단의 Caddy 인증서가 만료됐고, 원인은 Caddy가 아니라 특정 도메인의 NXDOMAIN 응답에서 멈추는 systemd-resolved 경로였어. Docker DNS, host stub resolver, NextDNS over TLS, ACME DNS-01 챌린지가 겹치면서 42시간 동안 갱신 실패가 조용히 누적된 장애 후기야.

  • 1

    Caddy는 10분마다 인증서 갱신을 큐에 넣었지만 DNS-01 챌린지에서 42시간 동안 실패했다

  • 2

    직접 upstream DNS에 질의하면 NXDOMAIN이 즉시 왔지만 systemd-resolved의 127.0.0.53 경로만 타임아웃됐다

  • 3

    문제 경로는 Docker 127.0.0.11에서 host 127.0.0.53을 거쳐 NextDNS DoT로 가는 DNS 체인이었다

  • 4

    임시 조치는 Caddy 컨테이너에 1.1.1.1과 8.8.8.8 DNS를 직접 지정하는 것이었고, 근본 조치는 서버에 불필요한 NextDNS DoT 설정을 제거하는 것이었다

  • 5

    인증서 만료 모니터링과 로그 기반 ACME 갱신 실패 알림이 없으면 자동 갱신 실패를 너무 늦게 알게 된다

장애는 인증서 만료 알림에서 시작됨

  • Matrix 홈서버의 federation 포트 인증서가 만료되면서 알림이 터졌음

    • federation 포트는 다른 Matrix 홈서버가 이벤트를 주고받는 공개 TLS 엔드포인트임
    • 인증서가 신뢰되지 않으면 외부 서버가 메시지를 보내지 못하고, 내 서버도 outbound federation이 깨짐
  • 문제의 인증서는 matrix-fed.takeonme.org용 Let's Encrypt 인증서였음

    • 앞단에는 Caddy 컨테이너가 있었고, Cloudflare DNS-01 챌린지로 자동 갱신하도록 설정돼 있었음
    • 인증서는 2월 초 발급, 5월 3일 만료였고 보통 60일 즈음 갱신됐어야 함
  • 컨테이너 상태는 멀쩡했음

    • Synapse, Element, Postgres, Caddy, Redis 등 Matrix 관련 컨테이너는 전부 떠 있었음
    • 그래서 애플리케이션 장애가 아니라 TLS 엔드포인트의 인증서 갱신 문제로 범위가 좁혀짐

Caddy는 계속 재시도하고 있었지만 실패 중이었음

  • Caddy 로그에는 인증서 갱신 필요 메시지가 반복되고 있었음

    • ACME Renewal Information(ARI)이 갱신 시점을 알려줬고, Caddy의 cache maintenance도 갱신 큐에 넣고 있었음
    • 겉으로는 자동 갱신 루프가 잘 돌고 있는 것처럼 보였음
  • 실제 실패 지점은 DNS-01 챌린지였음

    • Caddy가 _acme-challenge.matrix-fed.takeonme.org의 zone을 찾으려다 SERVFAIL을 받았음
    • 로그에는 29번째 시도, 경과 시간 151,329초가 찍혀 있었는데 대략 42시간 동안 실패한 셈임
    • 다음 재시도는 6시간 뒤로 밀려 있었고, 그 사이 인증서는 이미 만료될 수 있었음

⚠️주의

> 자동 갱신이 켜져 있어도 갱신 실패가 알림으로 올라오지 않으면 그냥 조용히 만료될 수 있음. Caddy 로그에 tls.renew 에러가 쌓이는지 따로 봐야 함.

  • 더 찜찜한 점은 Caddy가 Let's Encrypt staging으로 넘어가 있었다는 것임
    • 반복 실패 후 certmagic이 production 대신 staging CA를 시도한 상태였음
    • staging 인증서는 브라우저나 Matrix 홈서버가 신뢰하지 않기 때문에, 설령 성공해도 운영 장애는 해결되지 않음

진짜 원인은 로컬 DNS 경로였음

  • upstream DNS를 직접 찌르면 응답은 정상적이었음

    • Cloudflare 1.1.1.1, NextDNS, VPS 제공 DNS 모두 _acme-challenge.matrix-fed.takeonme.org에 즉시 NXDOMAIN을 반환함
    • NXDOMAIN은 레코드가 없다는 정상 응답이고, Caddy가 챌린지 때만 TXT 레코드를 만들었다 지우는 구조라 기대한 결과임
  • 그런데 로컬 stub resolver인 127.0.0.53은 타임아웃이 났음

    • 컨테이너 내부에서는 Docker의 내장 DNS 127.0.0.11을 거침
    • 그 다음 host의 systemd-resolved stub resolver 127.0.0.53으로 가고, 거기서 upstream으로 나가는 체인임
    • Caddy 컨테이너에서 보이는 실패는 사실 host resolver의 실패가 전파된 결과였음
sequenceDiagram
    participant 캐디컨테이너
    participant 도커DNS
    participant 호스트리졸버
    participant 넥스트DNS
    participant 렛츠인크립트
    캐디컨테이너->>도커DNS: _acme-challenge SOA 질의
    도커DNS->>호스트리졸버: 127.0.0.53으로 전달
    호스트리졸버->>넥스트DNS: DoT로 질의
    넥스트DNS-->>호스트리졸버: NXDOMAIN 응답
    호스트리졸버--x도커DNS: 특정 zone에서 타임아웃
    캐디컨테이너-->>렛츠인크립트: DNS-01 챌린지 실패
  • 이상한 건 systemd-resolved가 완전히 죽은 게 아니었다는 점임
    • google.com은 바로 풀렸고, 다른 zone의 NXDOMAIN도 바로 돌아왔음
    • matrix-fed.takeonme.org처럼 존재하는 이름도 정상 응답함
    • 오직 takeonme.org 아래의 존재하지 않는 이름, 즉 특정 zone의 NXDOMAIN만 타임아웃이 났음

먼저 불을 끄고, 그다음 뿌리를 잘랐음

  • 임시 복구는 Caddy 컨테이너가 host resolver를 우회하게 만드는 것이었음

    • compose 설정에 dns: [1.1.1.1, 8.8.8.8]를 추가함
    • 재배포 후 30초 안에 Caddy가 다시 갱신을 시도했고, 이번에는 production Let's Encrypt로 인증서 갱신에 성공함
    • 새 인증서는 5월 4일부터 8월 2일까지 유효했고 Matrix federation도 복구됨
  • 하지만 host resolver 버그는 그대로 남아 있었음

    • 직접 NextDNS DoT로 dig +tls를 날리면 같은 도메인도 즉시 NXDOMAIN이 왔음
    • 즉 NextDNS 자체나 DoT 프로토콜이 아니라, systemd-resolved가 이 조합의 응답을 다루는 경로에서만 문제가 난 셈임
  • 근본 조치는 서버에 불필요한 NextDNS DoT 설정을 제거하는 것이었음

    • /etc/systemd/resolved.conf에 NextDNS 프로필 기반 DNS와 DNSOverTLS=yes가 박혀 있었음
    • 이 서버는 클라이언트 기기가 아니라서 광고 차단이나 추적 방지 DNS 프로필의 이득이 거의 없었음
    • 설정을 주석 처리하고 systemd-resolved를 재시작하자, VPS 제공 link DNS로 돌아가며 NXDOMAIN이 즉시 응답됨

💡

> DNS 헬스체크로 google.com만 보는 건 거의 의미가 없음. 인증서, 서비스 디스커버리, federation처럼 실제 워크로드가 의존하는 이름을 같은 경로로 질의해야 함.

운영 교훈은 꽤 아픔

  • Caddy는 갱신 실패를 로그로는 남기지만 알아서 깨워주진 않음

    • 42시간 동안 구조화된 JSON 로그에 실패 원인이 찍혔는데, 모니터링은 그걸 보고 있지 않았음
    • 공개 엔드포인트의 인증서 만료 시점과 ACME 갱신 실패 로그를 따로 알림으로 잡아야 함
  • 재시도 백오프도 장애를 키울 수 있음

    • Caddy가 CA를 과도하게 두드리지 않으려고 6시간 뒤 재시도를 잡는 건 이해됨
    • 하지만 인증서가 3시간 뒤 만료된다면 그 백오프는 운영 관점에서 너무 김
  • 설정 드리프트가 진짜 무서운 부분임

    • NextDNS DoT 설정은 예전에 개인 기기 템플릿에서 서버로 흘러들어온 잔재였음
    • 몇 년 동안 정상처럼 보이다가 특정 zone, 특정 응답 타입, 특정 resolver 조합에서만 터짐
    • 감사 체크리스트에서 ‘이 서버에 클라이언트용 DNS 프로필이 왜 있지?’를 잡아내지 못하면 이런 문제는 계속 숨어 있음

기술 맥락

  • 이 장애의 핵심 선택지는 Caddy를 고치는 게 아니라 DNS 경로를 어디까지 신뢰할지였어요. Caddy는 DNS-01 챌린지를 하려면 _acme-challenge 레코드의 zone을 찾아야 하고, 그 과정에서 SOA 질의가 안정적으로 돌아와야 하거든요.

  • Docker 컨테이너 안에서 DNS가 실패하면 컨테이너만 보는 실수를 하기 쉬워요. 하지만 여기서는 컨테이너의 127.0.0.11이 host의 127.0.0.53으로 넘기고 있었기 때문에, 실제 장애 지점은 systemd-resolved였어요.

  • NextDNS DoT 설정을 제거한 이유도 단순히 우회가 아니에요. 서버는 브라우징을 하지 않으니 클라이언트용 DNS 필터링 프로필의 이득이 작고, 반대로 인증서 갱신 같은 핵심 운영 경로에 추가 복잡도를 넣고 있었거든요.

  • Caddy 컨테이너에 별도 DNS를 남겨둔 건 방어선이에요. host resolver가 다시 이상해져도 인증서 갱신 경로는 Cloudflare와 Google DNS를 직접 타니까, 같은 종류의 장애가 바로 반복될 가능성을 낮춰줘요.

  • 모니터링 관점에서는 ‘서비스가 떠 있다’와 ‘인증서가 갱신되고 있다’를 분리해서 봐야 해요. 컨테이너는 전부 healthy였지만 public TLS endpoint는 이미 신뢰를 잃었고, 그 차이를 잡는 건 로그 알림과 실제 엔드포인트 인증서 만료 체크예요.

이 글은 ‘DNS가 된다’는 말이 얼마나 허술한지 잘 보여줌. 운영에서는 google.com이 풀리는지가 아니라, 내 서비스가 실제로 의존하는 이름과 응답 타입이 같은 경로에서 안정적으로 풀리는지가 중요함.

댓글

댓글

댓글을 불러오는 중...

devops

디지털 스택을 유럽으로 옮겨보니, 생각보다 꽤 실전적이었다

한 개발자가 분석, 메일, 비밀번호 관리, 컴퓨트, 오브젝트 스토리지, 백업, 이메일, 에러 추적, AI API까지 유럽 중심 스택으로 옮긴 경험을 정리한 글이다. 핵심은 반미 감정이 아니라 데이터가 어디에 있고, 누가 접근할 수 있고, 정치나 기업 정책 변화에 얼마나 휘둘리는지를 의식하자는 얘기다.

devops

개인용 컴퓨터 다음은 개인용 클러스터라는 주장

이 글은 AI 시대에 개인 한 명이 쓰는 컴퓨팅 자원이 점점 ‘클러스터 한 덩어리’ 수준으로 커질 거라고 주장한다. PC가 직장, 취미 개발자, 게임 문화로 퍼졌듯이 개인용 클러스터도 업무용 AI, 오픈소스 실험, 게임 같은 흐름을 타고 대중화될 수 있다는 시나리오다.

devops

AI 에이전트 부하에 흔들린 GitHub, 왜 다른 서비스보다 더 아팠나

GitHub가 최근 몇 달 동안 가용성 저하, 검색 장애, GitHub Actions 문제, 심지어 squash merge에서 커밋이 빠지는 데이터 무결성 사고까지 겪었다. GitHub CTO는 AI 에이전트발 부하 증가를 원인으로 들었지만, 실제로는 2년간 약 3.5배 증가한 부하와 Azure 이전, 오래된 시스템, 조직적 지연이 겹친 문제에 가깝다. 개발자 입장에선 GitHub가 ‘없으면 안 되는 도구’에서 ‘업무를 막는 병목’으로 보이기 시작했다는 게 핵심이다.

devops

한국 클라우드 시장, 이제 GPU랑 데이터센터 싸움으로 넘어감

국내 클라우드 서비스 제공사들이 AI 전환 수요를 잡기 위해 GPUaaS, 데이터센터, 공공 클라우드 사업에 공격적으로 투자하고 있어. 네이버클라우드, KT클라우드, NHN클라우드 모두 2026년 1분기 실적에서 AI 인프라를 핵심 성장축으로 내세웠고, 정부의 2조805억원 규모 GPU 구축 사업이 판을 더 키우는 중이야.

devops

칩값 뛰니 K게임의 콘솔·피시 전환 해법으로 다시 뜨는 클라우드 게임

국내 게임사들이 모바일 중심에서 콘솔·피시로 넘어가려는 타이밍에 고성능 지피유와 콘솔 가격 상승이 발목을 잡고 있다. 이용자 입장에서는 300만원대 게이밍 피시, 오른 콘솔 가격, 스팀 가격 기준 개편까지 겹치면서 고사양 게임 접근성이 떨어지는 상황이다. 업계는 원격 서버에서 게임을 실행해 스트리밍하는 클라우드 게임을 다시 현실적인 대안으로 보고 있다.