무색
기술블로그
에세이
연구
소개

무색

소프트웨어로 비즈니스의 가능성을 만듭니다. 웹·앱 개발, 음성 AI, 자동화 콘텐츠 제작까지 — 기술이 필요한 곳에 무색이 있습니다.

연락처

contact@museck.com

사업자 정보

상호: 무색

대표: 배성재

사업자등록번호: 577-58-00836

인천광역시 연수구 인천타워대로 323, 에이동 8층 801-802호 AB-132 (송도동, 송도 센트로드)

© 2026 무색. All rights reserved.
개인정보처리방침·이용약관·연락처
INCHEON, KR
홈랩 TLS 자동화 — 자물쇠와 인증서 체인
홈랩 삽질기
2025. 12. 30.

홈랩 TLS 자동화: mkcert 자체 서명에서 cert-manager + Let's Encrypt로

cert-managerLet's EncryptKubernetesTraefik홈랩

자체 서명 인증서의 한계

홈랩을 처음 구축할 때 mkcert로 자체 서명 인증서를 만들었다. home.lab이라는 로컬 도메인을 쓰고, Traefik에 인증서를 걸어두면 끝이라고 생각했는데 막상 운영해보니 문제가 꽤 있었다.

mkcert의 로컬 CA 인증서를 접속하는 모든 기기에 설치해야 했다. PC는 그렇다 치고 스마트폰이나 태블릿까지 일일이 CA를 심는 건 번거로웠다. Tailscale로 외부에서 접속할 때도 매번 브라우저 보안 경고가 떴고, 인증서 만료되면 수동으로 다시 발급해야 했다.

결국 공인 도메인과 공인 인증서가 필요하다는 결론에 도달했다. 홈랩이니까 자체 서명이면 충분하다고 생각했는데 실제로는 그렇지 않았다.

DNS-01 Challenge가 답이었다

Let's Encrypt로 무료 인증서를 발급받으려면 도메인 소유를 증명해야 한다. 보통은 HTTP-01 challenge를 쓰는데 웹 서버가 외부에서 접근 가능해야 동작한다. 홈랩은 공인 IP가 없으니 이 방식은 안 된다.

DNS-01 challenge는 다르다. DNS TXT 레코드를 생성해서 도메인 소유를 증명하는 방식이라 웹 서버가 외부에 열려 있을 필요가 없다. 더 좋은 건 와일드카드 인증서를 발급받을 수 있다는 점이다. *.xssh.org 하나만 있으면 grafana.xssh.org든 syncthing.xssh.org든 인증서를 따로 신경 쓸 게 없다.

cert-manager + Cloudflare 구성

xssh.org 도메인을 Cloudflare에서 관리하고 있어서 Cloudflare API 토큰을 발급받아 cert-manager에 연결했다. 전체 흐름을 정리하면 이렇다.

  1. cert-manager가 Let's Encrypt에 인증서 발급을 요청한다
  2. Let's Encrypt가 DNS-01 challenge를 내보낸다
  3. cert-manager가 Cloudflare API로 TXT 레코드를 자동 생성한다
  4. Let's Encrypt가 TXT 레코드를 확인하고 인증서를 발급한다
  5. cert-manager가 인증서를 Kubernetes Secret에 저장한다

갱신도 자동이다. cert-manager가 만료 30일 전에 알아서 갱신해준다.

ClusterIssuer 설정

ClusterIssuer는 인증서를 어디서 어떻게 발급받을지 정의하는 리소스다. Cloudflare API 토큰을 Secret으로 만들어두고 참조하게 했다.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
      - dns01:
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token
              key: api-token

와일드카드 Certificate

Certificate 리소스에서 *.xssh.org와 xssh.org 두 개를 dnsNames에 넣었다. 와일드카드는 서브도메인만 커버하고 루트 도메인은 별도로 지정해야 한다.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: xssh-org-wildcard
  namespace: traefik
spec:
  secretName: xssh-org-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - "*.xssh.org"
    - "xssh.org"

Traefik 기본 인증서 전환

인증서가 발급되면 Traefik에 적용해야 한다. 여기서 꽤 깔끔한 패턴을 하나 발견했다. TLSStore의 default 리소스에 와일드카드 인증서를 등록해두면, 개별 IngressRoute에서는 tls: {} 한 줄만 넣으면 된다.

apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
  name: default
  namespace: traefik
spec:
  defaultCertificate:
    secretName: xssh-org-tls

기존에는 mkcert로 만든 homelab-tls Secret을 참조하고 있었는데, 이걸 cert-manager가 관리하는 xssh-org-tls로 바꿔주면 끝이다. Grafana, Syncthing 등 모든 IngressRoute의 TLS 설정을 건드릴 필요가 없었다.

