목록으로

Programming Notes

Kubernetes v1.35: CSI 드라이버에 서비스 계정 토큰을 전달하는 더 나은 방법

서비스 계정 토큰을 사용하는 CSI 드라이버를 유지 관리하는 경우, Kubernetes v1.35에 도입된 개선 사항에 주목할 필요가 있습니다. TokenRequests 기능 이 도입된 이후, CSI 드라이버가 요청한 서비스 계정 토큰은 volume_context 필드를 통해...

서비스 계정 토큰을 사용하는 CSI 드라이버를 유지 관리하는 경우, Kubernetes v1.35에 도입된 개선 사항에 주목할 필요가 있습니다. TokenRequests 기능이 도입된 이후, CSI 드라이버가 요청한 서비스 계정 토큰은 volume_context 필드를 통해 전달되었습니다. 이 방법이 작동하기는 했지만, 민감한 정보를 보관하기에 이상적인 장소는 아니었으며, CSI 드라이버에서 토큰이 실수로 로깅되는 사례가 발생하기도 했습니다.

Kubernetes v1.35는 이를 해결하기 위한 베타 솔루션을 도입합니다: CSI 드라이버의 시크릿 필드를 통한 서비스 계정 토큰 옵트인. 이 기능을 통해 CSI 드라이버는 NodePublishVolumeRequestsecrets 필드를 통해 서비스 계정 토큰을 받을 수 있으며, 이는 CSI 사양에서 민감한 데이터를 위한 적절한 장소입니다.

기존 접근 방식 이해

CSI 드라이버가 TokenRequests 기능을 사용하면, CSIDriver 스펙의 TokenRequests 필드를 구성하여 워크로드 아이덴티티를 위한 서비스 계정 토큰을 요청할 수 있습니다. 이 토큰은 csi.storage.k8s.io/serviceAccount.tokens 키를 사용하여 볼륨 속성 맵의 일부로 드라이버에 전달됩니다.

volume_context 필드가 작동하지만, 민감한 데이터를 위해 설계된 것은 아닙니다. 이로 인해 몇 가지 과제가 발생합니다:

첫째, CSI 드라이버가 사용하는 protosanitizer 도구는 볼륨 컨텍스트를 민감한 것으로 취급하지 않으므로, gRPC 요청이 로깅될 때 서비스 계정 토큰이 로그에 노출될 수 있습니다. 이는 Secrets Store CSI 드라이버의 CVE-2023-2878과 Azure File CSI 드라이버의 CVE-2024-3744에서 발생했습니다.

둘째, 이 문제를 피하려는 각 CSI 드라이버는 자체적인 정리(sanitization) 로직을 구현해야 하므로 드라이버 간에 불일치가 발생합니다.

CSI 사양에는 이미 이러한 종류의 민감한 정보를 위해 정확히 설계된 NodePublishVolumeRequestsecrets 필드가 있습니다. 문제는 기존 CSI 드라이버가 볼륨 컨텍스트에서 토큰을 기대하기 때문에, 토큰을 배치하는 위치를 단순히 변경할 경우 기존 드라이버가 오작동할 수 있다는 것입니다.

옵트인 메커니즘 작동 방식

Kubernetes v1.35는 CSI 드라이버가 서비스 계정 토큰을 받는 방식을 선택할 수 있도록 하는 옵트인(opt-in) 메커니즘을 도입합니다. 이 방법을 통해 기존 드라이버는 현재와 동일하게 작동하며, 드라이버가 준비되면 더 적절한 secrets 필드로 전환할 수 있습니다.

CSI 드라이버는 CSIDriver 스펙에 새 필드를 설정할 수 있습니다:

#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: example-csi-driver
spec:
  # ... existing fields ...
  tokenRequests:
  - audience: "example.com"
    expirationSeconds: 3600
  # New field for opting into secrets delivery
  serviceAccountTokenInSecrets: true # defaults to false

동작은 serviceAccountTokenInSecrets 필드에 따라 달라집니다:

false(기본값)로 설정된 경우, 토큰은 오늘날과 마찬가지로 csi.storage.k8s.io/serviceAccount.tokens 키와 함께 VolumeContext에 배치됩니다. true로 설정된 경우, 토큰은 동일한 키와 함께 Secrets 필드에만 배치됩니다.

베타 릴리스에 대하여

CSIServiceAccountTokenSecrets 기능 게이트는 kubelet과 kube-apiserver 모두에서 기본적으로 활성화됩니다. serviceAccountTokenInSecrets 필드의 기본값이 false이므로, 기능 게이트를 활성화해도 기존 동작에는 변화가 없습니다. 모든 드라이버는 명시적으로 옵트인하지 않는 한 볼륨 컨텍스트를 통해 토큰을 계속 수신합니다. 이것이 저희가 알파 대신 베타로 시작하는 데 부담을 느끼지 않은 이유입니다.

CSI 드라이버 작성자를 위한 가이드

