Kubernetes 리소스 Secret에 대해 이해하고 실습해보기

Bakumando·2022년 5월 26일
1

Kubernetes

목록 보기
10/17
post-thumbnail

들어가기에 앞서...

  • 본 글은 쿠버네티스 시리즈 중의 하나로, kubernetes 실습을 위한 기본 환경 세팅이 이루어져 있지 않은 분은 시리즈 1편을 확인해주시길 바란다.
  • 쿠버네티스 실습 시리즈는 아래 학습 자료를 참고하고 있다.

0. 블로깅 목적

  • Secret 정의, 종류를 이해한다.
  • kubectl Secret 생성 명령어를 이해하고 실습한다.
  • Secret의 선언적 관리 방법에 대해 이해한다.

1. Secret 정의, 적용 방식을 이해한다.

  • 그림을 보면 알겠지만 ConfigMap과 비슷하게 Volume과 환경변수 방식으로 Pod에 정보를 주입하는 걸 알 수 있다.

1) Secret 정의

  • Password, API key, SSH key 등 보안이 중요한 정보를 컨테이너에 주입해야할 때 사용되는 리소스 이다.
  • ConfigMap과 사용법은 비슷하다. 다만 ConfigMap이 민감하지 않은 설정 정보를 컨테이너에 주입하는 게 목적이라면, Secret은 반대로 민감한 정보를 안전하게 컨테이너에 주입하는 게 목적이다.
  • Secret은 사용 목적에 따라 몇 가지 종류로 나누어 진다.
  • Kubentes는 기본적으로 Secret 값을 etcd에 저장하는데, Base64 인코딩을 한다. 즉, etcd에 접근권한이 있다면 Secret을 읽는 게 어려운 일이 아니다.
    • 따라서 클라우드 서비스 같은 경우엔 암호화를 거칠 수 있도록 추가적인 방법을 제공한다. (가령 EKS를 사용하면 KMS로 encrypt 할 수 있다.)
    • 그럼에도 가장 중요한 건 읽기 권한(사용자 접근 제어 관리)이다. RBAC(Role Based Access Control)을 활용해 Secret 오브젝트에 대한 읽기권한을 누가 가질 수 있게 할지에 대해 설정을 잘 해야 한다.
      • 가령 ConfigMap과 Secret을 구분해서 보관하여 사용자별로 권한을 나눠서 주는 방식이 예시가 될 수 있다.


2) Secret 종류

(1) Opaque (generic)

  • 일반적인 용도의 시크릿이다.
  • ConfigMap과 동일한 목적으로 사용할 수 있다.
  • 민감한 데이터를 컨테이너에 전달하는 목적으로도 사용할 수 있다.

(2) dockerconfigjson

  • 도커 이미지 저장소 인증 정보이다.
  • 쿠버네티스는 컨테이너를 관리해야 하기 때문에, 이미지 저장소에 접근할 수 있어야 한다.
  • 다만 도커 허브나 ECR같은 레지스트리 서비스에는 Public과 Private이 이미지가 있어서, Private 이미지를 가져올 때는 추가적인 인증절차를 밟아야 한다.
  • 그 인증에 필요한 정보를 Secret에 생성을 해두고, Pod를 띄울 때마다 이미지 pull에 필요한 Secret을 사용할 수 있게 해야 한다. dockerconfigjson는 그런 Private 레지스트리에 접근할 때 사용하는 Secret인 것이다.

(3) tls

  • TLS 인증서를 Secret으로 관리할 수 있게 도와준다.
  • TLS 인증서 정보를 Secret에 올려두게 되면, Pod나 Service와 같은 오브젝트에서 TLS 인증서 정보를 가져다가 통신 암호화를 수행하거나 할 수 있다.

(4) service-account-token

  • RBAC, 즉 사용 접근 제어를 해야할 때 사용하는 대표적인 쿠버네티스의 API 리소스인 ServiceAccount와 연관이 있다.
  • ServiceAccount는 Pod에 연결이 되어, 해당 Pod의 권한을 설정해주는 역할을 한다. 그리고 ServiceAccount를 연결시켜주게 되면 ServiceAccount에 대한 인증 정보를 담은 토큰이 Secret으로 자동 생성 된다.

2. kubectl Secret 생성 명령어를 이해하고 실습한다.

1) kubectl Secret 생성 명령어

  • 이전 시리즈 편인 ConfigMap과 명령형 커맨드 사용방식이 거의 유사하다.
  • 명령형 커맨드 사용 이유에 있어서도, Secret도 ConfigMap처럼 선언형으로만 명세하는 게 상당히 번거로운 일이 될 수 있어서다.
  • 명령형 커맨드는 효율적으로 선언형 yml 파일을 만들고 관리할 수 있도록 해준다.

다양한 타입과 방식으로 secret yml 명세 파일을 만들어낼 수 있다.

