본문 바로가기
WIKI 기술 지식 베이스

컨트롤 플레인 TLS 자격증명 자동 순환

원문 보기 위키 갱신

컨트롤 플레인 TLS 자격증명 자동 순환 (Automatically Rotating Control Plane TLS Credentials)

Linkerd의 자동 mTLS는 메시 안에서 아이덴티티의 기반이 되는 올바르게 구성된 인증서에 의존해요. 이 문서에서는 cert-manager와 trust-manager를 이용해 Linkerd 컨트롤 플레인의 TLS 자격증명(신원 발급자 인증서와 신뢰 앵커)을 자동으로 순환하는 방법을 단계별로 알려드립니다.

출처: Linkerd Automatically Rotating Control Plane TLS Credentials

본문

Linkerd의 자동 mTLS는 다른 모든 TLS 구현과 마찬가지로 메시 내 아이덴티티의 기반이 되는 올바르게 구성된 인증서에 의존합니다. 각 meshed 워크로드는 Linkerd가 스스로 생성한 워크로드 인증서를 갖는데, 그것은 클러스터 간에 공유할 수 있는 신뢰 앵커(trust anchor) 와 클러스터에 특화된 아이덴티티 발급자 인증서(identity issuer certificate) 를 기반으로 합니다.

Note

인증서는 mTLS 기반 보안 시스템에서 가장 중요한 부분 중 하나지만, 흔히 문서화가 가장 덜 된 부분이기도 합니다. Linkerd의 인증서에 대한 자세한 내용은 Buoyant의 (m)TLS 개념 입문서를 참고하세요.

Linkerd는 워크로드 인증서를 자동으로 순환하지만, 아이덴티티 발급자 인증서나 신뢰 앵커는 자동으로 순환할 수 없습니다. 기본 설치(Out-of-the-box)는 유효기간이 1년인 정적 자체 서명 인증서를 생성하지만, 만료를 막으려면 사용자가 직접 수동 순환해야 합니다. 이 구성은 빠른 시작 테스트에는 편리하지만 프로덕션 환경에는 권장되지 않아요.

Linkerd 프로덕션 팁

이 페이지에는 오픈소스 커뮤니티의 최선 노력(best-effort) 지침이 담겨 있습니다. 미션 크리티컬 애플리케이션을 운영하는 프로덕션 사용자는 Linkerd 프로덕션 리소스에 익숙해지거나 상용 Linkerd 제공자와 상담하시길 권장합니다.

cert-manager와 trust-manager로 인증서 관리 자동화하기

cert-manager와 trust-manager는 Kubernetes 설치의 인증서 관리를 자동화하는 인기 있는 CNCF 도구입니다. 이 둘은 Linkerd와 함께 협력해 아이덴티티 발급자 인증서의 순환을 자동화하고, 신뢰 앵커의 순환을 부분적으로 자동화할 수 있어요.

cert-manager는 매우 유연해서 구성의 상당 부분이 이를 운영하는 조직의 특정 정책에 달려 있습니다. cert-manager에 대한 포괄적인 가이드를 제공하기보다, 이 문서는 아주 단순한 설정에 집중할 거예요.

  • cert-manager가 자체 서명 신뢰 앵커를 생성합니다. 신뢰 앵커는 자체 서명 인증서이므로 그 개인 키가 클러스터에 저장됩니다(구체적으로 cert-manager 네임스페이스의 Kubernetes Secret에). Linkerd는 신뢰 앵커의 개인 키에 접근할 필요가 전혀 없으므로 개인 키를 클러스터 밖에 두는 것이 더 안전합니다. 이에 대해서는 신뢰 앵커 설정 섹션에서 조금 더 다루겠습니다.
  • cert-manager가 신뢰 앵커를 이용해 Linkerd의 아이덴티티 발급자 인증서를 생성합니다. 아이덴티티 발급자 인증서의 개인 키도 클러스터에 저장됩니다(linkerd 네임스페이스의 Secret에). Linkerd는 이 개인 키에 접근해야 합니다. 이것이 아이덴티티 발급자를 관리하는 유일한 방법입니다.
  • 마지막으로, trust-manager가 Linkerd가 cert-manager가 발급한 인증서의 진위를 검증하는 데 사용할 신뢰 번들(trust bundle)을 생성합니다. 신뢰 번들은 개인 키를 전혀 포함하지 않습니다. linkerd 네임스페이스의 ConfigMap에 저장될 거예요.

인증서가 생성되면 cert-manager가 필요에 따라 아이덴티티 발급자 인증서를 자동으로 순환합니다. 신뢰 앵커의 순환은 조금 더 복잡합니다. cert-manager가 무거운 일을 처리해 주긴 하지만, 아래에서 설명하듯 순환에는 여전히 수동 개입이 필요해요.

Note

cert-manager는 매우 유연해서 구성 방법도 다양합니다. cert-manager 전반과 그 구성 접근에 대한 자세한 내용은 Buoyant의 cert-manager 개념 입문서를 참고하세요.

