클라우드 네이티브 애플리케이션 부트캠프 알림

티스토리 뷰

혹시 시크릿이 쿠버네티스 저장소인 etcd에 저장되지 않는다고 생각하시는 분이 계신가요 ?

아닙니다. 시크릿 데이터도 etcd에 저장됩니다.

그것도 무려 평문으로 저장됩니다. 하긴 base64로 인코딩되어 저장되도 쉽게 디코딩이 되니 그게 그거겠지만 말입니다. 

쿠버네티스에서는 etcd에 저장되는 시크릿 데이터를 암호화 하는 방법을 제공 합니다. 

etcd를 암호화 하는 이유는 컨트롤 플레인 노드의 루트 권한을 가진 사람은 etcd를 접근하여 시크릿 데이터를 볼 수 있기 때문입니다. 

https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

 

Encrypting Secret Data at Rest

This page shows how to enable and configure encryption of secret data at rest. Before you begin You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. It is recommended to run this tuto

kubernetes.io

 위 가이드에는 시크릿 데이터 암호화, 암호화 키 교체, 암호화 해제에 대한 내용이 있습니다. 

그러나 암호화 키 교체는 가이드대로 해도 안되더군요. 새로운 키로 바꾸면 쿠버네티스가 시작되지 않았습니다. 

그래서 암호화 키 교체는 암호화를 해제하고 다시 새로운 키로 암호화를 하는 방법을 쓸 수 밖에 없을듯 합니다. 

먼저 etcd에 평문으로 저장되는 것을 확인합니다. 

그리고 암호화 설정을 하고 다시 etcd를 확인하여 암호화 되었는지를 봅니다. 

마지막으로 새로운 암호화 키 교체를 암호화 취소와 다시 암호화 설정을 하는 순서로 진행 합니다. 

경고
시크릿 데이터 설정, 키 교체, 해제 작업을 하는 중에 쿠버네티스 클러스터가 완전히 손상될 수 있습니다.
운영중인 클러스터에서 한다면 반드시 etcd 백업과 리스토어를 충분히 테스트 하고 하십시오. 
제 글을 따라하다 클러스터가 손상되는 것을 책임지지 못함을 이해하여 주시기 바랍니다.  

※ 테스트 용 클러스터에서 하신다면 클러스터 손상시 이 글 맨 마지막에 있는 쿠버네티스 클러스터 재설치를 참고하여
다시 설치하는게 제일 쉽고 빠릅니다. 

etcd에 저장되는 시크릿 데이터를 암호화 한다고 해도 시크릿 데이터를 완벽히 보호할 수는 없습니다.
실무에서는 시크릿 관리를 위한 다른 제품들을 사용하는게 보통입니다. 

대표적인 제품에는 아래와 같은 것이 있습니다. 
- 클라우드 벤더 솔루션: AWS Secret Manager, Azure Key Vault , Google Cloud Platform KMS
- 오픈소스: Hashicorp Vault, CyberArk(Conjur 내장), Confidant

또한 아래 글에는 유명하진 않지만 흥미로운 오픈소스들이 소개되어 있습니다 

https://learnk8s.io/kubernetes-secrets-in-git

 

사전 준비

1. etcdctl 설치

etcd의 CLI인 etcdctl을 컨트롤플레인 노드에 설치 합니다. 

아래 GitHub 레포지토리의 가이드를 따라 하시면 됩니다. 

https://github.com/etcd-io/etcd/releases

위 가이드대로 설치한 후 아래와 같이 etcdctl을 /usr/bin에도 설치 하십시오. 

# install /tmp/etcd-download-test/etcdctl /usr/bin/etcdctl

 

2. 테스트 시크릿 오브젝트 생성 

kubectl create secret generic secret1 -n default --from-literal=mykey=mydata

 

etcd에서 시크릿 데이터 평문 확인

아래 명령을 수행하여 'secret1'의 데이터가 평문으로 etcd에 저장되어 있는걸 확인 합니다. 

[root@master pki]# etcdctl --endpoints localhost:2379 \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
get /registry/secrets/default/secret1 | hexdump -C

