v1.23으로 업그레이드하기 전 웹훅 인증서의 호환성 확인


버전 1.23부터 Kubernetes에서는 인증서에서 X.509 일반 이름(CN) 필드를 사용하는 서버 ID 검증이 더 이상 지원되지 않습니다. 대신 Kubernetes에서는 X.509 제목 대체 이름(SAN) 필드의 정보만 사용됩니다.

클러스터에 영향을 주지 않으려면 클러스터를 Kubernetes 버전 1.23으로 업그레이드하기 전에 웹훅 및 집계 API 서버의 백엔드의 SAN 없는 비호환 인증서를 교체해야 합니다.

Kubernetes가 SAN이 없는 백엔드 인증서를 더 이상 지원하지 않는 이유

GKE는 오픈소스 Kubernetes를 작동하여, kube-apiserver 구성요소를 사용하고 전송 계층 보안(TLS)를 사용해서 웹훅 및 집계된 API 서버 백엔드에 연결합니다. kube-apiserver 구성요소는 Go 프로그래밍 언어에 기록됩니다.

Go 1.15 이전에는 TLS 클라이언트가 연결 서버의 ID의 유효성을 2단계 프로세스를 통해 검증했습니다.

  1. 서버의 DNS 이름(또는 IP 주소)이 서버 인증서의 SAN 중 하나로 존재하는지 확인
  2. 대체를 위해 서버의 DNS 이름(또는 IP 주소)이 서버 인증서의 CN과 동일한지 확인

RFC 6125는 2011년 CN 필드 기반의 서버 ID 검증을 완전히 지원 중단했습니다. 브라우저 및 기타 보안 핵심 애플리케이션에는 이 필드가 더 이상 사용되지 않습니다.

더 많은 TLS 에코시스템을 지원하기 위해 Go 1.15에서는 해당 검증 프로세스에서 2단계가 삭제되었지만 마이그레이션 프로세스를 간소화하기 위해 이전 동작을 사용 설정하도록 디버그 스위치(x509ignoreCN=0)를 남겨 두었습니다. Kubernetes 버전 1.19는 Go 1.15를 사용하여 빌드된 최초의 버전입니다. 버전 1.19부터 1.22까지 GKE 클러스터에서는 영향을 받는 웹훅 및 집계된 API 서버 백엔드의 인증서를 대체하는 데 더 많은 시간을 고객들에게 제공할 수 있도록 기본적으로 디버그 스위치가 사용 설정되었습니다.

Kubernetes 버전 1.23디버그 스위치를 삭제하는 Go 1.17로 빌드됩니다. GKE가 클러스터를 버전 1.23으로 업그레이드하면 호출이 클러스터의 제어 영역으로부터 적절한 SAN이 있는 유효한 X.509 인증서를 제공하지 않는 웹훅 또는 집계 API 서비스로의 호출이 연결에 실패하게 됩니다.

영향을 받는 클러스터 식별

최소 1.21.9 또는 1.22.3의 패치 버전을 실행하는 클러스터

Cloud Logging이 사용 설정되었고 패치 버전이 1.21.9 및 1.22.3 또는 그 이상인 클러스터에서 GKE는 클러스터에서 영향을 받는 백엔드에 대해 호출을 식별할 수 있도록 Cloud 감사 로그를 제공합니다. 다음 필터를 사용해서 로그를 검색할 수 있습니다.

logName =~ "projects/.*/logs/cloudaudit.googleapis.com%2Factivity"
resource.type = "k8s_cluster"
operation.producer = "k8s.io"
"invalid-cert.webhook.gke.io"

클러스터가 영향을 받는 인증서를 사용해서 백엔드를 호출하지 않았으면 로그가 표시되지 않습니다. 이러한 감사 로그가 표시되면 영향을 받는 백엔드의 호스트 이름이 포함됩니다.

다음은 default 네임스페이스의 example-webhook이라는 서비스에서 호스팅되는 웹훅 백엔드의 로그 항목 예시입니다.

{
  ...
  resource {
    type: "k8s_cluster",
    "labels": {
      "location": "us-central1-c",
      "cluster_name": "example-cluster",
      "project_id": "example-project"
    }
  },
  labels: {
    invalid-cert.webhook.gke.io/example-webhook.default.svc: "No subjectAltNames returned from example-webhook.default.svc:8443",
    ...
  },
  logName: "projects/example-project/logs/cloudaudit.googleapis.com%2Factivity",
  operation: {
    ...
    producer: "k8s.io",
    ...
  },
  ...
}

영향을 받는 서비스의 호스트 이름(예를 들어 example-webhook.default.svc)은 invalid-cert.webhook.gke.io/로 시작하는 라벨 이름에 서픽스로 포함됩니다. 또한 resource.labels.cluster_name 라벨에서 호출을 수행하는 클러스터 이름을 가져올 수 있는데, 이 예시에서는 example-cluster 값을 가집니다.

