저장 시 기밀 데이터 암호화하기
저장 시 기밀 데이터 암호화하기 (Encrypting Confidential Data at Rest)
쿠버네티스에서 영구 API 리소스 데이터를 쓸 수 있게 하는 모든 API는 저장 시 암호화(at-rest encryption)를 지원해요. 예를 들어 Secrets에 대해 저장 시 암호화를 활성화할 수 있어요. 이 저장 시 암호화는 etcd 클러스터나 kube-apiserver를 실행하는 호스트의 파일시스템에 대한 시스템 레벨 암호화에 더해지는 추가적인 것이에요.
이 페이지는 API 데이터의 저장 시 암호화를 활성화하고 구성하는 방법을 보여드려요.
참고: 이 작업은 쿠버네티스 API를 사용해 저장되는 리소스 데이터의 암호화를 다뤄요. 예를 들어 Secret 객체(그들이 포함하는 키-값 데이터를 포함)를 암호화할 수 있어요. 컨테이너에 마운트된 파일시스템의 데이터를 암호화하려면 다음 중 하나를 해야 해요.
- 암호화된 볼륨을 제공하는 스토리지 통합 사용
- 자체 애플리케이션 안에서 데이터 암호화
출처: 문서
본문
시작하기 전에 (Before you begin)
쿠버네티스 클러스터가 필요하고, kubectl 명령줄 도구가 클러스터와 통신하도록 구성돼 있어야 해요. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 두 개 이상 있는 클러스터에서 실행하는 것을 권장해요. 아직 클러스터가 없다면 minikube로 만들거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있어요.
-
iximiuz Labs
-
Killercoda
-
KodeKloud
-
이 작업은 각 컨트롤 플레인 노드에서 쿠버네티스 API 서버를 정적 파드로 실행한다고 가정해요.
-
클러스터의 컨트롤 플레인이 etcd v3.x를 사용해야 해요(메이저 버전 3, 어떤 마이너 버전이든).
-
커스텀 리소스를 암호화하려면 클러스터가 Kubernetes v1.26 이상을 실행해야 해요.
-
와일드카드를 사용해 리소스를 일치시키려면 클러스터가 Kubernetes v1.27 이상을 실행해야 해요.
-
버전을 확인하려면
kubectl version을 입력하세요.
저장 시 암호화가 이미 활성화됐는지 확인하기 (Determine whether encryption at rest is already enabled)
기본적으로 API 서버는 리소스의 평문(plain-text) 표현을 저장 시 암호화 없이 etcd에 저장해요.
kube-apiserver 프로세스는 구성 파일 경로를 지정하는 --encryption-provider-config 인자를 받아요. 지정하면 그 파일의 내용이 쿠버네티스 API 데이터가 etcd에서 어떻게 암호화되는지 제어해요. --encryption-provider-config 명령줄 인자 없이 kube-apiserver를 실행한다면 저장 시 암호화가 활성화되지 않은 거예요. --encryption-provider-config 명령줄 인자로 실행하지만 참조하는 파일이 목록의 첫 번째 암호화 제공자로 identity 제공자를 지정한다면, 저장 시 암호화가 활성화되지 않은 거예요(기본 identity 제공자는 어떤 기밀성 보호도 제공하지 않아요).
--encryption-provider-config 명령줄 인자로 실행하고, 참조하는 파일이 목록의 첫 번째 암호화 제공자로 identity 외의 다른 제공자를 지정한다면 이미 저장 시 암호화가 활성화된 거예요. 하지만 이 확인은 이전에 암호화된 스토리지로의 마이그레이션이 성공했는지는 알려주지 않아요. 확실하지 않다면 모든 관련 데이터가 암호화됐는지 확인을 참고하세요.
저장 시 암호화 구성 이해하기 (Understanding the encryption at rest configuration)
---
#
# 주의: 이것은 예시 구성입니다.
# 자신의 클러스터에 이 것을 사용하지 마세요!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # 커스텀 리소스 API
providers:
# 이 구성은 데이터 기밀성을 제공하지 않습니다. 첫 번째
# 구성된 제공자는 리소스를 평문으로 저장하는 "identity" 메커니즘을 지정합니다.
#
- identity: {} # 평문, 즉 암호화 없음
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # 아래에 *.*가 지정되어 있어도 Events를 암호화하지 않음
- resources:
- '*.apps' # 와일드카드 일치는 Kubernetes 1.27 이상 필요
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # 와일드카드 일치는 Kubernetes 1.27 이상 필요
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
각 resources 배열 항목은 별도의 구성이며 완전한 구성을 포함해요. resources.resources 필드는 Secrets, ConfigMaps, 또는 다른 리소스처럼 암호화되어야 하는 쿠버네티스 리소스 이름(resource 또는 resource.group)의 배열이에요.
커스텀 리소스가 EncryptionConfiguration에 추가되고 클러스터 버전이 1.26 이상이라면, EncryptionConfiguration에 언급된 새로 생성된 커스텀 리소스는 암호화돼요. 그 버전과 구성 이전에 etcd에 존재했던 커스텀 리소스는 다음에 스토리지에 쓰일 때까지 암호화되지 않은 채 남아요. 이는 내장 리소스와 동일한 동작이에요. "모든 Secrets 암호화 확인" 섹션을 참고하세요.
providers 배열은 나열한 API에 사용할 가능한 암호화 제공자의 정렬된 목록이에요. 각 제공자는 여러 키를 지원해요. 키는 복호화를 위해 순서대로 시도되고, 제공자가 첫 번째 제공자라면 첫 번째 키가 암호화에 사용돼요.
항목당 한 가지 제공자 유형만 지정할 수 있어요(identity 또는 aescbc를 제공할 수 있지만 같은 항목에 둘 다는 안 돼요). 목록의 첫 번째 제공자는 스토리지에 쓰이는 리소스를 암호화하는 데 사용돼요. 스토리지에서 리소스를 읽을 때 저장된 데이터와 일치하는 각 제공자가 순서대로 데이터 복호화를 시도해요. 형식이나 secret 키의 불일치로 어떤 제공자도 저장된 데이터를 읽을 수 없다면, 클라이언트가 그 리소스에 접근하지 못하게 하는 오류가 반환돼요.
EncryptionConfiguration은 암호화되어야 할 리소스를 지정할 때 와일드카드 사용을 지원해요. *.<group>을 사용해 그룹 내의 모든 리소스를 암호화하거나(예: 위 예시의 *.apps), *.*를 사용해 모든 리소스를 암호화해요. *.은 코어 그룹의 모든 리소스를 암호화하는 데 사용할 수 있어요. *.*은 API 서버 시작 후 추가된 커스텀 리소스까지 포함해 모든 리소스를 암호화해요.
참고: 같은 리소스 목록 안이나 여러 항목에 걸쳐 겹치는 와일드카드 사용은 허용되지 않아요. 구성의 일부가 비효율적이 되기 때문이에요.
resources목록의 처리 순서와 우선순위는 구성에 나열된 순서에 의해 결정돼요.
리소스를 다루는 와일드카드가 있는데 특정 종류의 리소스에 대한 저장 시 암호화를 선택 해제하고 싶다면, 면제하려는 리소스의 이름이 있는 별도의 resources 배열 항목을 추가하고 그 뒤에 identity 제공자를 지정하는 providers 배열 항목을 추가해 달성할 수 있어요. 이 항목을 암호화를 지정하는 구성(identity가 아닌 제공자)보다 앞에 나타나도록 목록에 추가해요.
예를 들어 *.*이 활성화돼 있고 Events와 ConfigMaps의 암호화를 선택 해제하려면, resources에 더 이른 새 항목을 추가하고 그 뒤에 identity를 제공자로 하는 providers 배열 항목을 추가해요. 더 구체적인 항목은 와일드카드 항목보다 앞에 와야 해요.
새 항목은 다음과 비슷할 거예요.
...
- resources:
- configmaps. # 코어 API 그룹에서 구체적으로,
# 뒤의 "." 때문
- events
providers:
- identity: {}
# 그리고 resources의 다른 항목들
면제 항목이 resources 배열에서 와일드카드 *.* 항목보다 앞에 나열되어 우선순위를 갖도록 하세요.
EncryptionConfiguration 구조체에 대한 더 자세한 정보는 암호화 구성 API를 참고하세요.
주의: 어떤 리소스가 (키가 변경되어) 암호화 구성으로 읽을 수 없다면, 구성이 동작하도록 복원할 수 없다면 유일한 해결책은 그 항목을 기본 etcd에서 직접 삭제하는 것뿐이에요. 그 리소스를 읽으려는 쿠버네티스 API 호출은 그 항목이 삭제되거나 유효한 복호화 키가 제공될 때까지 실패할 거예요.
사용 가능한 제공자 (Available providers)
클러스터의 쿠버네티스 API 데이터에 대한 저장 시 암호화를 구성하기 전에 사용할 제공자를 선택해야 해요. 다음 표는 각 사용 가능한 제공자를 설명해요.
| 이름 | 암호화 | 강도 | 속도 | 키 길이 |
|---|---|---|---|---|
| identity | 없음 | N/A | N/A | N/A |
| 리소스가 암호화 없이 그대로 기록된다. 첫 번째 제공자로 설정되면 새 값이 쓰일 때 리소스가 복호화된다. 기존 암호화 리소스는 자동으로 평문 데이터로 덮어쓰이지 않는다. identity 제공자는 달리 지정하지 않으면 기본값이다. | ||||
| aescbc | PKCS#7 패딩이 있는 AES-CBC | 약함 | 빠름 | 16, 24, 32바이트 |
| CBC가 패딩 오라클 공격에 취약하므로 권장되지 않는다. 키 자료는 컨트롤 플레인 호스트에서 접근 가능하다. | ||||
| aesgcm | 무작위 nonce가 있는 AES-GCM | 200,000번의 쓰기마다 회전해야 함 | 가장 빠름 | 16, 24, 32바이트 |
| 자동 키 회전 방식이 구현된 경우를 제외하고는 사용이 권장되지 않는다. 키 자료는 컨트롤 플레인 호스트에서 접근 가능하다. | ||||
| kms v1 (Kubernetes v1.28부터 폐기) | 리소스당 DEK가 있는 봉투 암호화 방식을 사용 | 가장 강함 | (kms 버전 2에 비해) 느림 | 32바이트 |
| 데이터는 AES-GCM을 사용해 데이터 암호화 키(DEK)로 암호화된다. DEK는 Key Management Service(KMS)의 구성에 따라 키 암호화 키(KEK)로 암호화된다. 각 암호화마다 새 DEK가 생성되는 간단한 키 회전과, 사용자가 제어하는 KEK 회전. KMS V1 제공자 구성 방법 읽기. | ||||
| kms v2 | API 서버당 DEK가 있는 봉투 암호화 방식을 사용 | 가장 강함 | 빠름 | 32바이트 |
| 데이터는 AES-GCM을 사용해 데이터 암호화 키(DEK)로 암호화된다. DEK는 KMS의 구성에 따라 키 암호화 키(KEK)로 암호화된다. 쿠버네티스는 비밀 시드(secret seed)에서 암호화마다 새 DEK를 생성한다. 시드는 KEK가 회전될 때마다 회전된다. 키 관리를 위해 서드파티 도구를 사용한다면 좋은 선택. Kubernetes v1.29부터 안정적으로 사용 가능. KMS V2 제공자 구성 방법 읽기. | ||||
| secretbox | XSalsa20 및 Poly1305 | 강함 | 더 빠름 | 32바이트 |
| 높은 수준의 검토가 필요한 환경에서 허용되지 않을 수 있는 비교적 새로운 암호화 기술을 사용한다. 키 자료는 컨트롤 플레인 호스트에서 접근 가능하다. |
identity 제공자는 달리 지정하지 않으면 기본값이에요. identity 제공자는 저장된 데이터를 암호화하지 않으며 추가 기밀성 보호를 제공하지 않아요.
키 저장 (Key storage)
로컬 키 저장 (Local key storage) — 로컬 스토리지에 암호화 키를 저장하는 것은 컨트롤 플레인 호스트에서 키가 손상될 위험이 있어 etcd 데이터가 암호화되어 있어도 키 탈취 공격자에게 데이터를 노출시킬 수 있어요. KMS v2 제공자를 사용할 때는 --encryption-provider-config 파일 자체에 표준 base64 인코딩으로 저장됩니다. KMS v1 또는 KMS v2를 사용할 때, EncryptionConfiguration에 kms 키와 관련 name, endpoint, cachesize(선택)를 지정해 구성합니다. KMS 제공자 구성과 서드파티 KMS 플러그인 설정 방법은 관련 문서를 참고하세요.
다음 단계 (What's next)
- KMS v2 제공자 구성 방법 읽기
- KMS v1 제공자 구성 방법 읽기
- 모든 Secret이 암호화됐는지 확인하기 (복호화 관련 작업)