Kubernetes

Secret(시크릿)

jih0ssang 2024. 2. 19. 23:48

컨피그 & 스토리지 API 카테고리

설정/기밀 정보/영구 볼륨 등에 관련된 리소스

- 시크릿

- 컨피그맵

- 영구 볼륨 클레임

 

# 직접 옵션에서 type과 값을 지정하여 시크릿 생성
$ kubectl create secret generic --save-config sample-basic-auth \
--type kubernetes.io/basic-auth \
--from-literal=username=root --from-literal=password=rootpassword

MySQL 데이터베이스에 접속하려면 사용자명이나 패스워드 등의 기밀 정보가 필요하다.

쿠버네티스에서 애플리케이션을 실행할 경우, 이와 같은 기밀 정보를 컨테이너로 전달하는 방법으로는 어떤 것이 있을까?

 

도커 빌드 시 컨테이너 이미지에 추가

이미지를 빌드할 때 애플리케이션 측이나 컨테이너 환경 변수 및 실행 인수 등에 패스워드 등을 추가할 수 있다.

그러나 이미지 자체에 기밀 정보가 포함되어 있다면 도커 레지스트리(도커 이미지 저장소)에 기밀 정보도 업로드하게 되어 보안상 바람직하지 않다. 또, 기밀 정보를 변경하려면 다시 이미지를 빌드해야 하므로 불편하다.

 

파드나 디플로이먼트의 매니페스트를 통해 전달

이 경우도 매니페스트 자체에 기밀 정보가 포함되어 매니페스트 관리가 어려워진다.

 

이런 경우 기밀 정보를 별도 리소스에 정의하고 파드에서 읽어들일 수 있다. 그것이 시크릿 리소스이다.

시크릿을 정의하고 있는 매니페스트는 base64로 인코딩되어 암호화되어 있지 않아 매니페스트를 깃 저장소 등에 업로드하는 것은 불가능하다.

이러한 문제를 해결하기 위해 시크릿이 정의된 매니페스트를 암호화하는 kubesec/SealedSecret/ExternalSecret 같은 오픈 소스 소프트웨어도 있다. 

 

시크릿 분류

종류 개요
Opaque 일반적인 범용 용도
kubernetes.io/tls TLS 인증서용
kubernetes.io/basic-auth 기본 인증용
kubernetes.io/dockerconfigjson 도커 레지스트리 인증 정보용
kubernetes.io/ssh-auth SSH 인증 정보용
kubernetes.io/service-account-token 서비스 어카운트 토큰용
bootstrap.kubernetes.io/token Bootstrap 토큰용

 

type: Opaque

일반 사용자명과 패스워드 같은 인증 정보 등은 스키마리스(스키마가 없는 데이터 구조)

 

type: kubernetes.io/tls, type: kubernetes.io/dockerconfigjson

인그레스 등에서 참조 가능한 TLS용

 

type: kubernetes.io/service-account-token, type: bootstrap.kubernetes.io/token 

수동으로 만들 일은 거의 없지만, 파드에 서비스 어카운트 토큰이나 인증서 마운트용

 

Opaque 이외의 타입에는 스키마가 정해져 있다.

예를 들어 TLS 타입의 경우에는 '인증서(tls.crt)'와 '비밀키(tls.key)' 스키마가 정의되어 있다.

 

kubectl 명령어로 직접 시크릿을 생성할 경우 kubectl create secret generic 명령어에서 --type 옵션을 지정하여 시크릿을 생성한다. 그리고 위와 같이 스키마가 정해져 있는 일부 시크릿에 대해서는 kubectl create secret TYPE 명령어로 생성하는 것으로 스키마 체크를 할 수 있다.

 

Opaque

일반적인 스키마리스 시크릿을 생성하는 경우 타입에 Opaque를 지정한다.생성 방법에는 네 가지 패턴이 있다.

  • kubectl로 파일에서 값을 참조하여 생성(--from-file)
  • kubectl로 envfile에서 값을 참조하여 생성(--from-env-file)
  • kubectl로 직접 값을 전달하여 생성(--from-literal)
  • 매니페스트에서 생성(-f)

시크릿 리소스에서는 하나의 시크릿 안에 여러 key-value 값이 저장된다. 하나의 시크릿당 저장 가능한 데이터 사이즈는 총 1MB이다.

예를 들어 데이터베이스 인증 정보를 생성하는 경우 시크릿명은 db-auth, key는 username, password 두 가지를 지정한다.

 

kubectl로 파일에서 값을 참조하여 생성(--from-file)

kubectl을 사용하여 파일에서 값을 참조함으로써 생성하는 경우에는 --from-file 옵션을 지정한다.

일반적으로 파일명이 그대로 키가 되기 때문에 확장자는 붙이지 않는 것이 좋다.

(username.txt 대신 username)

확장자를 붙일 경우 --from-file=username=username.txt 등과 같이 지정한다.

또한, 파일을 생성할 때 개행 코드가 들어가지 않도록 주의해야 한다.

