Inside Kubernetes
k8s는 Secrets라는 리소스를 통해서 애플리케이션에서 필요로 하는 민감 정보들을 저장하고 관리할 수 있는 인터페이스를 제공하고 있습니다. 하지만 k8s의 Secrets는 AWS Secrets Manager와 같은 외부 Secrets 관리 시스템과 연동되어 있지 않기 때문에 외부 Secrets 관리 시스템이 가지고 있는 기능들을 활용하지 못한다는 단점이 있습니다. 예를 들면 AWS Secrets Manager가 가지고 있는 Auto Rolling 기능 같은 게 해당되겠죠.
자세한 사항은 https://www.godaddy.com/engineering/2019/04/16/kubernetes-external-secrets/ 을 참고해 주세요.
k8s External Secrets는 이런 한계를 해결하기 위해 만들어진 시스템입니다. (https://github.com/external-secrets/kubernetes-external-secrets)
이번 글에서는 k8s External Secrets를 AWS Secrets Manager와 함께 사용하는 예제를 통해 External Secrets 활용 방안에 대해 살펴보겠습니다.
k8s External Secrets는 아래와 같이 구성됩니다.
External Secrets Controller가 External Secrets 리소스에 해당하는 키를 AWS Secrets Manager로부터 가져와서 k8s Secrets에 저장해 줍니다. 시스템 구성도만 보면 그렇게 복잡하지 않습니다. 하지만 대부분 k8s 애드온들이 그런 것처럼 External Secrets도 권한을 설정하는 과정이 가장 복잡하고 어렵습니다. 그럼 권한 설정과 관련된 부분을 먼저 살펴보겠습니다.
앞서 시스템 구성도에서 살펴본 것처럼 External Secrets의 핵심은 External Secrets Controller 입니다. 실질적으로 Secrets를 가져오고 업데이트하는 등의 동작을 담당하고 있기 때문에 권한을 할당해야 하는 곳도 바로 External Secrets Controller 입니다. 그럼 어떤 권한들이 필요할까요?
먼저 AWS Secrets Manager에 접근할 수 있는 권한이 필요합니다. External Secrets Controller가 AWS Secrets Manager로부터 Secrets 정보를 가져와야 하기 때문에 AWS Secrets Manager에 접근해서 필요한 작업들을 할 수 있는 권한이 필요합니다.
그리고 이때 필요한 AWS IAM 권한은 아래와 같습니다.
이 권한은 어떻게 할당하면 좋을까요? External Secrets Controller가 올라가는 k8s 노드에게 권한을 줄 수도 있지만 가장 좋은 방법은 역시 IRSA 를 사용하는 것입니다. IAM Role을 ServiceAccount에 할당하고 이 ServiceAccount를 External Secrets Controller에게 할당하는 방법입니다.
다음으로 필요한 권한은 k8s의 Secrets를 살펴보고 업데이트할 수 있는 권한입니다. AWS Secrets Manager로부터 가져온 Secret을 k8s에서 사용할 수 있도록 k8s의 Secrets에 업데이트해주는 권한이죠. 특히 k8s Secrets는 여러 개의 Namespace에 설정될 수 있기 때문에 Namespace를 볼 수도 있어야 하고 Secrets를 업데이트할 수도 있어야 합니다.
이 권한은 어떻게 할당하면 좋을까요? 역시 다양한 방법이 있겠지만 가장 좋은 방법은 ClusterRole을 만들고 해당 권한을 ClusterRoleBinding을 통해 ServiceAccount에 할당하는 방법입니다. 어떤 ClusterRole이 필요한지는 아래에서 더 살펴보겠습니다.
이렇게 IRSA와 ClusterRoleBinding을 통해 ServiceAccount에 필요한 권한을 할당해 줍니다.
어떤 권한을 어떻게 할당해야 할지 살펴봤으니 이제 본격적으로 External Secrets를 설치해 보겠습니다.
External Secrets는 helm 차트를 제공해 줍니다만, helm 을 통해서 설치하기보다는 helm을 통해서 template을 만들고 이렇게 생성된 매니페스트를 살펴보면서 적용하는 것이 어떤 것들이 설치되는지를 이해하는데 도움이 됩니다.
먼저 git clone 명령으로 레포지터리를 복사해 옵니다.
그리고 CRD가 정의된 파일까지 포함해서 template을 생성해 줍니다.
가장 먼저 CRD를 생성해 줍니다.
다음으로 생성된 template들을 살펴보겠습니다. 먼저 deployment.yaml 입니다. RELEASE-NAME은 알맞게 다른 이름으로 바꿔주고 (예제에서는 클러스터의 이름인 apne2-ops-dev로 변경) namespace 값은 kube-system 으로 바꿔 줍니다.
External Secrets Controller를 임의의 namespace에 생성할 수 있겠지만, 클러스터 내에서 모든 namespace에 있는 Secrets에 작업할 수 있는 전역적인 Controller이기 때문에 kube-system에 설치해 보겠습니다.
물론 다른 namespace에 설치해도 됩니다.
다음은 rbac.yaml 파일을 살펴보겠습니다. rbac.yaml 파일은 앞에서 살펴봤던 권한과 관련된 부분이기 때문에 어떻게 보면 가장 중요한 부분이라고 볼 수 있습니다. 여기서 제대로 된 권한 설정이 되지 않는다면 External Secrets가 정상적으로 동작하지 않기 때문입니다. ClusterRole을 보면 앞서 이야기했던 권한들이 설정되어 있는 것을 볼 수 있습니다.
그리고 위에 설정된 ClusterRole을 ServiceAccount에 할당하는 코드도 볼 수 있습니다.
계속해서 service.yaml 파일을 수정해 주고, serviceaccount.yaml 파일을 수정해 줍니다. serviceaccount.yaml 파일에는 중요하게 설정해야 할 것이 하나 더 있습니다. 바로 IRSA 설정입니다.
k8s Secrets를 업데이트하는 권한은 ClusterRole과 ClusterRoleBinding으로 할당받았으니 AWS Secrets Manager의 Secrets를 가져오는 권한을 IRSA를 통해서 할당받아야 합니다.
그리고 IRSA를 어노테이션을 통해 ServiceAccount에 연결해 줍니다.
여기까지 완료되었으면 kubectl apply 명령으로 External Secrets를 설치합니다.
자 이제 AWS Secrets Manager에 저장된 Secret을 잘 가져오는지 테스트해보겠습니다. AWS CLI를 통해 간단하게 Secret을 생성해 줍니다.
그리고 아래와 같이 External Secret을 생성해 줍니다.
여기서 property 항목이 중요한데요, AWS Secrets Manager는 Key/Value 형태로 값을 가지고 있기 때문에 Secret의 특정 Key를 k8s의 Secrets에 저장하기 위해서는 property가 필요합니다. property가 없으면 Key/Value가 나눠지지 않은 Secrets 값 그대로가 들어갑니다.
그리고 kubectl get external secrets를 입력하면 아래와 같이 상태가 확인됩니다.
만약 설정이 잘못되었다면 STATUS에 에러 메시지가 보이게 되니 에러 메시지에 따라 디버깅하면 됩니다. 저는 리전 설정을 잘못해서 아래와 같은 에러 메시지도 봤습니다.
그리고 kubectl get secret 명령을 통해 External Secrets가 AWS Secrets Manager를 통해 Secret 정보를 잘 가지고 와서 k8s Secrets에 업데이트한 것을 확인할 수 있습니다.
위와 같이 External Secrets는 k8s Secrets 이 가지고 있는 한계를 벗어나 조금 더 유연하고 확장성 있게 Secrets를 관리할 수 있도록 도와줍니다. 특히 External Secrets와 환경 변수를 활용하면 애플리케이션에 설정해야 할 여러 정보들을 더 유연하게 관리할 수 있게 됩니다.
먼저 애플리케이션에 설정해야 할 정보들, 예를 들면 DB 접근 정보와 같은 것들을 애플리케이션에서 환경 변수로 설정할 수 있게 합니다. 그리고 이 정보들들 AWS Secrets Manager에 Key/Value 형태로 저장해 놓은 후 External Secrets를 통해 k8s Secrets에 저장합니다. 마지막으로 매니페스트 작성 시 envFrom과 secretRef를 이용해서 이 정보들을 환경 변수로 설정해 줍니다. 이를 통해 애플리케이션 설정 파일을 가볍고 환경과 관계없이 일관되게 가져갈 수 있으며 코드 상에서의 Secret을 분리해서 코드 상의 보안을 높일 수 있습니다. 이번 글이 External Secrets를 고민하고 계신 분들에게 도움이 되었으면 좋겠습니다. 감사합니다.
ps. 항상 그렇지만 혹시라도 잘못된 내용이 있다면 언제든 말씀해 주세요~!
External Secrets의 IRSA를 설정할 때 AWS Secrets Manager에 저장된 Secret을 복호화 하기 위해 KMS에 대한 접근도 필요 합니다. 따라서 아래와 같은 권한도 추가 해야 정상 동작 합니다.