$ kubectl create secret {secret 종류} {secret 이름} --dry-run -o yaml
$ kubectl create secret {secret 종류} {secret 이름} --from-file {파일명/파일경로} --dry-run -o yaml
$ kubectl create secret {secret 종류} {secret 이름} --from-file {key}={파일명/파일경로} --dry-run -o yaml
$ kubectl create secret {secret 종류} {secret 이름} --from-file {key}={파일명/파일경로} --dry-run -o yaml
$ kubectl create secret {secret 종류} {secret 이름} --from-file {key}={파일명/파일경로} --dry-run -o yaml --from-literal {key}={value}
  • 핵심은 --dry-run -o yaml이다.
    • --dry-run: 가짜로 실행해라. 결과를 클러스터에 바로 반영하는 게 아니라 어떤 결과를 만들어낼지를 일단 확인해보기 위한 것.
    • -o yaml: 출력 결과를 yml 형태로 보려는 것.
  • 위 옵션을 없이 실행하면 그냥 바로 ConfigMap 오브젝트를 생성해버린다. 그래서 일단 위 옵션을 활용하여 어떤 명세가 될지를 살펴보고, 이후에 옵션을 지우고 적용을 하면 더 실수할 일이 적고 효율적일 수 있다.
  • 아니면 출력된 새로운 명세를 기존 yml에 덮어씌워주는 것도 좋은 방법일 것이다.
  • ConfigMap과의 차이점: ConfigMap은 명령어 커맨드 전달 시에 종류(타입)를 명시할 필요가 없었는데, Secret은 종류를 추가로 기입해줘야 한다는 점이다.


1) Secret 첫번째 실습

  • 첫 실습은 envFrom을 활용하는 방식이다.
  • yml 파일을 3가지 준비한다.

configmap.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  MYSQL_DATABASE: kubernetes
  • ConfigMap 시리즈 에선 Password를 key-value로 yml 파일에 명세했었는데, 여기엔 넣지 않았다.
  • 사실 ConfigMap에 민감한 정보를 담으면 안 된다. 이는 Secert을 이용해야 한다.

secret.yml

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
stringData:
  MYSQL_ROOT_PASSWORD: bakumando
# data:
  # MYSQL_ROOT_PASSWORD: YmFrdW1hbmRv
  • 이렇게 secret.yml에 출력된 명세를 붙여 넣는다. stringData 아래에 Password가 key-value로 존재한다.
  • stringData 아래에 환경변수를 세팅해줘야 한다. 만약 ConfigMap처럼 사용하면 base64 인코딩 에러가 발생한다.
  • value를 base64로 미리 인코딩해주면 주석처리된 data 아래처럼 삽입해도 에러가 발생하지 않는다.
  • 참고
    • 이렇게 명령형 커맨드로도 yml 명세를 만들 수는 있다.

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      name: mysql
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mariadb:10.7
        envFrom:
        - configMapRef:
            name: mysql-config
        - secretRef:
            name: mysql-secret
  • envFromconfigMapRefsecretRef라는 2가지 옵션을 준다.
  • 여기에선 순서가 중요하다. 뒤에 나오는 게 앞에 것을 덮어 씌울 수 있다. 그래서 각 옵션에 중복되는 key가 있다면 잘 감안하여 배치를 해줘야 한다.

  • kubectl apply -f configmap.yml
  • kubectl apply -f secret.yml
  • kubectl apply -f deployment.yml
  • watch kubectl get pod
    • 준비한 yml 파일을 전부 apply해준다. 단, deployment를 마지막에 적용해줘야 한다. 그래야 미리 생성된 configmap, secret을 envFrom을 통해 참조를 할 수 있기 때문이다.
    • 그리고 watch 모드도 활성화시켜주자. 배포된 pod가 보인다.

  • kubectl exec -it deploy/mysql bash
  • echo $MYSQL_ROOT_PASSWORD
  • echo $MYSQL_DATABASE
    • 실행 중인 mysql deployment의 Pod에 접속하여 환경변수를 확인해본다. configmap.yml과 secret.yml의 환경변수가 전부 잘 적용되었음을 알 수 있다.

  • mysql -u root -p
  • show databases;
    • 비번을 활용해 mysql 환경에 접속하여, kubernetes db가 생성된 걸 확인할 수 있다.


2) Secret 두번째 실습

  • 2번 실습은 envValueFrom을 활용하는 방식이다.
  • yml 파일을 3가지 준비한다.
  • configmap.yml, secret.yml은 1번 실습과 동일하다.

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      name: mysql
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mariadb:10.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: MTSQL_ROOT_PASSWORD
  • envFrom 대신 env를 넣되, 그 아래에 valueFrom으로 secretKey를 참조하게끔 한다.
  • secret.yml에 있는 key 이름이 잘 매칭되게 기재하면 해당 key-value를 환경변수로 적용할 수 있다.

  • kubectl apply -f deployment.yml
    • configmap, secret은 사실 이전에 적용한 상태와 달라질 게 없기 때문에 deployment만 새롭게 적용해주면 되긴 하다.
    • deployment도 created가 아닌 configured이다.
    • watch 터미널을 보면, 이전 Pod가 종료되고 새로운 식별자의 pod가 생성된 걸 볼 수 있다.

  • kubectl exec -it deploy/mysql bash
  • echo $MYSQL_ROOT_PASSWORD
  • echo $MYSQL_DATABASE
    • 실행 중인 mysql deployment의 Pod에 접속하여 환경변수를 확인해본다. secret.yml의 환경변수만 적용되고, configmap.yml은 참조하지 않았기 때문에 아무것도 출력되지 않음을 확인할 수 있다.

  • mysql -u root -p
  • show databases;
    • 비번을 활용해 mysql 환경에 접속하였다.
    • confimap으로부터 MYSQL_DATABASE를 key로 참조하지는 않았기 때문에, kubernetes db도 생성되지 않았다.


