안녕하세요 nopro입니다.

이번엔 두다지에서 어떻게 https 엔드포인트를 생성하고 관리하고 있는지에 대해 다루려고 합니다. 많은 분들이 무료 https 인증서인 Let’s Encrypt를 사용하고 계실겁니다. 하지만 무료이기 때문에 유효기간이 3개월로 제한되어 있어서 3개월이 지날 때마다 매번 갱신해야 합니다. 만약 서비스를 하고 계신 경우라면, 관리자가 알기 전에 사용자가 먼저 인증서 만료로 인한 오류를 맞닥뜨릴 수 있습니다. 두다지에서도 이러한 문제가 있었고, 이를 해결하기 위해 쿠버네티스를 활용하여 인증서를 자동으로 갱신할 수 있는 일종의 파이프라인(?)을 구축했습니다.

Cert-Manager


Cert-Manager는 위에서 언급한 이슈를 정확히 해결해주는 오픈소스 툴입니다. 심지어 쿠버네티스에서 사용할 수 있다니!! 이건 완전 저희를 위한 툴이 아닐 수 없습니다. Let’s Encrypt 뿐만 아니라 다른 인증서 발급처(?)도 지원하고 있으니 공식 문서에서 확인해보시길 바랍니다. 두다지에서 사용하는 버전의 리소스와 기능들을 중심으로 다루겠습니다.

v1.11.0 버전 사용

Issuer

Issuer는 단어에서 유추할 수 있듯이 인증서를 발급에 대한 설정을 정의하는 리소스입니다. Https 인증서에는 CA(Certificate Authority)라는 인증서에 서명하는 주체가 있습니다. 브라우저는 신뢰하는 CA의 서명을 받은 인증서를 사용하면 이 사이트는 안전하다고 판단하며 그렇지 않으면 아래와 같은 경고가 뜨게 되죠. Let’s Encrypt는 nonprofit CA로써, 대부분 브라우저에서 신뢰하는 CA입니다.

따라서 Issuer 리소스에 Let’s Encrypt CA의 서명을 하도록 설정을 추가하기만 하면 됩니다. Issuer 설정을 하기 전에 몇가지 배경지식을 공부하고 가봅시다.

Issuer vs ClusterIssuer

Cert-Manager의 Issuer는 2가지 종류의 리소스가 있습니다. 그냥 Issuer는 namespaced resource입니다. 즉 네임스페이스 별로 issuer가 존재하며, 앞으로 같이 사용할 Certificate이나 Secret 리소스가 모두 같은 네임스페이스에 있어야합니다. 반대로 ClusterIssuer는 cluster resource로, 네임스페이스에 국한되지 않습니다. 하나의 인증서로 여러 네임스페이스의 있는 인증서들을 발급 및 관리하고 싶다면 ClusterIssuer를, 네임스페이스 별로 인증서를 발급 및 관리하고 싶다면 Issuer를 사용하면 됩니다. 좋고 나쁨은 없으며 취향에 맞게 선택하시면 됩니다. 두다지에서는 ClusterIssuer로 모든 네임스페이스의 인증서를 관리하고 있습니다.

Let’s Encrypt

Let’s Encrypt가 아무리 무료라지만, 아무 도메인의 인증서에 서명해주지 않습니다. 실제 도메인이 있고, 이 도메인을 통해 서비스하고 있는 서버가 있는지 체크하는 과정을 통과해야 서명을 해줍니다. 체크하는 과정을 challenge(도전)이라고 부르고 있고, challenge에는 HTTP-01과 DNS-01 2가지가 있습니다. 이번 포스팅에서는 두다지에서 사용하고 있는 DNS-01에 대해서만 언급하겠습니다. DNS-01 challenge는 Let’s Encrypt에서 제공한 임의의 문자열이 증명하고자 하는 도메인의 txt record에 똑같이 있는지 확인하는 방식입니다. 갑자기 좀 어렵게 느껴지실 거 같지만 사전 지식을 알면 아주 쉽습니다. 도메인은 DNS 서버에 등록이 되는건 모두 알고 계실겁니다. 하지만 그냥 도메인 이름하나 띡하고 등록하는게 아니라 record 형태로 등록합니다. 도메인에 해당하는 IP, TTL 등의 도메인에 대한 요청 처리방법이 적혀있는 일련의 명령어 파일입니다. 도메인 record에는 사람과 컴퓨터 둘 다 읽을 수 있는 text 값을 넣을 수 있는데 이것이 바로 txt record입니다. txt record는 스팸 방지와 지금처럼 도메인 검증용으로 쓰입니다. 즉 위에서 설명한 DNS-01을 풀어서 얘기하면, Let’s Encrypt는 DNS서버에 저희가 등록한 도메인 record를 살펴보고, Let’s Encrypt에서 발급한 랜덤한 text 값이 도메인의 txt record에도 똑같이 적혀있는지 확인함으로써 도메인을 검증하는 방식입니다. 두다지에서 DNS-01 challenge를 통해 도메인을 검증하고 있으며, 이것의 장점은 wildcard 도메인에 대해 인증서를 발급할 수 있다는 장점이 있으며, google-cloud-dns를 사용하고 있기 때문에 HTTP-01보다 비교적 쉽게 검증을 할 수 있어서 이 방법을 선택했습니다.

Google Cloud DNS

두다지에서 도메인을 생성할 때 사용하는 것이 google cloud dns입니다. 여기서 쉽게 도메인을 생성하고 txt record도 바로 추가할 수 있습니다.