예를 들어 echo -n 출력 결과를 리다이렉트하여 파일에 쓰는 방법을 사용할 수 있다.

 

# 시크릿에 포함된 값을 파일로 내보내기
$ echo -n "root" > ./username
$ echo -n "rootpassword" > ./password

 

생성한 파일로 시크릿을 생성한다.

# 파일에서 값을 참조하여 시크릿을 생성
$ kubectl create secret generic --save-config sample-db-auth \
--from-file=./username --from-file=./password
secret/sample-db-auth created

 

생성한 시크릿을 확인하려면 kubectl get 명령어로 json이나 yaml 형식으로 추력하여 data부분을 확인한다.

# 시크릿 확인
$ kubectl get secrets sample-db-auth -o json | jq .data
{
    "password": "cm9vdHBhc3N3b3Jk",
    "username": "cm9vdA=="
}

base64로 인코드되어 있으므로 일반 텍스트로 확인하려면 다음과 같이 디코드한다.

 

# 보통 base64로 인코드되어 있다.
$ kubectl get secrets sample-db-auth -o json | jq -r .data.username
cm9vdA==

# 일반 텍스트로 변경하려면 base64로 디코드가 필요하다.
$ kubectl get secrets sample-db-auth -o json | jq -r .data.username | base64 --decode
root

 

kubectl로 envfile에서 값을 참조하여 생성(--from-env-file)

하나의 파일에서 일괄적으로 생성하는 경우에는 envfile로 생성하는 방법도 있다.

도커에서 --env-file 옵션을 사용하여 컨테이너를 기동한 경우에 이 방법을 사용하면 그대로 시크릿을 이관할 수 있다.

아래와 같이, 텍스트 파일을 준비한다.

username=root
password=rootpassword

 

이 파일로 시크릿을 생성할 수 있다.

# envfile로 시크릿을 생성
$ kubectl create secret generic --save-config sample-db-auth \
--from-env-file ./env-secret.txt
secret/sample-db-auth created

 

kubectl로 직접 값을 전달하여 생성(--from-literal)

kubectl을 사용하여 직접 값을 전달함으로써 생성하려면 --from-literal 옵션을 사용하여 생성한다.

이때 지정하는 타입은 일반 텍스트다.

# 직접 옵션으로 값을 지정하여 시크릿을 생성
$ kubectl create secret generic --save-config sample-db-auth \
--from-literal=username=root --from-literal=password=rootpassword
secret/sample-db-auth created

 

매니페스트에서 생성(--f)

매니페스트에서 생성하는 경우 base64로 인코드한 값을 매니페스트에 추가한다.

apiVersion: v1
kind: Secret
metadata:
  name: sample-db-auth
type: Opaque
data:
  username: cm9vdA==   		  # root
  password: cm9vdHBhc3N3b3Jk  # rootpassword

 

시크릿 매니페스트를 쿠버네티스에 등록할 수 없는 경우에는 base64로 제대로 인코드되었는지 확인한다.

 

또 시크릿 매니페스트는 data가 아닌 stringData 필드를 사용하면 일반 텍스트로 작성할 수 있다.

stringData와 data를 모두 설정한 경우, stringData가 우선순위가 높다.

apiVersion: v1
kind: Secret
metadata:
  name: sample-db-auth
type: Opaque
stringData:
  username: root
  password: rootpassword

 

TLS 타입 시크릿

인증서로 사용할 시크릿을 생성하는 경우, type: tls를 지정한다.

인그레스 리소스 등에서 사용하는 것이 일반적이다.

매니페스트로 생성할 수 있찌만 기본적으로 비밀키와 인증서 파일로 생성하는 것을 권장한다.

 

# 자체 서명된 인증서 생성
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ~/tls.key -out ~/tls.crt -subj "/CN=sample1.example.com"

 

kubectl에서 비밀키와 인증서 파일로 생성(--key/--cert)

# TLS시크릿 생성
$ kubectl create secret tls --save-config tls-sample --key ~/tls.key --cert ~/tls.crt
secret/tls-sample created

kubectl을 사용하여 비밀키와 인증서 파일로 시크릿을 생성하는 경우 --key와 --cert 옵션에 각각 파일 경로를 지정한다.

여기서는 앞에서 작성한 자체 서명된 인증서를 사용한다.

 

도커 레지스트리 타입의 시크릿

사용하고 있는 컨테이너 레지스트리가 프라이빗 저장소인 경우, 도커 이미지를 가져오려면 인증이 필요하다.

쿠버네티스는 이 인증 정보를 시크릿으로 정의하여 사용할 수 있다.

 

도커 레지스트리 인증용 시크릿을 생성하는 경우에는 type: docker-registry를 지정한다. 

이 타입의 시크릿은 ~/.docker/config.json 파일 대체용으로사용한다.

 

kubectl 직접 생성

kubectl을 사용하여 시크릿을 직접 생성할 때는 레지스트리 서버와 인증 정보를 인수로 지정한다.