설정 개요 (Setup Overview)

우리가 따를 과정은 여러 단계가 있긴 하지만 간단합니다.

  • 우리의 Linkerd 인증서가 살아야 할 linkerd 네임스페이스 생성하기
  • 클러스터에 cert-manager와 trust-manager 설치하기
  • cert-manager가 신뢰 앵커를 생성하도록 구성하기
  • cert-manager가 아이덴티티 발급자 인증서를 생성하도록 구성하기
  • cert-manager가 Linkerd가 사용할 신뢰 번들을 생성하도록 구성하기
  • cert-manager가 생성한 인증서로 Linkerd 설치하기
  • cert-manager가 한 모든 것을 확인하기!
  • 아이덴티티 발급자 순환하기
  • 신뢰 앵커 순환하기

1. linkerd 네임스페이스 생성하기

아직 Linkerd를 설치하지 않았는데 좀 이상해 보일 수 있어요. 하지만 이것은 중요한 첫 단계입니다. Linkerd는 인증서가 linkerd 네임스페이스에 있기를 기대하고, cert-manager와 함께 작업할 때 Linkerd는 설치 시점에 인증서가 이미 존재하길 필요로 하거든요. 그래서 지금 네임스페이스를 만들게요:

`kubectl create namespace linkerd
`

2. cert-manager와 trust-manager 설치하기

다음으로 cert-manager를 설치합니다(여기서는 Helm을 사용하지만, cert-manager 설치 가이드에서 더 많은 옵션을 확인할 수 있어요):

`helm repo add jetstack https://charts.jetstack.io --force-update

helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

kubectl rollout status -n cert-manager deploy
`

cert-manager를 cert-manager 네임스페이스에 설치하는 것을 강력히 권장합니다.

cert-manager를 설치했으면 trust-manager를 설치합니다(역시 Helm을 사용하지만, trust-manager 설치 가이드에 더 많은 옵션이 있습니다). trust-manager도 cert-manager 네임스페이스에 설치하고, trust-manager가 신뢰 네임스페이스(trust namespace) 로 cert-manager 네임스페이스를 사용하도록 구성할 거예요. 신뢰 네임스페이스는 trust-manager가 Secret을 읽을 수 있도록 허용된 유일한 네임스페이스입니다. 우리는 trust-manager가 cert-manager가 생성하는 인증서용 Secret을 보길 원하므로 cert-manager 네임스페이스가 바로 우리가 원하는 곳이에요.

`helm install \
  trust-manager jetstack/trust-manager \
  --namespace cert-manager \
  --set app.trust.namespace=cert-manager \
  --wait
`

3. cert-manager가 신뢰 앵커를 생성하도록 구성하기

Buoyant의 cert-manager 개념 입문서에서 설명했듯, cert-manager는 issuer를 사용해 인증서를 생성합니다. cert-manager가 생성·관리하는 모든 인증서는 Certificate 리소스로 구성되어야 하고 issuer에 연결되어야 합니다. cert-manager가 사용하는 모든 issuer는 Issuer 또는 ClusterIssuer 리소스로 구성되어야 해요.

Issuer와 ClusterIssuer의 주요 차이는 Issuer는 Issuer와 같은 네임스페이스의 Certificate만 사용할 수 있는 반면, ClusterIssuer는 네임스페이스를 넘나들 수 있다는 점입니다. 신뢰 앵커에는 cert-manager 네임스페이스의 Issuer를 사용하겠습니다. 이것은 자체 서명 issuer가 될 텐데, 즉 무작위 키로 자체 서명 인증서를 그냥 생성한다는 뜻입니다. 이것이 가장 간단한 종류의 issuer예요.

Note

위에서 설명했듯 자체 서명 issuer는 가장 안전한 방법이 아닙니다. 신뢰 앵커의 개인 키를 클러스터 밖에 전부 두는 것이 더 좋고, 실제로 많은 조직에서 이를 강력한 요구사항으로 만듭니다.

자체 서명 issuer가 적합하지 않은 상황이라면, 다른 종류의 issuer를 사용해 Linkerd의 신뢰 앵커를 제공하는 것만으로도 설정을 필요에 맞게 조정할 수 있을 거예요. 하지만 신뢰 앵커의 개인 키를 완전히 클러스터 밖에 두는 것이 목표라면 아이덴티티 발급자용 issuer도 바꿔야 할 가능성이 높습니다. 여기 보여주는 issuer는 그저 예시일 뿐이라 여러분의 환경에 맞게 편집해도 됩니다. 다만 네임스페이스에 주의하세요! issuer가 cert-manager 네임스페이스에 없다면 몇 가지 추가 변경이 필요할 수 있어요.

`kubectl apply -f - apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  # This is the name of the Issuer resource; it's the way
  # Certificate resources can find this issuer.
  name: linkerd-trust-root-issuer
  namespace: cert-manager
spec:
  selfSigned: {}
EOF
`

다음으로, 앞서 만든 Issuer를 사용하는 cert-manager Certificate 리소스를 만들겠습니다.

