본문 바로가기
DevOps/Kubernetes

Encryption at Rest (쿠버네티스 데이터 암호화)

by 비어원 2023. 8. 27.
728x90

쿠버네티스에서 etcd에 저장되는 모든 API 리소스들은 암호화 기능을 지원한다. 기본적으로는 etcd로 저장될 때는 평문으로 저장되지만 특정 리소스에 대해서 암호화하여 저장하도록 설정할 수 있다.

 

  • 암호화 하지 않았을 때는 다음과 같이 etcd에 평문으로 저장된다.
# middleware 네임스페이스의 mysql 시크릿 조회
$ sudo ETCDCTL_API=3 etcdctl --endpoints=https://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/middleware/mysql

/registry/secrets/middleware/mysql
k8s


v1Secret�
�
mysql
middleware"*$1128b4df-c4b6-4fe6-96e0-770095c75ab52����
kubectl-createUpdatev��FieldsV1:R
P{"f:data":{".":{},"f:MYSQL_PASSWORD":{},"f:MYSQL_ROOT_PASSWORD":{}},"f:type":{}}B
MYSQL_PASSWORD
beerone123
          MYSQL_ROOT_PASSWORDrootOpaque"

 

EncryptionConfiguration

kube-apiserver에서 etcd에서 API 리소스의 데이터를 암호화하는 방식을 정할 수 있다. 암호화 방식을 정의하기 위해서는 EncryptionConfiguration 이라는 API 리소스를 yaml파일 형식으로 정의해야 한다.

이 파일은 쿠버네티스 컨트롤플레인 노드에 저장하자.

 

$ sudo mkdir -p /etc/kubernetes/apiserver
$ sudo vi /etc/kubernetes/apiserver/EncryptionConfiguration.yaml

 

일단 쿠버네티스 리소스 중 가장 민감한 정보를 담고있는 Secret을 암호화 하기 위해 EncryptionConfiguration을 설정하자.

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - secretbox:
          keys:
            - name: key1
              secret: ODIyZmVhZTc4OTI1ZTc1YjczNDMxYTk5MWY2NmEyY2Q= # 32 byte의 String을 base64 인코딩한 것
      - identity: {}
  - resources:
      - "*.*"
    providers:
      - identity: {}

 

각 필드에 대해서 간단히 설명하자면 다음과 같다.

  • resources는 하나의 암호화 알고리즘을 적용할 하나 이상의 쿠버네티스 리소스를 배열 형태로 나타낼 수 있다. 예를 들면, 어떤 리소스들은 AES-CBC 방식으로 암호화를 하고, 어떤 리소스는 AES-GCM 방식으로 암호화를 할 수 있도록 구성할 수 있다.
  • resources.resources에는 같은 암호화 방식을 사용하는 여러 쿠버네티스 리소스를 배열 형태로 나타낸다.
    • Kubernetes 1.27 버전 이상이라면 와일드 카드와 그룹을 사용할 수 있는데, *.<group>을 사용하여 특정 그룹에 있는 모든 리소스를 암호화 대상으로 나타낼 수도 있고, *.*를 사용하여 모든 리소스들에 대한 기본 암호화 방식을 정의할 수도 있다.
  • resources.providers에는 여러 암호화 프로바이더를 정의할 수 있는데, 첫 번째 요소의 프로바이더만 암호화 방식으로 채택된다.
  • resources 에서 정의한 암호화 방식이 2개 이상일 때, 암호화 방식이 여러개인 리소스가 있다면, 위에서 정의한 암호화 방식이 우선순위를 가진다.
  • provider.identity는 암호화 방식을 사용하지 않는 것으로 선언하기 위한 프로바이더이다. 이 프로바이더는 기존에 사용 중인 프로바이더를 해제하거나, 와일드카드를 사용하여 기본 암호화 방식을 채택할 때 예외적으로 암호화 하지 않는 리소스를 정의하기 위해 사용한다.

 

여기서 설정한 대로라면, Secret에 대해서만 secretbox 암호화 방식으로 암호화 한다. 프로바이더 종류와 key에 대해서는 나중에 살펴보도록 하자. 일단 secretbox의 시크릿 키는 32 byte의 String을 base64 인코딩한 것이어야 한다. 리눅스에서는 다음 명령어로 랜덤 32바이트 키를 base64로 인코딩한 것을 얻을 수 있다.

$ head -c 32 /dev/urandom | base64

 

암호화 적용