오른쪽 아래쯤 보면 'mykey'의 값이 'mydata'인것을 볼 수 있습니다.  

00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
00000020  31 0a 6b 38 73 00 0a 0c  0a 02 76 31 12 06 53 65  |1.k8s.....v1..Se|
00000030  63 72 65 74 12 cd 01 0a  b1 01 0a 07 73 65 63 72  |cret........secr|
00000040  65 74 31 12 00 1a 07 64  65 66 61 75 6c 74 22 00  |et1....default".|
00000050  2a 24 39 38 30 30 34 65  38 61 2d 62 64 31 37 2d  |*$98004e8a-bd17-|
00000060  34 62 62 65 2d 39 32 37  35 2d 39 62 32 63 66 32  |4bbe-9275-9b2cf2|
00000070  33 62 35 66 32 62 32 00  38 00 42 08 08 fd a1 9a  |3b5f2b2.8.B.....|
00000080  8f 06 10 00 7a 00 8a 01  62 0a 0e 6b 75 62 65 63  |....z...b..kubec|
00000090  74 6c 2d 63 72 65 61 74  65 12 06 55 70 64 61 74  |tl-create..Updat|
000000a0  65 1a 02 76 31 22 08 08  fd a1 9a 8f 06 10 00 32  |e..v1".........2|
000000b0  08 46 69 65 6c 64 73 56  31 3a 2e 0a 2c 7b 22 66  |.FieldsV1:..,{"f|
000000c0  3a 64 61 74 61 22 3a 7b  22 2e 22 3a 7b 7d 2c 22  |:data":{".":{},"|
000000d0  66 3a 6d 79 6b 65 79 22  3a 7b 7d 7d 2c 22 66 3a  |f:mykey":{}},"f:|
000000e0  74 79 70 65 22 3a 7b 7d  7d 42 00 12 0f 0a 05 6d  |type":{}}B.....m|
000000f0  79 6b 65 79 12 06 6d 79  64 61 74 61 1a 06 4f 70  |ykey..mydata..Op|
00000100  61 71 75 65 1a 00 22 00  0a                       |aque.."..|
00000109

 

암호화 설정 및 시크릿 데이터 암호화 저장 확인

암호화 설정은 'EncryptionConfiguration'라는 쿠버네티스 리소스를 이용 합니다. 

1. EncryptionConfiguration 정의 파일 작성

아무 디렉토리에 작성해도 되지만 쿠버네티스 인증서 파일들이 있는 '/etc/kubernetes/pki'디렉토리에 만듭니다. 

이 디렉토리에 secrets.yaml파일로 만드십시오. 파일명은 바꿔도 상관 없습니다. 

아래 예제를 참고하여 작성 하십시오. 

# vim /etc/kubernetes/pki/secrets.yaml

kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
   - resources:
     - secrets
     providers:
     - secretbox:
         keys:
         - name: key1
           secret: j2LrGps7ZZoQf7bvyDTmVQ84YMCzGka/7MWIF2RbxMU=
     - identity: {}

'resources.providers'를 'secretbox'로 지정했습니다. 위 가이드에 나와 있는 provider 중 제일 권고하는 provider이기 때문에 이를 이용했습니다. 

'resources.providers.keys.name'은 반드시 'key1'으로 하셔야 합니다. 

'key1'의 secret값은 아래 명령으로 만드시면 됩니다. 32byte의 랜덤 암호를 만들고 base64로 인코딩한 결과를 제공 합니다. 

'secretbox' provider는 32byte 암호화만 지원 합니다. 

head -c 32 /dev/urandom | base64

 

2. API Server 설정 파일에 추가

API Server 설정 파일은 '/etc/kubernetes/manifest/kube-apiserver.yaml'에 있습니다. 

쿠버네티스 컨트롤 플레인의 'kube-apiserver-master'파드는 이 값을 읽어 실행 됩니다. 

이 파일을 열고 아래와 같이 '- --encryption-provider-config=/etc/kubernetes/pki/secrets.yaml' 파라미터를 추가 합니다. 

위치는 'spec.containers.command'하위 아무곳이나 상관 없습니다. 

spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=169.56.70.197
    - --encryption-provider-config=/etc/kubernetes/pki/secrets.yaml
    - --allow-privileged=true
    ...

 

3. API Server 재시작 

'kube-apiserver.yaml'을 저장하고 닫으면 자동으로 API Server가 재시작됩니다. 

컨트롤플레인의 모든 파드들이 재시작 됩니다. 

아래 명령으로 모든 파드가 정상적으로 실행될때까지 기다립니다. 

watch kubectl get po -A

 

4. 기존 시크릿 데이터 암호화하여 etcd에 저장

'secrets.yaml'에 정의한 암호화 키로 기존의 모든 시크릿 오브젝트들의 데이터를 암호화하여 etcd에 저장해야 합니다. 

아래 명령을 수행 하시면 됩니다.  기존 시크릿이 많으면 네임 스페이스별로 나누어 작업 하십시오. 

kubectl get secrets -A -o json | kubectl replace -f -

 

5. 시크릿 데이터 암호화 저장 확인 

etcdctl 명령으로 'secret1'의 데이터를 다시 확인해 봅니다. 

[root@master pki]# etcdctl --endpoints localhost:2379 \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
get /registry/secrets/default/secret1 | hexdump -C

아래와 같이 이제는 완전히 암호화 되어 어떤 값인지 알 수가 없게 됩니다. 

00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
00000020  31 0a 6b 38 73 3a 65 6e  63 3a 73 65 63 72 65 74  |1.k8s:enc:secret|
00000030  62 6f 78 3a 76 31 3a 6b  65 79 31 3a 99 7b 00 83  |box:v1:key1:.{..|
00000040  6a 16 af 2c 04 20 1f d9  db 72 57 f9 be 21 d1 49  |j..,. ...rW..!.I|
00000050  3a e7 77 f7 8f af b5 c2  ed ad f2 16 fc 47 3e 7b  |:.w..........G>{|
00000060  e0 84 3d 36 2b be 65 69  8e dd 0b 9d 96 a7 85 20  |..=6+.ei....... |
00000070  b2 cb 6a a5 ee 19 7d 4e  bc 34 df 91 7f c6 39 32  |..j...}N.4....92|
00000080  33 9c 17 06 dd 37 ed d0  02 7b 52 aa 50 60 c4 1f  |3....7...{R.P`..|
00000090  9f a0 4f a7 c8 cd f7 38  a2 f3 57 32 14 29 8e 25  |..O....8..W2.).%|
000000a0  c9 7f e9 83 57 26 fa 30  b9 57 fa f0 13 a2 c3 15  |....W&.0.W......|
000000b0  a5 66 98 a4 53 b3 16 de  b0 85 bb ad 92 50 40 50  |.f..S........P@P|
000000c0  6f a9 7d 8a c9 4a 9b 0e  c1 f7 47 5d 65 a7 03 09  |o.}..J....G]e...|
000000d0  81 c2 63 4d ad 95 62 51  b0 62 8b 45 c4 c7 5a 7c  |..cM..bQ.b.E..Z||
000000e0  45 bb 68 6d e8 3d 5b 59  28 f8 2d 37 e2 bd cb da  |E.hm.=[Y(.-7....|
000000f0  b9 32 f4 37 35 3e 68 86  ef 33 d6 17 3a 4c af f8  |.2.75>h..3..:L..|
00000100  de 6d 61 2e 67 7e 43 33  4d 15 ab a1 05 4d c1 af  |.ma.g~C3M....M..|
00000110  76 00 78 20 1a f7 fb ea  23 70 df be 89 14 75 d9  |v.x ....#p....u.|
00000120  76 88 03 60 38 16 3e 75  fe 6b b9 0f 75 d0 36 f1  |v..`8.>u.k..u.6.|
00000130  57 e2 52 be a3 80 be 0e  5b 64 57 dc 78 8d ac 6f  |W.R.....[dW.x..o|
00000140  74 e2 fd 6c 33 9a e6 30  34 47 0a                 |t..l3..04G.|
0000014b

 

암호화 키 교체

암호화 키는 정기적으로 교체하는 것이 보안을 위해 필요 합니다.  이 키가 탈취되면 시크릿 데이터를 모두 볼 수 있게 되니까요.

앞에도 말했지만 쿠버네티스 가이드 문서대로 하면 클러스터 시작이 안되기 때문에 암호화를 해제하고 신규 키로 바꾸는 순서로 작업 합니다. 

1. 암호화 해제 

1) secrets.yaml 파일 수정

'secrets.yaml' 파일을 열고 아래와 같이 'identity: {}'를 'providers' 바로 밑으로 이동 하십시오. 

# vim /etc/kubernetes/pki/secrets.yaml

kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
   - resources:
     - secrets
     providers:
     - identity: {}
     - secretbox:
         keys:
         - name: key1
           secret: j2LrGps7ZZoQf7bvyDTmVQ84YMCzGka/7MWIF2RbxMU=

 

 

2) API Server 재시작

'kube-apiserver.yaml'파일을 열고 바로 저장 하십시오.  

아무것도 수정 안해도 자동으로 API Server가 다시 시작 됩니다. 

모든 파드가 실행될때까지 기다립니다. 

 

3) 기존 시크릿 데이터 암호화 해제하여 저장

아래 명령으로 기존 시크릿 데이터의 암호화를 해제하여 다시 etcd에 저장 합니다. 

kubectl get secrets -A -o json | kubectl replace -f -

 

2. 신규 키로 교체 

이제 새로운 암호화 키를 발급하여 교체 합니다. 

1) 새 암호화 키 발급