배경지식 내용이 좀 길어졌는데 이제 ClusterIssuer yaml을 한번 살펴봅시다.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-issuer
spec:
  acme:
    server: <https://acme-v02.api.letsencrypt.org/directory>
    email: example@naver.com
    privateKeySecretRef:
      name: letsencrypt-issuer
    solvers:
    - dns01:
        cloudDNS:
          project: project-name
          # Set this to the secret that we publish our service account key
          # in the previous step.
          serviceAccountSecretRef:
            name: cloud-dns-key
            key: key.json
  • acme: 도메인 인증할 때 사용하는 표준 프로토콜이며, 우리가 사용하는 Let’s Encrypt도 acme 프로토콜을 준수하고, 앞서 설명한 DNS-01 challenge도 acme 프로토콜 기반이기 때문에 acme 스펙을 정의합니다.
  • server: acme 통신을 하는 Let’s Encrypt의 서버 주소입니다.
  • privateKeySecretRef:
  • solvers: Let’s Encrypt의 challenge 방식을 선택합니다. 저희는 DNS-01 방식이기 때문에 dns01을 적어줍니다.
  • cloudDNS: google-cloud-dns에 관련된 설정을 적어줍니다. google 이외에 다른 provider도 지원하고 있으므로 자세한 사용법은 공식 문서를 참고하시길 바랍니다.

이렇게 설정을 해주면, 앞서 설명한 인증서 검증 과정이 자동으로 진행됩니다.

Certificate

저희가 발급할 인증서를 Certificate 리소스를 통해서 생성합니다.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example
	namespace: istio-system
spec:
  secretName: example
  renewBefore: 360h
  issuerRef:
    name: letsencrypt-issuer
    kind: ClusterIssuer
  dnsNames:
  - "example.dudaji.com"
  • secretName: 모든 과정이 마치고 나서 생성된 https 인증서를 secret으로 만드는데, 그 때 생성될 secret 이름입니다.
  • renewBefore: 인증서 갱신 시점입니다. 만료 시점으로부터 360시간(2주) 전에 갱신한다는 의미입니다.
  • issuerRef: 저희가 사전에 생성한 ClusterIssuer의 이름을 적습니다. 이로써 Certificate 리소스가 생기면 바인딩 된 ClusterIssuer의 설정에 따라 검증과정이 진행될 수 있게 됐습니다.
  • dnsNames: 검증할 도메인 이름을 적습니다. 검증이 완료되면 dnsNames에 적힌 도메인으로 https 접속이 가능하게 됩니다. ex) https://example.dudaji.com

지금까지 Cert-Manager를 통해 어떻게 인증서를 생성하고 관리하는지 알아보았습니다. 이렇게 만들어진 인증서를 가지고 두다지에서는 어떤 방식으로 사용하고 있는지 알아보겠습니다.

Istio Gateway & VirtualService


저희는 Service Mesh로 Istio를 사용해왔기 때문에, Istio와 결합하여 https 엔드포인트를 생성하고 있습니다.

Gateway

Istio Gateway는 네트워크 요청에 대한 명세를 정의합니다. 어떤 프로토콜로, 어떤 도메인으로, 어떤 포트번호로 요청이 들어오는지에 대해 미리 정의하며, https의 경우 tls 인증을 위해 위에서 생성한 인증서 secret 이름을 적어줍니다. 이 때 인증서 secret은 istio-system 네임스페이스에 있어야합니다. 그래서 위에 Certificate 생성할 때 namespace: istio-system이 적혀있던 거였습니다. 아래 예시를 보면 Gateway의 역할을 단번에 이해하실 수 있습니다.

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: example
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - example.dudaji.com
****      port:
        name: http
        number: 80
        protocol: HTTP
      tls:
    - hosts:
        - example.dudaji.com
****      port:
        name: https
        number: 443
        protocol: HTTPS
      tls:
        credentialName: example
        mode: SIMPLE
  • tls.credentialName: 인증서 secret의 이름입니다.

VirtualService

VirtualService는 특정 Gateway에 부합하는 네트워크를 받아서 쿠버네티스 내에 특정 서비스와 포트번호로 포워딩 되게끔 forwarding rule이 적혀있는 리소스입니다. 아래 예시를 보면서 설명드리겠습니다

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: example
spec:
  gateways:
    - example
  hosts:
    - example.dudaji.com
****  http:
    - route:
        - destination:
            host: example.default.svc.cluster.local
            port:
              number: **80**
  • gateways: 위에서 생성한 gateway의 이름을 적습니다. gateway 명세에 부합하는 네트워크를 현재 virtualservice가 받겠다는 의미입니다.
  • hosts: gateway로부터 받은 네트워크 중에서 취급하려는 도메인 이름을 적습니다. 일반적인 경우에는 gateway와 virtualservice에 저희가 사용하려는 도메인 하나만 적습니다. 만약 gateway에서 여러 도메인이 적혀있으면, virtualservice에서 그 도메인 중에 하나 혹은 그 이상을 선택할 수 있습니다.
  • http: gateway와 hosts 조건을 모두 만족하는 네트워크의 목적지를 적습니다. 쿠버네티스 default 네임스페이스에 80번 포트로 example 서버가 있다는 가정 하에 예시입니다.

지금까지 Cert-Manager + Let’s Encrypt + Google Cloud DNS + Istio Gateway & VirtualService를 통해 두다지에서 어떻게 https 엔드포인트들을 쉽게 생성하고 관리하는지에 대해 알아보았습니다. 이렇게 한번 설정해놓으면 Certificate, Istio Gateway와 VirtualService만 추가 생성함으로써 새로운 도메인에 대해 인증서를 얻고 퍼블릭하게 서빙할 수 있으며, 자동 갱신까지 되기 때문에 인증서 만료에 대해 크게 신경쓸 필요없습니다. Let’s Encrypt와 쿠버네티스를 사용하고 계시다면 이러한 방법을 고려해보시는 걸 추천드립니다.

ℹ️
Nopro(노재원)
한국 서버 개발자