Warning

이 Certificate의 rotationPolicy에 관한 아래 경고를 참고하세요.

`kubectl apply -f - ---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  # This is the name of the Certificate resource, but the Secret
  # we save the certificate into can be different.
  name: linkerd-trust-anchor
  namespace: cert-manager
spec:
  # This tells cert-manager which issuer to use for this Certificate:
  # in this case, the Issuer named linkerd-trust-root-issuer.
  issuerRef:
    kind: Issuer
    name: linkerd-trust-root-issuer

  # The issued certificate will be saved in this Secret
  secretName: linkerd-trust-anchor

  # These are details about the certificate to be issued: check
  # out the cert-manager docs for more, but realize that setting
  # the private key's rotationPolicy to Always is _very_ important,
  # and that for Linkerd you _must_ set isCA to true!
  isCA: true
  commonName: root.linkerd.cluster.local
  # This is a one-year duration, rotating two months before expiry.
  # Feel free to reduce this, but remember that there is a manual
  # process for rotating the trust anchor!
  duration: 8760h0m0s
  renewBefore: 7320h0m0s
  privateKey:
    rotationPolicy: Always
    algorithm: ECDSA
EOF
`

Warning

Certificate privateKey 섹션의 rotationPolicy 기본값은 cert-manager 버전에 따라 다릅니다. 1.17 이하 버전에서는 기본값이 Never였습니다. 이 구성에서는 cert-manager가 실제로 신뢰 앵커를 순환하지 않습니다. 대신 유효성 타임스탬프를 갱신할 뿐 새 개인 키를 생성하지는 않습니다. 이것은 개인 키를 순환하는 것보다 확실히 덜 안전합니다.

cert-manager를 1.17 이하에서 1.18 이상으로 업그레이드한다면, Certificate 리소스 매니페스트에서 명시적으로 설정하지 않을 때 이 새 기본값이 rotationPolicy를 바꿀 수 있다는 점에 유의하세요.

모호함을 피하려면 cert-manager가 관리하는 모든 인증서에 rotationPolicy: Always를 항상 설정할 것을 권장합니다.

이 Certificate은 linkerd-trust-root-issuer Issuer와 함께 cert-manager 네임스페이스에 있습니다. cert-manager는 새로 생성된 인증서를 Certificate과 같은 네임스페이스에서 secretName 필드가 가리키는 이름의 Secret에 씁니다. Linkerd는 신뢰 번들이 linkerd 네임스페이스에 있길 필요로 하지만, 신뢰 앵커의 개인 키에는 접근할 필요가 없으므로 그 Secret을 Linkerd가 접근하지 못하도록 막을 수 있는 cert-manager 네임스페이스에 두는 것이 더 좋습니다.

이 시점에서 cert-manager 네임스페이스에 linkerd-trust-anchor라는 이름의 Secret이 보여야 합니다:

`kubectl get secret -n cert-manager linkerd-trust-anchor
`

4. cert-manager가 아이덴티티 발급자 인증서를 생성하도록 구성하기

이제 cert-manager가 Linkerd 아이덴티티 발급자 인증서를 생성하도록 구성해야 하는데, 이는 또 다른 issuer를 만들어야 함을 뜻합니다. 이를 위해 CA 타입 ClusterIssuer를 사용하겠습니다. cert-manager가 방금 만든 신뢰 앵커 인증서를 사용해 두 번째 인증서를 발급하길 원하기 때문이에요.

Linkerd는 아이덴티티 발급자 인증서의 개인 키에 접근해야 하므로 그 Certificate이 linkerd 네임스페이스에 있어야 하기 때문에 이것은 ClusterIssuer여야 합니다. 여기서 네임스페이스 경계를 넘는 가장 간단한 방법이 ClusterIssuer를 쓰는 것입니다.

`kubectl apply -f - apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  # This is the name of the Issuer resource; it's the way
  # Certificate resources can find this issuer.
  name: linkerd-identity-issuer
spec:
  ca:
    secretName: linkerd-trust-anchor
EOF
`

Note

다시 말하지만, 신뢰 앵커의 개인 키가 클러스터에 아예 없도록 변경했다면 여기서 다른 종류의 issuer로 바꿔야 할 수 있습니다. CA issuer는 개인 키에 접근할 수 없으면 동작하지 않아요.

다음으로 linkerd-identity-issuer ClusterIssuer를 사용해 Linkerd 아이덴티티 발급자 인증서를 만드는 Certificate 리소스를 만들겠습니다. Linkerd는 이 인증서를 사용해 시스템의 모든 Linkerd 프록시에 워크로드 인증서를 발급합니다. 그래서 이 Certificate은 cert-manager 네임스페이스에서 방금 만든 ClusterIssuer를 참조하지만, Certificate 자체는 반드시 linkerd 네임스페이스에 있어야 합니다.

Warning

이 Certificate의 rotationPolicy에 관한 아래 경고를 참고하세요.