일단 구성을 완료했다면 kube-apiserver.yaml 파일에 --encryption-provider-config args를 추가하여 암호화 방식을 적용하도록 하자.

$ sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
  containers:
  - command:
    - kube-apiserver
    - ...
    - --encryption-provider-config=/etc/kubernetes/apiserver/EncryptionConfiguration.yaml # 이부분 추가
    ...

 

kube-apiserver.yaml 파일이 변경되면 apiserver 파드가 자동으로 뜰 것이다.

일단 시크릿을 암호화하도록 설정은 완료되었다. 하지만 암호화 방식을 적용하더라도 자동으로 시크릿이 암호화되지는 않기 때문에 시크릿을 수동으로 다시 etcd에 넣어줘야 한다. 아래 명령어를 통해 모든 네임스페이스의 시크릿 정보를 다시 넣어줄 수 있다.

$ kubectl get secrets --all-namespaces -o json | kubectl replace -f -

 

암호화 확인

이렇게 암호화를 적용한 후 실제로 etcd에 저장될 때 암호화되었는지 확인해보자.

sudo ETCDCTL_API=3 etcdctl --endpoints=https://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/middleware/mysql

/registry/secrets/middleware/mysql
k8s:enc:secretbox:v1:key1:��"w�?��I����K���������̔�L�������a"��5J�9��gY^N�z
lZ���2fСQ�B�}����t��k�ev�/;K���W"�N�d���0c�Deu
��+$_�Y������
%����c-�aIt����cO����C<ʕ�    y3��S��<M��d�H�5�bm�RR�$-�r��۾1�!�æ�����Тq}��IZJ��x0�D�S��A7U�5�v�����=(��2�� !���E$��ʊ���
ԃ��1�����jQtV���}�m�R�k��ѐ�?Օv�r�����^"��

알아보지 못하는 문자열이 출력되었기 때문에 일단 평문으로 저장된 것은 확실하다. 대신 시크릿을 yaml로 조회할 때 Base64로 보여지는 것은 변하지 않는다. 이 방식은 etcd로 저장되는 방식을 암호화하기 때문이다.

 

프로바이더 종류

공식문서에 따르면 프로바이더 종류는 이정도가 있는 것 같다.

 

프로바이더 이름 암호화 방식 암호화 강도 암호화 속도 Key 길이 설명
identity X N/A N/A N/A 암호화를 하지 않을 때 사용된다. identity가 첫 번째 프로바이더로 설정되면 새 값이 기록될 떄 암호화된 리소스가 복호화된다.
aescbc AES-CBC Weak Fast 32 byte CBC는 패딩 오라클 공격에 취약하기 때문에 추천하지 않는다.
aesgcn AES-GCM 200,000번 읽을 때 마다 교체 필요 Fastest 16, 24, 32 byte 자동 키 회전 방식이 구현된 경우를 제외하고는 사용하지 않는 것이 좋다.
kms v1 DEK Strongest Slow 32 byte AES-GCM을 사용하여 데이터가 암호화된다. 이 방식을 채택하려면 별도의 KMS plugin gRPC server가 필요하다.
kms v2 DEK Strongest Fast 32-bytes AES-GCM을 사용하여 데이터가 암호화된다. 이 방식을 채택하려면 별도의 KMS plugin gRPC server가 필요하다.
secretbox (1.27 이상) XSalsa20 and Poly1305 Strong Faster 32-byte 높은 수준의 보안이 필요한 환경에서는 권장하지 않는 것 같고, 비교적 새로운 암호화 기술을 사용한다.

 

secretbox가 비교적 최근에 나왔고 암호화 강도가 높으며 속도도 빠르다고 한다. 그리고 kms는 별도의 암호화 서버가 필요한 것 같은데 자세히는 잘 모르겠다. 고수준의 보안이 필요하다면 kms, 그렇지 않다면 secretbox를 사용하면 될 것 같다. 그리고 여기서 key의 길이는 key를 생성할 때 꼭 지켜줘야 한다.

 

참고 문서

728x90

'DevOps > Kubernetes' 카테고리의 다른 글

Helm Chart 만들어보기  (0) 2023.09.25
Helm Chart 소개  (0) 2023.09.17
[11] 쿠버네티스 ConfigMap & Secret  (0) 2023.08.27
[2] 쿠버네티스 클러스터 설치  (0) 2023.02.25
[10] 쿠버네티스 Ingress  (0) 2022.07.21

댓글