3) Secret 세번째 실습

  • 3번 실습은 volume을 활용하는 방식이다.
  • yml 파일을 3가지 준비한다.
  • 3번 실습도 configmap.yml, secret.yml은 1번 실습과 동일하다.

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      name: mysql
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mariadb:10.7
        envFrom:
        - configMapRef:
            name: mysql-config
        - secretRef:
            name: mysql-secret
        volumeMounts:
        - mountPath: /tmp/config
          name: mysql-config
        - mountPath: /tmp/secret
          name: mysql-secret
      volumes:
      - name: mysql-config
        configMap:
          name: mysql-config
      - name: mysql-secret
        secret:
          secretName: mysql-secret    
  • env가 아닌 envFrom을 사용하여 configmap과 secret을 전체 참조한다.
  • volumes를 보면, mysql-config라는 이름의 volume은 configmap 데이터를 사용하고, mysql-secret이라는 이름의 volume은 mysql-secret의 데이터를 사용하게끔 세팅되어 있다.
  • volumeMounts는 volumes에서 참조하는 데이터를 마운트 하는 옵션이다. mysql-config과 mysql-secret이라는 이름의 volume을 각각에 지정 경로에 마운트한다는 의미이다.

  • kubectl apply -f deployment.yml
    • deployment만 새롭게 적용해주면 된다.
    • watch 터미널의 Pod 새롭게 바뀐 걸 볼 수 있다.

  • kubectl exec -it deploy/mysql bash
  • cd /tmp
  • ls
  • cat secret/MYSQL_ROOT_PASSWORD
  • cat config/MYSQL_DATABASE
    • 실행 중인 mysql deployment의 Pod에 접속하여 환경변수를 확인해본다. mount 경로에 저장되어 있기 때문에 접근해야 한다. 파일명이 key이고 내용이 value이기 때문에 cat으로 출력하면 확인할 수 있다.

  • mysql -u root -p
  • show databases;
    • mysql 환경에 접속하여, kubernetes db가 확인되었다.

3. Secret의 선언적 관리 방법에 대해 이해한다.

1) Secret의 선언적 관리

  • Secret을 yml 명세에 코드로 담는 선언적 관리 방식의 편의성은 두말할 필요도 없지만, 이를 Git과 같은 버전관리 시스템에서 관리하게 되면 기밀정보가 담겨 있어 부적절하다.
  • 어떻게 하면 보안 관점에서 안전하면서도 Secret의 선언적 관리의 이점을 취할 수 있을지에 대해서 알아볼 필요가 있다.

(1) External Secrets

  • HshiCorp Valut, AWS Secrets Manager 등과 통합하여 문제를 해결하는 방식이다.
  • ExternalSecret 오브젝트를 생성하면 컨트롤러가 프로바이더로부터 기밀 값을 가져와서 Secret 오브젝트를 생성해준다.
  • 클라우드 서비스와 같이 외부 솔루션 도구를 이용한 암호화 방식이다.
  • 참고 링크: https://github.com/external-secrets/external-secrets

(2) Sealed Secrets

  • SealedSecret 오브젝트를 생성하면 쿠버네티스 컨트롤러가 복호화하여 Secret 오브젝트를 생성하는 방식이다.
    • SealedSecret를 사용하게 되면 kubeseal CLI라고 하는 또다른 커맨드 툴을 사용해야 하는데, 이 툴이 컨트롤러와 통신하며 데이터를 암호화하게 되는 것이다.
    • SealedSecret은 클러스터 상에서만 복호화된 Secret 오브젝트가 사용될 수 있게 관리해 준다.
    • 즉, git과 같은 pulbic 공간에는 데이터가 암호된 상태로 g올라가기 때문에 보안을 보장받을 수 있다.
    • 내부적으로 라이브러리만 사용하여 암호화하는 방식이다.
  • 참고 링크: https://github.com/bitnami-labs/sealed-secrets
profile
그렇게 바쿠만도는 개발에 퐁당 빠지고 말았답니다.

1개의 댓글

comment-user-thumbnail
2023년 5월 24일

쉬운 내용과 예제 잘 봤습니다. 내용을 보다 보니 실 사용에서의 의문의 있어 질문 드립니다.
예를 들어 DB와 서버의 네입스페이스가 다를 경우.. 즉 DB네이스페이스에 디비 접속정보가 들어 있는 secrets가 있으면 서버네이스페이스의 서버 설정에서 불러다 사용이 가능 한지요?

답글 달기