`kubectl apply -f - ---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  # This is the name of the Certificate resource, but the Secret
  # we save the certificate into can be different.
  name: linkerd-identity-issuer
  namespace: linkerd
spec:
  # This tells cert-manager which issuer to use for this Certificate:
  # in this case, the ClusterIssuer named linkerd-identity-issuer.
  issuerRef:
    name: linkerd-identity-issuer
    kind: ClusterIssuer

  # The issued certificate will be saved in this Secret.
  secretName: linkerd-identity-issuer

  # These are details about the certificate to be issued: check
  # out the cert-manager docs for more, but realize that setting
  # the private key's rotationPolicy to Always is _very_ important,
  # and that for Linkerd you _must_ set isCA to true!
  isCA: true
  commonName: identity.linkerd.cluster.local
  # This is a two-day duration, rotating slightly over a day before
  # expiry. Feel free to set this as you like.
  duration: 48h0m0s
  renewBefore: 25h0m0s
  privateKey:
    rotationPolicy: Always
    algorithm: ECDSA
EOF
`

Warning

Certificate privateKey 섹션의 rotationPolicy 기본값은 cert-manager 버전에 따라 다릅니다. 1.17 이하 버전에서는 기본값이 Never였습니다. 이 구성에서는 cert-manager가 실제로 신뢰 앵커를 순환하지 않습니다. 대신 유효성 타임스탬프를 갱신할 뿐 새 개인 키를 생성하지는 않습니다. 이것은 개인 키를 순환하는 것보다 확실히 덜 안전합니다.

cert-manager를 1.17 이하에서 1.18 이상으로 업그레이드한다면, Certificate 리소스 매니페스트에서 명시적으로 설정하지 않을 때 이 새 기본값이 rotationPolicy를 바꿀 수 있다는 점에 유의하세요.

모호함을 피하려면 cert-manager가 관리하는 모든 인증서에 rotationPolicy: Always를 항상 설정할 것을 권장합니다.

이 시점에서 linkerd 네임스페이스에 linkerd-identity-issuer라는 이름의 Secret이 보여야 합니다:

`kubectl get secret -n linkerd linkerd-identity-issuer
`

5. cert-manager가 Linkerd가 사용할 신뢰 번들을 생성하도록 구성하기

거의 끝났어요! 하나만 더 있으면 됩니다. 신뢰 번들은 Linkerd가 어떤 신뢰 앵커를 수용할지 알게 해줍니다. 이를 위해 trust-manager를 사용하지만 주의할 점이 하나 있습니다. 신뢰 앵커를 순환할 때 컨트롤 플레인과 데이터 플레인(프록시) 둘 다 재시작해야 한다는 거예요. 그것은 순간적으로 일어날 수 없으므로, 모든 재시작이 끝날 때까지 신뢰 번들에 이전 신뢰 앵커와 새 신뢰 앵커 둘 다 있어야 합니다.

trust-manager가 이걸 처리할 수 있지만, 번들 안의 각 인증서에 대해 특정 소스가 필요합니다. 그래서 먼저 linkerd-trust-anchor Secret의 신뢰 앵커를 복사해 두 번째 Secret인 linkerd-previous-anchor에 넣고, 그 다음 trust-manager가 두 Secret을 모두 신뢰 번들의 소스로 사용하도록 구성하겠습니다.

Note

신뢰 앵커의 개인 키를 완전히 클러스터 밖에 두고 있다면 신뢰 앵커가 Secret이 아닌 다른 리소스에 저장될 수 있습니다. 그 경우 이 명령을 여러분의 리소스 유형에 맞게 수정해야 합니다.

`kubectl -n cert-manager get secret linkerd-trust-anchor -o json \
  | jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: "linkerd-previous-anchor", namespace: .metadata.namespace}, type: .type, data: .data}' \
  | kubectl apply -f -
`

이렇게 하면 cert-manager가 신뢰 앵커를 순환해 linkerd-trust-anchor Secret을 갱신할 때, trust-manager가 linkerd-trust-anchor Secret에서 새 앵커를, linkerd-previous-anchor Secret에서 이전 앵커를 가져와 함께 묶고 그 번들을 ConfigMap에 저장합니다. 모든 것이 재시작된 후에는 새 신뢰 앵커를 linkerd-previous-anchor Secret으로 복사합니다. 두 Secret이 동일해지면 ConfigMap은 번들에 앵커 하나만 담게 돼요.

그것이 끝나면 Bundle 리소스를 만들어 trust-manager가 신뢰 번들을 어떻게 만들지 알려줄 수 있습니다.

Note

Bundle 리소스는 Certificate 리소스와 다르게 동작합니다. 특히 Bundle 이름은 반드시 생성될 ConfigMap과 일치해야 합니다.