서비스 계정 토큰을 사용하는 CSI 드라이버를 유지 관리하는 경우, 이 기능을 채택하는 방법은 다음과 같습니다.

폴백(Fallback) 로직 추가

먼저, 토큰을 위해 두 위치를 모두 확인하도록 드라이버 코드를 업데이트하세요. 이렇게 하면 드라이버가 기존 방식과 새 방식 모두와 호환됩니다:

const serviceAccountTokenKey = "csi.storage.k8s.io/serviceAccount.tokens"

func getServiceAccountTokens(req *csi.NodePublishVolumeRequest) (string, error) {
 // Check secrets field first (new behavior when driver opts in)
 if tokens, ok := req.Secrets[serviceAccountTokenKey]; ok {
 return tokens, nil
 }

 // Fall back to volume context (existing behavior)
 if tokens, ok := req.VolumeContext[serviceAccountTokenKey]; ok {
 return tokens, nil
 }

 return "", fmt.Errorf("service account tokens not found")
}

이 폴백 로직은 하위 호환되며, 클러스터가 v1.35로 업그레이드하기 전에도 모든 드라이버 버전에 안전하게 포함하여 배포할 수 있습니다.

롤아웃 순서

CSI 드라이버 작성자는 기존 볼륨이 손상되는 것을 방지하기 위해 이 기능을 채택할 때 특정 순서를 따라야 합니다.

드라이버 준비 (언제든지 가능)

지금 바로 드라이버 준비를 시작할 수 있습니다. 토큰을 위해 secrets 필드와 볼륨 컨텍스트를 모두 확인하는 폴백 로직을 추가하세요. 이 코드 변경은 하위 호환되며, 클러스터가 v1.35로 업그레이드하기 전에도 모든 드라이버 버전에 안전하게 포함하여 배포할 수 있습니다. 이 폴백 로직을 조기에 추가하고, 릴리스를 발행하며, 가능한 경우 유지 관리 브랜치에도 백포트할 것을 권장합니다.

클러스터 업그레이드 및 기능 활성화

드라이버에 폴백 로직이 배포되면, 클러스터에서 이 기능을 안전하게 활성화하는 롤아웃 순서는 다음과 같습니다:

  1. kube-apiserver를 1.35 이상으로 업그레이드를 완료합니다.
  2. 모든 노드에서 kubelet을 1.35 이상으로 업그레이드를 완료합니다.
  3. 폴백 로직이 포함된 CSI 드라이버 버전이 배포되었는지 확인합니다 (준비 단계에서 완료하지 않았다면).
  4. 모든 노드에 걸쳐 CSI 드라이버 DaemonSet 롤아웃을 완전히 완료합니다.
  5. CSIDriver 매니페스트를 업데이트하여 serviceAccountTokenInSecrets: true로 설정합니다.

중요 제약 사항

가장 중요한 것은 타이밍입니다. CSI 드라이버 DaemonSet과 CSIDriver 객체가 동일한 매니페스트 또는 Helm 차트에 포함되어 있다면, 두 번의 별도 업데이트가 필요합니다. 먼저 폴백 로직이 포함된 새 드라이버 버전을 배포하고, DaemonSet 롤아웃이 완료될 때까지 기다린 다음, CSIDriver 스펙을 업데이트하여 serviceAccountTokenInSecrets: true로 설정해야 합니다.

또한, 모든 드라이버 파드가 롤아웃되기 전에 CSIDriver를 업데이트하지 마십시오. 그렇게 하면 이전 드라이버 버전을 실행 중인 노드에서는 볼륨 마운트가 실패할 수 있습니다. 해당 파드들은 볼륨 컨텍스트만 확인하기 때문입니다.

이 기능이 중요한 이유

이 기능을 채택하는 것은 몇 가지 면에서 도움이 됩니다:

  • gRPC 요청에서 서비스 계정 토큰이 볼륨 컨텍스트의 일부로 실수로 로깅될 위험을 제거합니다.
  • CSI 사양의 민감한 데이터를 위한 지정된 필드를 사용하므로, 더욱 적절하다고 느껴집니다.
  • protosanitizer 도구가 secrets 필드를 자동으로 올바르게 처리하므로, 드라이버별 해결 방법을 사용할 필요가 없습니다.
  • 옵트인 방식이므로, 기존 배포를 중단하지 않고 자체 속도로 마이그레이션할 수 있습니다.

참여 요청

저희(Kubernetes SIG Storage)는 CSI 드라이버 작성자들이 이 기능을 채택하고 마이그레이션 경험에 대한 피드백을 제공해 주시기를 권장합니다. API 디자인에 대한 의견이 있거나 채택 과정에서 문제가 발생하면, Kubernetes Slack의 #csi 채널로 문의해 주십시오 (초대장은 https://slack.k8s.io/에서 받을 수 있습니다).

KEP-5538을 통해 향후 Kubernetes 릴리스의 진행 상황을 추적할 수 있습니다.