head -c 32 /dev/urandom | base64

 

2) 'secrets.yaml'파일의 암호화 키 교체

아래와 같이 'secret'값을 위에서 만든 암호화 키로 교체 합니다. 

주의할것은 '- identity: {}' 항목은 반드시 맨 밑으로 다시 옮겨야 합니다. 

kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
   - resources:
     - secrets
     providers:
     - secretbox:
         keys:
         - name: key1
           secret: NkJ07H1HhesztYWxeV3iWwJRp5vcP0rhwzqFFgn3U/0=
     - identity: {}

 

3) API Server 재시작

'kube-apiserver.yaml'파일을 열고 바로 저장 하십시오.  

역시 아무것도 수정 안해도 자동으로 API Server가 다시 시작 됩니다. 

모든 파드가 실행될때까지 기다립니다. 

 

4) 기존 시크릿 데이터 새로운 키로 암호화하여 저장

아래 명령으로 기존 시크릿 데이터를 새로운 키로 암호화 하여 다시 etcd에 저장 합니다. 

kubectl get secrets -A -o json | kubectl replace -f -

 

암호화 해제

암호화 해제는 이미 암호화 키 교체 시 했습니다. 

아래와 같은 순서로 하시면 됩니다. 

- 'secrets.yaml'에서 '- identity: {}' 항목을 'providers'밑으로 이동

