Devops/Kubernetes

[Kubernetes] Secrets 암호화

dev_ss 2023. 9. 26. 22:13

쿠버네티스 환경에서 주로 yaml 파일의 선언형 자료를 이용하여 어플리케이션 단위를 관리하는데,

그 선언형 자료에서 공통적인 상수 값들은 주로 Configmap을 이용하고,

일반적으로 공개되지 않아야하는 값들은 Secret을 통하여 값을 배정하게 된다.

 

하지만 여기서 큰 문제가 발생한다.

 

쿠버네티스 클러스터에서 기본적으로 Secret 값은 암호화되지 않고 그저 Base64로 인코딩되어 저장된다는 것이다.

 

https://kubernetes.io/ko/docs/concepts/configuration/secret/

 

시크릿(Secret)

시크릿은 암호, 토큰 또는 키와 같은 소량의 중요한 데이터를 포함하는 오브젝트이다. 이를 사용하지 않으면 중요한 정보가 파드 명세나 컨테이너 이미지에 포함될 수 있다. 시크릿을 사용한다

kubernetes.io

위 쿠버네티스 공식 Docs를 덧붙이자면, 쿠버네티스 시크릿은 기본적으로 암호화되지 않은 상태로 etcd에 저장된다고 나와있다.

 

API 접근 권한이나 etcd에 접근할 수 있는 모든 사용자는 시크릿을 조회/수정이 가능할 수 있다는 것인데, 이는 시크릿 본연의 기능을 이용하지 못한다는 것이다.

 

권한에 대한 부분은 RBAC을 통하여 지정할 수 있을 것이고, 이번 글에서는 쿠버네티스 시크릿을 어떻게 암호화하여 이용할 수 있는지에 대하여 알아볼 것이다.

 


들어가기 앞서, ETCD 설치와 쿠버네티스 환경의 Manifest 파일을 수정하는 과정이 존재하기에,

운영 상황이 아닌 환경에서 충분한 백업이 이루어진 환경에서 진행하는 것이 추천됨


 

1. etcdctl 설치

시크릿은 etcd에 설치되기 때문에 etcd를 조회/백업/복원에 쓰이는 etcdctl을 설치한다.

 

아래 깃허브 배포에서 설치 가이드가 나와있다.

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

 

Releases · etcd-io/etcd

Distributed reliable key-value store for the most critical data of a distributed system - etcd-io/etcd

github.com

 

필자는 Linux(Ubuntu) 환경이기에 아래 명령어를 통해 설치했다.

ETCD_VER=v3.4.27

# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}

rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test

curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz

/tmp/etcd-download-test/etcd --version
/tmp/etcd-download-test/etcdctl version

 

위가 가이드 내용이고 마지막 줄을 참고하자면 /tmp/etcd-download-test/ 경로에 etcd와 etcdctl이 있는 것을 확인할 수 있는데, etcdctl을 편리하게 이용하기 위하여 유저의 바이너리에 추가로 설치해도 되고, bashrc에 추가하는 방식을 이용할 수도 있다.

# 유저 바이너리에 설치
install /tmp/etcd-download-test/etcdctl /usr/bin/etcdctl

========================== 또는 ================================

# Alias 추가
echo "alias etcdctl='/tmp/etcd-download-test/etcdctl'" >> ~/.bashrc

# 적용
source ~/.bashrc

 

 

2. 시크릿 생성 및 확인

 

kubectl create secret generic my-secret --from-literal=my-secret-key=my-secret-value

 

위 생성한 시크릿을 통하여, base 64로 인코딩되어 저장된 etcd를 확인해볼 것이다.

 

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/my-secret | hexdump -C

※ 위 명령어에서 본인이 멀티 클러스터를 이용하고 있다면 /etc/kubernetes/manifests/kube-apiserver.yaml를 조회하여,

localhost:2379 부분을 수정해야 할 것이다.

 