`kubectl apply -f - ---
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
  # This is the name of the Bundle and _also_ the name of the
  # ConfigMap in which we'll write the trust bundle.
  name: linkerd-identity-trust-roots
spec:
  # This tells trust-manager where to find the public keys to copy into
  # the trust bundle.
  sources:
    # This is the Secret that cert-manager will update when it rotates
    # the trust anchor.
    - secret:
        name: "linkerd-trust-anchor"
        key: "tls.crt"

    # This is the Secret that we will use to hold the previous trust
    # anchor; we'll manually update this Secret after we're finished
    # restarting things.
    - secret:
        name: "linkerd-previous-anchor"
        key: "tls.crt"

  # This tells trust-manager the key to use when writing the trust
  # bundle into the ConfigMap. The target stanza doesn't have a way
  # to specify the name of the namespace, but thankfully Linkerd puts
  # a unique label on the control plane's namespace.
  target:
    configMap:
      key: "ca-bundle.crt"
    namespaceSelector:
      matchLabels:
        linkerd.io/is-control-plane: "true"
EOF
`

Note

Linkerd 아이덴티티 발급자는 신뢰 앵커의 공개 키를 포함하므로, trust-manager가 아이덴티티 발급자에서 공개 키를 읽도록 구성할 수도 있습니다. 하지만 trust-manager가 두 네임스페이스의 정보를 읽을 수 없다는 점과, 신뢰 앵커의 순환을 수동으로 트리거하면 cert-manager가 아이덴티티 발급자를 자동으로 순환하지 않는다는 점 때문에 그렇게 하면 복잡해집니다. 그래서 신뢰 앵커 Secret을 직접 사용하는 편이 더 간단해요.

linkerd 네임스페이스에는 아직 linkerd-identity-trust-roots ConfigMap이 보이지 않을 겁니다. Linkerd를 설치할 때까지 그 네임스페이스에 trust-manager가 찾는 라벨이 없기 때문이에요! 그러니 계속해서 Linkerd를 설치해 봅시다.

6. cert-manager가 생성한 인증서로 Linkerd 설치하기

Linkerd가 cert-manager가 만든 인증서를 사용하게 하려면, 다음을 values.yaml 파일에 추가하거나 런타임에 플래그로 전달해야 합니다.

Field Value
identity.externalCA true
identity.issuer.scheme kubernetes.io/tls

Helm으로 설치하기 (권장)

Helm으로 설치하려면 먼저 linkerd-crds 차트를 설치하세요:

`helm install linkerd-crds \
     -n linkerd --create-namespace \
     linkerd/linkerd-crds
`

그다음 linkerd-control-plane 차트를 설치하세요:

`helm install linkerd-control-plane -n linkerd \
  --set identity.externalCA=true \
  --set identity.issuer.scheme=kubernetes.io/tls \
  linkerd/linkerd-control-plane
`

그리고 trust-manager가 찾는 라벨로 linkerd 네임스페이스에도 라벨을 붙여야 합니다:

`kubectl label namespace linkerd linkerd.io/is-control-plane=true
`

Voila! Linkerd 컨트롤 플레인의 TLS 자격증명 자동 순환을 설정했습니다.

CLI로 설치하기

먼저 CRD를 설치하세요:

`linkerd install --crds | kubectl apply -f -
`

그다음 컨트롤 플레인을 설치하세요:

`linkerd install \
  --set identity.externalCA=true \
  --set identity.issuer.scheme=kubernetes.io/tls \
  | kubectl apply -f -
`

Voila! Linkerd 컨트롤 플레인의 TLS 자격증명 자동 순환을 설정했습니다.

7. cert-manager가 한 모든 것을 확인하기!

급하다면 이 단계를 건너뛰어도 되지만, Linkerd의 신뢰 설정을 확인하는 방법을 아는 것은 좋은 생각이에요! 몇 가지 방법이 있는데, 가장 쉬운 것 중 하나는 step CLI로 cert-manager가 방금 설정해 준 여러 Kubernetes 객체에 저장된 실제 인증서를 검사하는 것입니다.

먼저 실제 신뢰 앵커 시크릿을 살펴봅시다. 그것은 cert-manager 네임스페이스의 linkerd-trust-anchor Secret이고, kubectl describe는 그것이 tls.key, tls.crt, ca.crt 키를 가짐을 보여줄 거예요:

`kubectl describe secret -n cert-manager linkerd-trust-anchor
`

tls.key는 보지 않겠습니다. 그건 개인 키니까요! 하지만 tls.crt는 base64로 인코딩된 공개 키이고, 그것을 검사할 수 있어요:

`kubectl get secret -n cert-manager linkerd-trust-anchor \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | step certificate inspect -
`

거기에는 정보가 아주 많이 있습니다. Issuer, Subject, Validity 타임스탬프 등을 확인해 볼 가치가 있어요. 하지만 셸 함수를 사용하면 한 인증서가 다른 인증서에 서명하는 신뢰 체인을 더 쉽게 볼 수 있습니다:

