내부 서비스를 위한 TLS 인증서: Split-Horizon DNS 및 ACME 구현

내부 서비스를 위한 TLS 인증서: Split-Horizon DNS 및 ACME 구현

내부 TLS의 과제

내부 서비스를 TLS로 보안 처리하는 것은 일반적으로 자체 서명 인증서(self-signed certificates)를 사용할 것인지 아니면 공인 인증서를 사용할 것인지에 대한 이분법적인 선택을 제시합니다. 자체 서명 인증서는 내부 네트워크 세부 정보를 유출하지 않지만, 모든 클라이언트 장치가 사용자 정의 루트 CA를 수동으로 신뢰하도록 설정해야 하므로 관리적 부담이 큽니다. 공인 인증서는 기본적으로 신뢰되지만, 일반적으로 검증을 위해 서비스가 공인 인터넷을 통해 도달 가능해야 합니다.

내부 서비스를 위한 Split-Horizon DNS 구현

Split-horizon DNS를 사용하면 요청자의 위치에 따라 단일 도메인 이름이 서로 다른 IP 주소로 해석될 수 있습니다. 내부 서비스의 경우, grafana.tuxnet.dev와 같은 도메인이 외부 리졸버(CA 검증을 위해)에는 공인 IP로 해석되고, VPN을 통해 연결된 클라이언트에게는 사설 내부 IP로 해석됨을 의미합니다.

기술 아키텍처

이 워크플로우를 구현하려면 다음과 같은 구성 요소가 필요합니다:

  1. VPN과 DNS 리졸버: NetBird와 같은 도구를 사용하여 Custom Zones를 관리함으로써, VPN에 연결된 클라이언트는 내부 IP를 받고 서버 자체는 인증서 발급을 용이하게 하기 위해 공인 DNS를 사용하도록 보장할 수 있습니다.
  2. ACME 클라이언트: acme.sh를 사용하여 Let's Encrypt 또는 ZeroSSL로부터 인증서 발급을 자동화할 수 있습니다.
  3. 리버스 프록시/WAF: Nginx는 특정 VPN 네트워크 인터페이스에 바인딩되어, VPN에서 시작된 트래픽만 내부 서비스에 접근할 수 있도록 보장함으로써 Web Access Firewall (WAF) 역할을 효과적으로 수행하는 엔트리 포인트로 작동할 수 있습니다.

인증서 발급 및 갱신

acme.sh를 standalone 모드로 사용하여 http-01 챌린지를 수행하면 클라이언트가 발급 과정 중에만 80번 포트에 바인딩할 수 있으므로, Nginx가 80번 포트를 영구적으로 점유할 필요가 없습니다.

갱신을 자동화하려면 acme.sh --cron을 실행하고 이후 인증서를 Nginx 디렉토리로 동기화한 뒤 서비스를 재로드하는 cron job을 구성할 수 있습니다. ACME 클라이언트를 root 권한으로 실행하는 것을 피하기 위해, socatsetcap CAP_NET_BIND_SERVICE=+ep를 적용하여 권한이 없는 사용자가 80번 포트에 바인딩할 수 있도록 할 수 있습니다.

SANs 및 CNAMEs를 통한 확장

모든 내부 서비스에 대해 개별 인증서를 발급하는 대신, Subject Alternative Names (SANs)를 사용할 수 있습니다. 여러 DNS 이름(예: internal.tuxnet.dev, grafana.tuxnet.dev, analytics.tuxnet.dev)을 포함하는 단일 인증서를 생성함으로써, 하나의 인증서 파일을 여러 Nginx 서버 블록에서 재사용할 수 있습니다.

대안적 접근 방식 및 트레이드오프

Split-horizon DNS는 클라이언트 신뢰 문제를 해결하지만,

Sources