위 명령어를 치게 된다면 아래와 같이 나온다.

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 6d 79 2d 73 65 63  |s/default/my-sec|
00000020  72 65 74 0a 6b 38 73 00  0a 0c 0a 02 76 31 12 06  |ret.k8s.....v1..|
00000030  53 65 63 72 65 74 12 e6  01 0a b9 01 0a 09 6d 79  |Secret........my|
00000040  2d 73 65 63 72 65 74 12  00 1a 07 64 65 66 61 75  |-secret....defau|
00000050  6c 74 22 00 2a 24 65 62  34 39 30 37 63 38 2d 32  |lt".*$eb4907c8-2|
00000060  32 34 35 2d 34 30 35 36  2d 61 63 36 66 2d 30 31  |245-4056-ac6f-01|
00000070  34 35 63 64 66 64 65 38  30 38 32 00 38 00 42 08  |45cdfde8082.8.B.|
00000080  08 d6 f3 c6 a8 06 10 00  8a 01 6a 0a 0e 6b 75 62  |..........j..kub|
00000090  65 63 74 6c 2d 63 72 65  61 74 65 12 06 55 70 64  |ectl-create..Upd|
000000a0  61 74 65 1a 02 76 31 22  08 08 d6 f3 c6 a8 06 10  |ate..v1"........|
000000b0  00 32 08 46 69 65 6c 64  73 56 31 3a 36 0a 34 7b  |.2.FieldsV1:6.4{|
000000c0  22 66 3a 64 61 74 61 22  3a 7b 22 2e 22 3a 7b 7d  |"f:data":{".":{}|
000000d0  2c 22 66 3a 6d 79 2d 73  65 63 72 65 74 2d 6b 65  |,"f:my-secret-ke|
000000e0  79 22 3a 7b 7d 7d 2c 22  66 3a 74 79 70 65 22 3a  |y":{}},"f:type":|
000000f0  7b 7d 7d 42 00 12 20 0a  0d 6d 79 2d 73 65 63 72  |{}}B.. ..my-secr|
00000100  65 74 2d 6b 65 79 12 0f  6d 79 2d 73 65 63 72 65  |et-key..my-secre|
00000110  74 2d 76 61 6c 75 65 1a  06 4f 70 61 71 75 65 1a  |t-value..Opaque.|
00000120  00 22 00 0a                                       |."..|
00000124

 

자세히 보면, 우측의 데이터 내에서 my-secret이라는 시크릿 객체에 담겨진 my-secret-key와 my-secret-value가 전부 드러나는 것을 확인할 수 있다.

 

이로써, 현재 시크릿은 보안에 무방비인 상태와 같다고도 볼 수 있는 것이다.

 

 이제 아래 과정을 통하여 시크릿을 암호화 해 볼 것이다.

 

 

 

3. 암호화 Config파일 작성

아래 명령어를 이용하여 32byte의 랜덤한 문자열을 base 64 인코딩하여 출력할 수 있다.

head -c 32 /dev/urandom | base64

 

위 명령어를 토대로 api-server에서 사용될 암호화 config의 yaml 파일을 만들어 줄 것이다.

 

# 위치는 임의로 지정 - pki는 쿠버네티스 인증서가 위치
vi /etc/kubernetes/pki/encryption.yaml

#=================================================#
#################### yaml 내용 ####################
kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
   - resources:
     - secrets
     providers:
     - secretbox:
         keys:
         - name: key1 # name은 변경하면 안되고 key1을 유지해야 함
           secret: # 이전 생성한 32byte 랜덤한 시크릿 입력
     - identity: {}
###################################################

 

 

 

4. kube-apiserver.yaml 수정

3번 과정으로 Secret을 암호화할 yaml을 제작했으니, 이를 쿠버네티스 manifest의 api-server에 등록해주면 설정으로는 끝이다.

 

# kube-apiserver yaml open
vi /etc/kubernetes/manifests/kube-apiserver.yaml

#======================================================#
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.31.186.6:6443
  creationTimestamp: null
  labels:
    component: kube-apiserver
    tier: control-plane
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    ##### 아래 내용 추가 #####
    - --encryption-provider-config=/etc/kubernetes/pki/encryption.yaml
    ##########################

 

위 과정을 통하여 암호화 할 config를 등록한 것이다.

변경된 사항을 저장하면 자동으로 kubelet이 재시작된다.

 

하지만 기존의 시크릿은 생성된 시점의 데이터를 저장하고 있기 때문에 새로 암호화된 시크릿으로 저장해주어야 한다.

 

 

 

5. 기존 시크릿 암호화 및 확인

 

아래 명령어는 모든 네임스페이스의 데이터를 json 형식으로 가져와서 교체하는 명령어로, 등록된 시크릿의 암호화를 한 번에 적용할 수 있는데, 명령어를 조금 수정한다면, 네임스페이스 별로 진행할 수도 있다.

 

# 시크릿 전체 교체
kubectl get secrets -A -o json | kubectl replace -f -

# 시크릿 네임스페이스 별 교체
kubectl get secrets -n [네임스페이스] -o json | kubectl replace -f -

 


위 과정을 통해 시크릿을 전부 교체했다면 2번에서 이용했던 etcdctl를 사용하여 시크릿의 암호화 여부를 확인할 수 있다. 

 

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/my-secret | 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/my-sec|
00000020  31 0a 6b 38 73 3a 65 6e  63 3a 73 65 63 72 65 74  |ret.k8s:enc:secr|
00000030  62 6f 78 3a 76 31 3a 6b  65 79 31 3a 99 7b 00 83  |etbox: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  |..=3+.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.g.E..Z||
000000e0  45 bb 68 6d e8 3d 5b 59  28 f8 2d 37 e2 bd cb da  |E.hm.=[Y(.-3....|
000000f0  b9 32 f4 37 35 3e 68 86  ef 33 d6 17 3a 4c af f8  |.2.75>h..1..: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.a ....#p....u.|
00000120  76 88 03 60 38 16 3e 75  fe 6b b9 0f 75 d0 36 f1  |v..`5.>u.k..u.6.|
00000130  57 e2 52 be a3 80 be 0e  5b 64 57 dc 78 8d ac 6f  |W.Z.....[...x..o|
00000140  74 e2 fd 6c 33 9a e6 30  34 47 0a                 |t..l5..04A.|
0000014b

 

이러한 암호화의 원리는 그저 base64로만 인코딩되어 저장되던 기존의 시크릿은 암호화 config를 등록함으로, 시크릿은 encryption.yaml의 secret에 기반하여 암호화가 되고, 이 데이터가 base64로 인코딩된 후 시크릿으로 저장되는 것이다.

 

6. 결론

위 과정을 통하여 etcd에 저장된 데이터를 암호화할 수 있다는 것을 확인해 볼 수 있었다.

 

하지만 결국 컨트롤플레인에 대한 접근 권한이 있다면 시크릿과 그 암호화 키에 대한 보안 이슈가 발생할 수 있기 때문에, 실무에서는 etcd만 암호화하여 사용하지 않고 외부 솔루션이나 오픈 소스를 이용하여 시크릿을 관리한다.

 

아래는 그 목록이다.

 

  • 클라우드 : AWS Secret Manager(AWS),  Google Cloud Platform KMS(GCP), Azure Key Vault(Azure)
  • 오픈소스 : Vault(Hashicorp), Infisical

 

https://www.vaultproject.io/

 

Vault by HashiCorp

Vault secures, stores, and tightly controls access to tokens, passwords, certificates, API keys, and other secrets critical in modern computing.

www.vaultproject.io

 

https://infisical.com/

 

Infisical is an open-source end-to-end encrypted platform to sync secrets and configs across your team and infrastructure.

Automaticallyidentify and preventsecret leaks to git using Infisical's continuous monitoring and precommit checks – supportsover 140 secret types.

infisical.com

 

반응형