# 도커 레지스트리 인증 정보의 시크릿 생성
$ kubectl create secret docker-registry --save-config sample-registry-auth \
--docker-server=REGISTRY_SERVER \
--docker-username=REGISTRY_USER \
--docker-password=REGISTRY_USER_PASSWORD \
--docker-email=REGISTRY_USER_MAIL
secret/sample-registry-auth created

 

dockercfg 형식의 JSON이 base64로 인코드된 형식으로 되어 있다.

따라서 매니페스트를 직접 작성하는 것은 귀찮다.

 

이미지 다운로드 시 시크릿 사용

인증이 필요한 도커 레지스트리(도커 허브 등)의 프라이빗 저장소에 저장된 이미지를 다운로드 하는 경우,

시크릿을 사전에 생성한 후 파드 정의 spec.imagePullSecrets에 docker-registry 타입의 시크릿을 지정해야 한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-pull-secret
spec:
  containers:
  - name: secret-image-container
    image: REGISTRY_NAME/secret-image:latest
  imagePullSecrets:
  - name: sample-registry-auth

파드에는 여러 컨테이너가 포함된 경우가 있으므로imagePullSecrets은 복수로 설정할 수 있다.

 

기본 인증 타입의 시크릿

kubectl로 직접 값을 전달하여 생성(--from-literal)

kubectl로 직접 값을 전달하여 생성하려면 --from-literal 옵션을 사용한다.

그리고 기본 인증 타입은 --type 옵션에서 지정한다.

또한, 리소스를 생성하지 않고 매니페스트를 출력하는 경우 --dry-run, -o yaml 옵션을 함께 사용한다.

# 직접 옵션에서 type과 값을 지정하여 시크릿 생성
$ kubectl create secret generic --save-config sample-basic-auth \
--type kubernetes.io/basic-auth \
--from-literal=username=root --from-literal=password=rootpassword

 

매니페스트에서 생성(-f)

기본 인증용 시크릿을 생성하는 경우에는 type: kubernetes.io/basic-auth를 지정한다.

또 데이터 스키마로 username과 password를 지정해야 한다.

apiVersion: v1
kind: Secret
metadata:
  name: sample-basic-auth
type: Opaque
data:
  username: cm9vdA==   		  # root
  password: cm9vdHBhc3N3b3Jk  # rootpassword

 

시크릿 사용

시크릿을 컨테이너에서 사용할 경우 다음과 같이 크게 두 가지 패턴으로 나눌 수 있다.

  • 환경 변수로 전달
    • 시크릿의 특정 키만
    • 시크릿의 전체 키
  • 볼륨으로 마운트
    • 시크릿의 특정 키만
    • 시크릿의 전체 키

환경 변수로 전달

환경 변수로 전달할 경우 특정 키만 전달하거나 시크릿 전체를 전달하는 두 가지 방법이 있다.

시크릿의 특정 키를 전달할 경우에는 spec.containers[].env의 valueFrom.secretKeyRef를 사용하여 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-secret-single-env
spec:
  containers:
  - name: secret-container
    image: nginx:1.16
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: sample-db-auth
          key: username

 

env로 하나씩 정의하기 때문에 환경변수명을 지정할 수 있는 것이 특징이다.

 

# sample-secret-single-env 파드의 DB_USERNAME 환경 변수 확인
$ kubectl exec -it sample-secret-single-env -- env | grep DB_USERNAME
DB_USERNAME=root

 

시크릿 전체를 변수로 전달

apiVersion: v1
kind: Pod
metadata:
  name: sample-secret-prefix-env
spec:
  containers:
  - name: secret-container
    image: nginx:1.16
    envFrom:
    - secretRef:
    	name: sample-db-auth
      prefix: DB1_
    - secretRef:
        name: sample-db-auth
      prefix: DB2_

envFrom에서 여러 시크릿을 가져오면 충돌할 가능성이 있으므로 접두사를 붙여 충돌을 방지할 수 있다.

 

 

볼륨으로 마운트

볼륨으로 마운트하는 경우에도 특정 키만 마운트하거나 시크릿 전체를 마운트하는 두 가지 방법이 있다.

시크릿의 특정 키를 마운트하는 경우에도 spec.volumes[]의 secret.items[]를 사용하여 지정한다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-secret-single-volume
spec:
  containers:
  - name: secret-container
    image: nginx:1.16
    volumeMounts:
    - name: config-volume
      mountPath: /config
    volumes:
    - name: config-volume
      secret:
        secretName: sample-db-auth
        items:
        - key: username
          path: username.txt

 

 

 

 

 

 

 

'Kubernetes' 카테고리의 다른 글

영구 볼륨 클레임  (0) 2024.02.20
ConfigMap(컨피그맵)  (0) 2024.02.20
컨피그 & 스토리지 API 카테고리  (0) 2024.02.18
서비스 API 카테고리 정리  (0) 2024.02.18
Ingress(인그레스)  (0) 2024.02.17