지원 중단 통계

지원 중단 통계에서 호환되지 않는 인증서를 사용하는 클러스터를 확인할 수 있습니다. 통계는 버전 1.22.6-gke.1000 이상을 실행하는 클러스터에서 이용할 수 있습니다.

기타 클러스터 버전

1.22.3 이전 패치 버전(1.22 부 버전) 또는 1.21.9 이전 패치 버전의 클러스터를 가진 경우 클러스터가 이러한 지원 중단의 영향을 받는지 확인하는 방법은 두 가지입니다.

옵션 1(권장): 로그를 사용해서 영향을 받는 인증서 식별을 지원하는 패치 버전으로 클러스터를 업그레이드합니다. 클러스터에 Cloud Logging이 사용 설정되었는지 확인합니다. 클러스터가 업그레이드된 후에는 클러스터가 적절한 SAN이 포함된 인증서를 제공하지 않는 서비스를 호출하려고 시도할 때마다 Cloud 감사 로그 식별 로그가 생성됩니다. 호출을 시도할 때 로그가 생성되므로, 모든 호출 경로가 호출되도록 업그레이드 후 30일까지 충분한 시간을 기다리는 것이 좋습니다.

로그를 사용하여 영향을 받는 서비스를 식별하는 방법을 권장하는데, 이 접근 방식은 영향을 받는 서비스를 보여주는 로그를 자동으로 생성하여 수동 작업을 최소화하기 때문입니다.

옵션 2: 클러스터의 웹훅 또는 집계 API 서비스에서 사용하는 모든 인증서를 검사하여 SAN이 없어 영향을 받는지 확인하는 방법.

  1. 클러스터의 웹훅 및 집계 API 서버 목록을 가져와 백엔드(서비스 또는 URL)를 식별합니다.
  2. 백엔드 서비스에 사용되는 인증서를 검사합니다.

이 방법으로 모든 인증서를 검사하는 데 필요한 수동 작업을 감안할 때, 클러스터를 버전 1.21로 업그레이드하기 전에 Kubernetes 버전 1.23에서 지원 중단의 영향을 평가해야 하는 경우에만 이 방법을 따라야 합니다. 클러스터를 1.21로 업그레이드할 수 있는 경우 먼저 업그레이드를 수행한 다음 옵션 1의 안내에 따라 수동 작업을 진행해야 합니다.

검사할 백엔드 서비스 식별

지원 중단의 영향을 받을 수 있는 백엔드를 식별하려면 클러스터에서 웹훅 및 집계 API 서비스 및 관련 백엔드 목록을 가져옵니다.

클러스터에서 모든 관련 웹훅을 나열하려면 다음 kubectl 명령어를 사용합니다.

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

웹훅 구성에서 clientConfig.service 필드 또는webhooks.clientConfig.url 필드를 검사하여 해당 웹훅의 연결된 백엔드 서비스 또는 URL을 가져올 수 있습니다.

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

이 명령어 결과는 다음과 비슷합니다.

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- admissionReviewVersions:
  clientConfig:
    service:
        name: example-service
        namespace: default
        port: 443

참고로 clientConfig는 백엔드를 Kubernetes 서비스(clientConfig.service) 또는 URL(clientConfig.url)로 지정할 수 있습니다.

클러스터에서 모든 관련된 집계 API 서비스를 나열하려면 다음 kubectl 명령어를 사용합니다.

kubectl get apiservices -A |grep -v Local      # aggregated API services

이 명령어 결과는 다음과 비슷합니다.

NAME                     SERVICE                      AVAILABLE   AGE
v1beta1.metrics.k8s.io   kube-system/metrics-server   True        237d

이 예시에서는 kube-system 네임스페이스에서 metric-server 서비스를 반환합니다.

spec.service 필드를 검사하여 지정된 집계 API에 대해 연관된 서비스를 가져올 수 있습니다.

kubectl get apiservices v1beta1.metrics.k8s.io -o yaml

이 명령어 결과는 다음과 비슷합니다.

...
apiVersion: apiregistration.k8s.io/v1
kind: APIService
spec:
  service:
    name: metrics-server
    namespace: kube-system
    port: 443

서비스 인증서 검사