- 'kube-apiserver.yaml' 재저장 하여 API 서버 재시작 

- 모든 파드 실행시까지 기다리기

- 기존 시크릿 데이터 암호화 해제하여 etcd에 저장

 

쿠버네티스 클러스터 재설치

위 작업을 테스트 용 클러스터에서 하신다면 클러스터가 망가졌을때 다시 설치하는게 제일 빠릅니다. 

아래와 같이 빠르게 재설치 할 수 있습니다. 

1. 클러스터 초기화 

컨트롤플레인과 워커노드에서 아래 작업을 수행 하여 클러스터를 초기화 합니다. 

컨트롤플레인에서 먼자 하고 워커노드에서 하십시오. 

# kubeadm reset

위 작업에서 에러 메시지가 나올 텐데 무시하셔도 됩니다. 

kubenetes config 파일을 삭제 합니다. 

rm -f ~/.kube/config

 

2. 컨트롤 플레인과 워커노드 재 설치 

08. 컨트롤 플레인 설치

09. 워커 노드 설치

 

3. 필요 시 추가 작업 

아마 이 작업은 모든 시크렛 데이터 암호화 설정 테스트를 하시고 필요 하실겁니다. 

12. 인그레스 컨트롤러 설치

13. 쿠버네티스 대시보드 설치

 

댓글

클라우드 네이티브 애플리케이션 부트캠프 알림