`inspect_cert () {

    sub_selector='\(.extensions.subject_key_id | .[0:16])... \(.subject_dn)'
    iss_selector='\(.extensions.authority_key_id // "................" | .[0:16])... \(.issuer_dn)'
    val_selector='\((.validity.start // .not_before // .notBefore)) → \((.validity.end // .not_after // .notAfter))'

    local input="${1:--}"

    step certificate inspect --bundle --format json "$input" \
    | jq -r '
        # Handle both array (bundle) and single object
        (if type == "array" then .[] else . end)
        | select(type == "object")
        | "Issuer:  '"$iss_selector"'",
          "Subject: '"$sub_selector"'",
          "Valid:   '"$val_selector"'",
          ""
      '
}
`
`kubectl get secret -n cert-manager linkerd-trust-anchor \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert
`

다음과 같은 결과를 볼 수 있어야 합니다:

`Issuer:  ................... CN=root.linkerd.cluster.local
Subject: 421a7aa8e0c92dd0... CN=root.linkerd.cluster.local
Valid:   2025-12-16T18:19:30Z → 2025-12-16T20:19:30Z
`

여기서 Subject의 키 지문(두 번째 줄의 16진수)은 당연히 여러분의 인증서마다 다를 거예요.

우리 설정은 자체 서명 인증서를 사용하므로(예상대로!), issuer 지문은 보이지 않고 issuer와 subject 이름은 같을 것이며, ca.crt 키는 tls.crt와 정확히 같은 정보를 가져야 합니다:

`kubectl get secret -n cert-manager linkerd-trust-anchor \
        -o jsonpath='{ .data.ca\.crt }' \
    | base64 -d | inspect_cert
`

이 출력은 tls.crt 출력과 정확히 같아 보여야 합니다.

신뢰 앵커를 linkerd-previous-anchor Secret으로도 복사했으므로, 거기서도 linkerd-trust-anchor Secret과 정확히 같은 정보를 볼 수 있어야 합니다:

`kubectl get secret -n cert-manager linkerd-previous-anchor \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert

kubectl get secret -n cert-manager linkerd-previous-anchor \
        -o jsonpath='{ .data.ca\.crt }' \
    | base64 -d | inspect_cert
`

Note

기억하세요. 신뢰 앵커에 다른 종류의 issuer를 선택했다면 자체 서명 인증서가 보이지 않아야 하며, ca.crt는 실제로 신뢰 앵커를 발급한 키에 대한 정보를 보여줍니다.

다음으로 아이덴티티 발급자 인증서를 확인할 수 있어요. 이것은 linkerd 네임스페이스의 linkerd-identity-issuer Secret이고, 신뢰 앵커 시크릿과 정확히 같은 구조를 가질 거예요:

`kubectl describe secret -n linkerd linkerd-identity-issuer
`

ca.crt 키는 이 시점에서 방금 신뢰 앵커에서 본 것과 정확히 같아야 합니다:

`kubectl get secret -n linkerd linkerd-identity-issuer \
        -o jsonpath='{ .data.ca\.crt }' \
    | base64 -d | inspect_cert
`

tls.crt 키는 아이덴티티 발급자 인증서의 공개 키여야 하므로, 그 Issuer 줄은 신뢰 앵커의 지문을 보여주고 Subject 줄은 달라야 합니다.

`kubectl get secret -n linkerd linkerd-identity-issuer \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert
`

여기서 다음과 같은 결과를 볼 수 있어야 합니다:

`Issuer:  421a7aa8e0c92dd0... CN=root.linkerd.cluster.local
Subject: 76fc76842fff67f7... CN=identity.linkerd.cluster.local
Valid:   2025-12-16T19:05:55Z → 2025-12-16T20:05:55Z
`

여기서 Issuer 줄은 이전 검사의 Subject 줄과 정확히 같아야 합니다(그리고 다시, 여러분의 16진수 값은 위의 것과 다를 거예요).

Note

신뢰 앵커에 다른 종류의 issuer를 선택했더라도, 아이덴티티 발급자는 여전히 신뢰 앵커에서 본 지문을 보여줘야 합니다.

마지막으로 신뢰 번들을 확인할 수 있어요. 이것은 linkerd 네임스페이스의 linkerd-identity-trust-roots ConfigMap이고, ca-bundle.crt라는 키를 가져야 합니다. 이 키는 우리의 신뢰 번들, 즉 공개 키 집합을 담아야 합니다. 지금은 키가 하나뿐이어야 해요, 바로 현재 신뢰 앵커의 공개 키입니다. 이것은 base64로 인코딩되지 않으므로 곧바로 덤프할 수 있습니다:

`kubectl get configmap -n linkerd linkerd-identity-trust-roots \
        -o jsonpath='{ .data.ca-bundle\.crt }'
`

이것은 단일 PEM CERTIFICATE 블록이어야 합니다:

`-----BEGIN CERTIFICATE-----
...lots of random-looking stuff here...
-----END CERTIFICATE-----
`

그리고 그것을 inspect_cert로 파이프하면 다시 신뢰 앵커의 정보를 볼 수 있어야 합니다.

`kubectl get configmap -n linkerd linkerd-identity-trust-roots \
        -o jsonpath='{ .data.ca-bundle\.crt }' \
    | inspect_cert
`

Note