검사할 관련된 백엔드 서비스를 식별한 후에는 example-service와 같은 특정 서비스의 인증서를 검사할 수 있습니다.

  1. 서비스의 선택기 및 대상 포트를 찾으세요.

    kubectl describe service example-service
    

    이 명령어 결과는 다음과 비슷합니다.

    Name: example-service
    Namespace: default
    Labels: run=nginx
    Selector: run=nginx
    Type: ClusterIP
    IP: 172.21.xxx.xxx
    Port: 443
    TargetPort: 444
    

    이 예시에서 example-service에는 run=nginx 선택기와 대상 포트 444가 포함됩니다.

  2. 선택기와 일치하는 포드를 찾으세요.

    kubectl get pods --selector=run=nginx
    

    이 명령어 출력은 다음과 비슷합니다.

    NAME          READY   STATUS    RESTARTS   AGE
    example-pod   1/1     Running   0          21m
    
  3. 포트 전달

    kubectl localhost에서 포드로 설정합니다.

    kubectl port-forward pods/example-pod LOCALHOST_PORT:TARGET_PORT # port forwarding in background
    

    명령어에서 다음을 바꿉니다.

    • LOCALHOST_PORT: 리슨할 주소
    • TARGET_PORT: 1단계의 TargetPort
  4. openssl을 사용하여 서비스에서 사용하는 인증서를 출력하세요.

    openssl s_client -connect localhost:LOCALHOST_PORT </dev/null | openssl x509 -noout -text
    

    다음은 유효한 인증서(SAN 항목 포함)를 보여주는 예시 출력입니다.

    Subject: CN = example-service.default.svc
    X509v3 extensions:
      X509v3 Subject Alternative Name:
        DNS:example-service.default.svc
    

    다음은 누락된 SAN이 있는 인증서를 보여주는 예시 출력입니다.

    Subject: CN = example-service.default.svc
      X509v3 extensions:
          X509v3 Key Usage: critical
              Digital Signature, Key Encipherment
          X509v3 Extended Key Usage:
              TLS Web Server Authentication
          X509v3 Authority Key Identifier:
              keyid:1A:5F:29:D8:E9:3C:54:3C:35:CC:D8:AB:D1:21:FD:C3:56:25:C0:74
    
  5. 다음 명령어를 사용하여 포트 전달이 백그라운드에서 실행되지 않도록 하세요.

    $ jobs
    [1]+  Running                 kubectl port-forward pods/example-pod 8888:444 &
    $ kill %1
    [1]+  Terminated              kubectl port-forward pods/example 8888:444
    

URL 백엔드의 인증서 검사

웹훅에 url 백엔드가 사용되는 경우 URL에 지정된 호스트 이름에 직접 연결합니다. 예를 들어 URL이 https://example.com:123/foo/bar이면 다음 openssl 명령어를 사용해서 백엔드에 사용되는 인증서를 출력합니다.

  openssl s_client -connect example.com:123 </dev/null | openssl x509 -noout -text

1.23 업그레이드 위험 완화

SAN 없는 인증서를 사용하여 영향을 받는 클러스터와 백엔드 서비스를 식별한 후에는 클러스터를 버전 1.23으로 업그레이드하기 전에 적절한 SAN이 있는 인증서를 사용하도록 웹훅 및 집계 API 서버 백엔드를 업데이트해야 합니다.

인증서를 교체하거나 버전 1.22지원 종료될 때까지는 비호환 인증서를 사용하는 백엔드 버전 1.22.6-gke.1000 이상의 클러스터를 자동으로 업그레이드하지 않습니다.

클러스터가 1.22.6-gke.1000 이전의 GKE 버전을 사용하는 경우 부 버전 업그레이드를 하지 않도록 유지보수 제외를 구성하여 자동 업그레이드를 일시적으로 막아둘 수 있습니다.

리소스

이 변경사항에 대한 상세 설명은 다음 리소스를 참조하세요.

  • Kubernetes 1.23 출시 노트
    • Kubernetes는 Go 1.17을 사용하여 빌드됩니다. 이 Go 버전은 X.509 제공 인증서의 CN을 호스트 이름을 취급하는 지원 중단된 기존 동작을 다시 사용 설정하기 위한 GODEBUG=x509ignoreCN=0 환경 설정 사용 기능을 삭제합니다.
  • Kubernetes 1.19Kubernetes 1.20 출시 노트
    • SAN이 없을 때 X.509 제공 인증서의 CN 필드를 호스트 이름으로 취급하는 지원 중단된 레거시 동작은 이제 기본적으로 사용 중지되어 있습니다.
  • Go 1.17 출시 노트
    • 임시 GODEBUG=x509ignoreCN=0 플래그가 삭제되었습니다.
  • Go 1.15 출시 노트
    • SAN이 없을 때 X.509 인증서의 CN 필드를 호스트로 취급하는 지원 중단된 레거시 동작은 이제 기본적으로 사용 중지되어 있습니다.
  • RFC 6125 (46페이지)
    • CN 값 사용은 기존 방식이지만 지원 중단되었으며 인증 기관은 subjectAltName 값을 대신 제공하는 것이 좋습니다.
  • 허용 웹훅