home.lab에서 xssh.org로 도메인 전환

인증서 전환과 함께 도메인도 바꿨다. home.lab은 로컬에서만 쓸 수 있는 가짜 도메인이라 외부 접속이 안 됐다. xssh.org로 통일하면 내부든 외부든 같은 주소로 접속할 수 있다.

바꿔야 할 것이 꽤 있었다.

  • 각 서비스의 IngressRoute에서 호스트명을 *.home.lab에서 *.xssh.org로 변경
  • CoreDNS(home-dns)에서 home.lab 호스트 레코드 제거하고 순수 DNS 포워더로 전환
  • Cloudflare에 각 서비스의 DNS A 레코드 추가 (내부 IP 지정)

Cloudflare DNS 관리를 편하게 하려고 간단한 셸 스크립트도 만들었다. A 레코드를 추가하거나 삭제할 때 대시보드에 안 들어가도 된다.

삽질했던 부분

cert-manager Helm 차트를 설치할 때 installCRDs: true 옵션을 빼먹으면 CRD가 없어서 ClusterIssuer나 Certificate를 만들 수 없다. Helm 차트 설치 시 CRD를 같이 설치하거나 kubectl apply로 먼저 CRD를 깔아야 한다.

Cloudflare API 토큰도 권한 설정에 주의가 필요하다. Zone:DNS:Edit 권한이 있어야 TXT 레코드를 생성할 수 있고, 토큰의 Zone 범위도 정확히 해당 도메인으로 지정해야 한다. 처음에 All zones로 설정했다가 보안상 좋지 않아서 xssh.org만 지정하도록 바꿨다.

Certificate 리소스의 네임스페이스도 중요하다. Traefik이 traefik 네임스페이스에서 동작하고 있으면 Certificate도 같은 네임스페이스에 만들어야 한다. Secret이 다른 네임스페이스에 생기면 Traefik이 접근하지 못한다.

전환 후 달라진 점

브라우저 보안 경고가 완전히 사라졌다. 어디서 접속하든 초록색 자물쇠가 뜬다. 새 서비스를 추가할 때 인증서를 따로 만들 필요도 없다. IngressRoute에 호스트명만 지정하고 tls: {}를 넣으면 와일드카드 인증서가 자동으로 적용된다.

인증서 갱신도 완전 자동이다. 3개월마다 Let's Encrypt 인증서가 만료되는데 cert-manager가 알아서 갱신해준다. 한번 세팅해두니 신경 쓸 일이 없어졌다.

정리

이번 작업에서 배운 핵심은 두 가지다.

첫째, 홈랩이라도 공인 TLS 인증서를 자동으로 관리할 수 있다. DNS-01 challenge 덕분에 공인 IP가 없어도 Let's Encrypt 인증서를 발급받을 수 있고, cert-manager가 발급부터 갱신까지 전부 처리해준다.

둘째, 와일드카드 인증서 + TLSStore 기본 인증서 패턴은 서비스 확장이 정말 편하다. 새 서비스 추가할 때 인증서 걱정이 사라진다. 처음에 제대로 세팅해두면 나중에 할 일이 없다.

"홈랩이니까 대충 해도 되지" 싶었는데 보안 기본기는 처음부터 제대로 하는 게 맞았다. 나중에 외부에 서비스를 공개할 때도 추가 작업 없이 바로 가능하니까.

자주 묻는 질문

홈랩에서 공인 IP 없이 Let's Encrypt 인증서를 발급받을 수 있나요?
DNS-01 challenge를 사용하면 가능합니다. 웹 서버를 외부에 노출하지 않고 DNS TXT 레코드로 도메인 소유를 증명하므로, 공인 IP가 없는 홈랩에서도 와일드카드 인증서까지 발급받을 수 있습니다.
cert-manager 와일드카드 인증서를 쓰면 서비스 추가 시 인증서를 다시 만들어야 하나요?
아닙니다. *.xssh.org 같은 와일드카드 인증서를 TLSStore 기본 인증서로 등록해두면, 새 서비스의 IngressRoute에 tls: {} 한 줄만 넣으면 자동 적용됩니다.
cert-manager 인증서 갱신은 수동으로 해야 하나요?
cert-manager가 만료 30일 전에 자동으로 갱신합니다. 한번 ClusterIssuer와 Certificate를 설정해두면 이후 인증서 관리에 신경 쓸 일이 없습니다.
홈랩 삽질기(1/19)
Next

홈랩에 GitOps 파이프라인 구축하기: Gitea + ArgoCD App of Apps 패턴