주의 깊게 읽으면, 위 명령 중 어떤 것도 cert-manager에 특화된 것과 상호작용하지 않는다는 것을 알아챌 수 있을 거예요(cert-manager 네임스페이스에 시크릿을 저장한 것만 제외하면요). linkerd-identity-issuer Secret과 linkerd-identity-trust-roots ConfigMap을 최신 상태로 유지하는 어떤 해법이든 Linkerd와 함께 동작합니다. Linkerd가 필요한 것은 그 두 리소스에 올바른 정보가 들어 있다는 것뿐이에요.

8. 아이덴티티 발급자 순환하기

아이덴티티 발급자를 순환하는 것은 기본적으로 이벤트가 아닙니다. cert-manager가 아이덴티티 발급자 순환을 완전히 스스로 처리할 수 있거든요. 그렇게 하면 linkerd 네임스페이스의 linkerd-identity-issuer Secret을 갱신하고, 그 시점에 모든 Linkerd 프록시가 자동으로 이 변경을 알아차려 워크로드 인증서 발급에 새 인증서를 사용하기 시작합니다. 신뢰 앵커는 변하지 않았으므로 더 필요한 것은 없고 모든 것이 순조롭게 계속됩니다.

Linkerd가 내보내는 IssuerUpdated 이벤트를 확인해 아이덴티티 발급자 순환을 모니터링할 수 있어요:

`kubectl get events --field-selector reason=IssuerUpdated -n linkerd
`

Note

여기서 cert-manager를 그냥 두면 cert-manager가 아이덴티티 발급자를 순환하는 순간 모든 프록시가 즉시 인증서를 순환하지는 않습니다. 프록시는 이전 아이덴티티 발급자가 서명한 워크로드 인증서를 계속 사용하다가 워크로드 인증서를 순환할 때가 되면 바꿉니다. 정상적인 상황에서 이것은 괜찮으며, Linkerd의 아이덴티티 컨트롤러의 부하를 줄여줍니다.

프록시가 즉시 인증서를 순환하도록 강제해야 한다면, 워크로드를 재시작하면 됩니다.

9. 신뢰 앵커 순환하기

신뢰 앵커의 순환은 조금 다릅니다. (앞서 언급했듯) 신뢰 앵커를 순환한다는 것은 신뢰 번들을 관리하면서 Linkerd 컨트롤 플레인과 모든 프록시를 재시작해야 한다는 뜻이기 때문이에요. 실제로는 이것에 수동 개입이 필요합니다. cert-manager가 신뢰 앵커를 실제로 순환하는 어려운 작업을 처리할 수는 있어도, 필요한 재시작을 트리거할 수는 없기 때문이에요.

이는 신뢰 앵커 순환을 가장 간단하게 처리하는 방법이, 여러분에게 편리한 때 순환을 수동으로 트리거해서 cert-manager가 신뢰 앵커 인증서를 관리하게 두면서 신뢰 번들과 재시작을 직접 관리하는 것임을 뜻합니다.

실제로 그렇게 하는 과정은 간단하지만, 역시 여러 단계가 있습니다.

  • 신뢰 앵커 순환 트리거하기
  • 컨트롤 플레인 재시작하기
  • 데이터 플레인 재시작하기
  • 아이덴티티 발급자 순환 트리거하기
  • 컨트롤 플레인 재시작하기
  • 데이터 플레인 재시작하기
  • 신뢰 번들에서 이전 앵커 제거하기

Note

신뢰 앵커의 개인 키는 시스템의 다른 어떤 키보다 덜 사용된다는 점을 알아둘 만합니다. 올바르게 설정하면 그것은 오직 아이덴티티 발급자를 순환할 때만 사용되며, 그것은 비교적 드물게 일어나고 실제로는 완전히 클러스터 밖에서 일어날 수도 있어요! 이는 신뢰 앵커를 아예 순환할 필요가 있는지 신중히 생각해야 함을 뜻합니다. 위협 모델과 보안 요구사항에 따라 신뢰 앵커를 클러스터가 살아있는 한 그냥 두는 것이 타당할 수도 있어요.

1. 신뢰 앵커 순환 트리거하기

cert-manager가 신뢰 앵커를 순환하도록 트리거부터 시작하세요. 가장 쉬운 방법은 cert-manager의 cmctl CLI를 쓰는 것입니다:

`cmctl renew -n cert-manager linkerd-trust-anchor
`

이것은 cert-manager가 신뢰 앵커 인증서를 순환하게 만들고, linkerd-trust-anchor Secret을 갱신하며, trust-manager가 linkerd-identity-trust-roots ConfigMap을 갱신하도록 트리거합니다. kubectl로 이 둘을 모두 확인할 수 있습니다(그래야 합니다!). linkerd-trust-anchor와 linkerd-previous-anchor의 Subject 키가 더 이상 같지 않아야 해요:

`kubectl get secret -n cert-manager linkerd-trust-anchor \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert

kubectl get secret -n cert-manager linkerd-previous-anchor \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert
`

linkerd-identity-trust-roots ConfigMap이 이제 두 Secret의 Subject 키를 모두 포함하는지 확인하세요.

`kubectl get configmap -n linkerd linkerd-identity-trust-roots \
        -o jsonpath='{ .data.ca-bundle\.crt }' \
        | inspect_cert
`

이 시점에도 mTLS 통신은 여전히 잘 동작합니다. 데이터 플레인의 모든 프록시는 여전히 이전 신뢰 앵커가 서명한 이전 아이덴티티 발급자를 사용하고 있고, 그 신뢰 앵커는 여전히 linkerd-identity-trust-roots에 존재하니까요.

2. 컨트롤 플레인 재시작하기

kubectl rollout restart로 컨트롤 플레인을 재시작하세요:

`kubectl rollout restart -n linkerd deploy
kubectl rollout status -n linkerd deploy
`

이것은 컨트롤 플레인이 새 신뢰 앵커 번들을 사용하게 합니다.

3. 데이터 플레인 재시작하기

이 시점에서 워크로드도 재시작해 프록시가 새 신뢰 앵커 번들을 갖도록 해야 합니다. 정확한 메커니즘은 워크로드에 따라 다르지만, 보통은 각 애플리케이션 네임스페이스에 대해 kubectl rollout restart를 실행하는 것뿐이에요.

4. 아이덴티티 발급자 순환 트리거하기

모든 것이 여전히 이전 아이덴티티 발급자를 사용합니다. 이상하게도 cert-manager가 신뢰 앵커를 순환하도록 수동으로 트리거해도 아이덴티티 발급자는 자동으로 순환되지 않기 때문이에요. 아이덴티티 발급자를 직접 다시 확인해서 이를 검증할 수 있습니다:

`kubectl get secret -n linkerd linkerd-identity-issuer \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert
`

그 issuer가 여전히 이전 신뢰 앵커임을 알게 될 거예요. 그러므로 우리의 다음 단계는 cert-manager가 아이덴티티 발급자를 순환하도록 트리거하는 것입니다.

`cmctl renew -n linkerd linkerd-identity-issuer
`

지금 아이덴티티 발급자를 다시 확인하면, 그것이 새 신뢰 앵커로 서명된 것을 볼 수 있어요:

`kubectl get secret -n linkerd linkerd-identity-issuer \
        -o jsonpath='{ .data.tls\.crt }' \
    | base64 -d | inspect_cert
`

5. 컨트롤 플레인 재시작하기

kubectl rollout restart로 컨트롤 플레인을 다시 재시작하세요:

`kubectl rollout restart -n linkerd deploy
kubectl rollout status -n linkerd deploy
`

이것은 컨트롤 플레인이 새 신뢰 앵커와 아이덴티티 발급자를 사용하게 합니다.

6. 데이터 플레인 재시작하기

다시, 워크로드를 재시작해 프록시가 새 신뢰 앵커로 서명된 새 아이덴티티 발급자로 전환하도록 해야 합니다. 정확한 메커니즘은 워크로드에 따라 다르지만, 보통은 각 애플리케이션 네임스페이스에 대해 kubectl rollout restart를 실행하는 것뿐이에요.

7. 신뢰 번들에서 이전 앵커 제거하기

마지막 단계 하나: 모든 것이 재시작되면 신뢰 번들에서 이전 신뢰 앵커를 제거해야 합니다. 그러려면 linkerd-trust-anchor Secret을 linkerd-previous-anchor Secret으로 복사하기만 하면 됩니다. 그러면 trust-manager가 신뢰 번들 ConfigMap을 갱신하도록 트리거됩니다.

Note

신뢰 앵커의 개인 키를 완전히 클러스터 밖에 두고 있다면 신뢰 앵커가 Secret이 아닌 다른 리소스에 저장될 수 있습니다. 그 경우 이 명령을 여러분의 리소스 유형에 맞게 수정해야 합니다.

`kubectl -n cert-manager get secret linkerd-trust-anchor -o json \
  | jq '{apiVersion: .apiVersion, kind: .kind, metadata: {name: "linkerd-previous-anchor", namespace: .metadata.namespace}, type: .type, data: .data}' \
  | kubectl apply -f -
`

kubectl로 다시 한 번 확인할 수 있습니다:

`kubectl get configmap -n linkerd linkerd-identity-trust-roots \
        -o jsonpath='{ .data.ca-bundle\.crt }' \
        | inspect_cert
`

그러면 현재 신뢰 앵커의 단일 ID만 보여야 합니다.

이 시점에서 순환이 완료됩니다. 모든 것이 새 신뢰 앵커를 사용하고, 이전 신뢰 앵커는 더 이상 신뢰되지 않아요. 아직 사용 중인 단 하나의 신뢰 앵커만 인식하도록 워크로드를 한 번 더 재시작해야 합니다.

더 알아보기 (Learn more)

  • 웹훅 TLS 자격증명 자동 순환 (Automatically Rotating Webhook TLS Credentials)
  • Linkerd 신뢰 앵커 자격증명 수동 순환 (Manually rotating Linkerd's trust anchor credentials)