/ kubernetes

Kubernetes Secret

https://kubernetes.io/docs/concepts/configuration/secret/
민감한 정보를 보관해두고 Pod에 주입시켜서 사용 할수 있게 해준다.

기본 시크릿

Service Account

생성

--from-file

# Create files needed for rest of example.
$ echo -n "admin" > ./username.txt
$ echo -n "1f2d1e2e67df" > ./password.txt

$ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret "db-user-pass" created

--from-literal

kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11

secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
kubectl create -f ./secret.yaml

조회

kubectl get secrets

사용

Pod에서 Secret을 파일로 사용 케이스

  • 여러 Pods에서 동시에 접근이 가능하다.
  • spec.volumes[].secret.secretName에 추가하 Secret이름을 추가함
  • spec.containers[].volumeMounts[] spec.containers[].volumeMounts[].readOnly = true
    spec.containers[].volumeMounts[].mountPath
  • data맵의 키는 mountPath의 파일이름이 됨
{
 "apiVersion": "v1",
 "kind": "Pod",
  "metadata": {
    "name": "mypod",
    "namespace": "myns"
  },
  "spec": {
    "containers": [{
      "name": "mypod",
      "image": "redis",
      "volumeMounts": [{
        "name": "foo",
        "mountPath": "/etc/foo",
        "readOnly": true
      }]
    }],
    "volumes": [{
      "name": "foo",
      "secret": {
        "secretName": "mysecret"
      }
    }]
  }
}

Secret의 특정 Path만 노출하기

spec.volumes[].secret.items

{
 "apiVersion": "v1",
 "kind": "Pod",
  "metadata": {
    "name": "mypod",
    "namespace": "myns"
  },
  "spec": {
    "containers": [{
      "name": "mypod",
      "image": "redis",
      "volumeMounts": [{
        "name": "foo",
        "mountPath": "/etc/foo",
        "readOnly": true
      }]
    }],
    "volumes": [{
      "name": "foo",
      "secret": {
        "secretName": "mysecret",
        "items": [{
          "key": "username",
          "path": "my-group/my-username"
        }]
      }
    }]
  }
}

Secret 파일 권한설정

  • 아무런 설정이 없으면 0644
  • JSON의 경우 8진수 표기법이 안되기 때문에 10진수로 입력을 함
{
 "apiVersion": "v1",
 "kind": "Pod",
  "metadata": {
    "name": "mypod",
    "namespace": "myns"
  },
  "spec": {
    "containers": [{
      "name": "mypod",
      "image": "redis",
      "volumeMounts": [{
        "name": "foo",
        "mountPath": "/etc/foo"
      }]
    }],
    "volumes": [{
      "name": "foo",
      "secret": {
        "secretName": "mysecret",
        "defaultMode": 256
      }
    }]
  }
}

마운트된 Secret의 볼륨

  • base64 디코딩이 된 파일로 존재
$ ls /etc/foo/
username
password
$ cat /etc/foo/username
admin
$ cat /etc/foo/password
1f2d1e2e67df

마운트된 Secret은 자동 업데이트됨

업데이트 시간 : kubelet sync period + ttl(secret cache in kublet)

Secret 환경변수로 사용

  • 여러 Pods에서 사용가능
  • env[x].valueFrom.secretKeyRef
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
    - name: mycontainer
      image: redis
      env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: SECRET_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
  restartPolicy: Never

Secret 환경변수사용

base64 디코딩이 된 상태로 환경변수로 세팅됨

$ echo $SECRET_USERNAME
admin
$ echo $SECRET_PASSWORD
1f2d1e2e67df

Private Registry Pulling

Private 레지스트리를 관리하면 아래를 참고한다.
https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod

Secret의 자동 마운트 방법

https://kubernetes.io/docs/tasks/run-application/podpreset/

깊게 보기

제한사항

  • Pod에서 사용하기 전에 생성 되어야 함
  • Secret은 같은 namespace에서만 접근 가능
  • Secret당 1MB 크기 제한
  • apiserver와 kubelet memory issue로 제약함
  • 작지만 너무 많은 secret가 있어도 동일한 이슈 발생 할수 있음
  • secret은 kubectrl의 --manifest-url --config 다음과 같은 cli 옵션을 제공하지 않은
  • Pod 명세로만 주입 가능
    • 무조건 REST API 방식으로 제공됨
  • secret 사용값이 optional 해도 Pod에 정의된 secret은 무조건 존재해야함
  • 지정한 secret이 없으면 Pod 기동이 안됨
  • envFrom에서 secretKeyRef 으로 접근 할 경우도 존재해야만 Pod가 시작됨
  • 단 환경 변수 이름 오류(InvalidVariableNames)의 경우는 무시하고 Pod가 시작됨

Secret과 Pod의 라이프타임 작용

  • Pod 생성시 secret을 체크하지 않음
  • Pod가 스케쥴링이 되면 kublet이 secret value를 패치 시도
  • secret value가 없거나 API 딜레이가 걸리면 주기적으로 다시 패치 시도
  • Pod시작이 되지 않으면 event에 기록됨
  • fetch가 완료되면 마운트나 환경 변수로 입력이 되고 컨테이너 시작이 됨

보안특징

보호

  • Secret은 Pod와 독립적으로 생성됨
  • 시스템적으로도 Secret은 가능한 디스크에 기록되지 않게 되어 있음
  • Secret은 Pod가 요청할때 전송되고 디스크에 기록되지 않음
  • 전송된 Secret은 tmpfs에 저장되고 Pod 삭제시 함께 삭제됨
  • Secret은 API Server나 kublet간에 통신 채널인 SSL/TLS을 그대로 이용함

주의사항

  • API server는 Secret Data를 etcd에 plaintext로 저장함
  • admin 사용자만 접근하도록 제한 필요
  • etcd를 더 이상 사용하지 않을때는 꼭 디스크를 완벽하게 지워야함
  • manifest(JSON or YAML)파일로 secret data(base64 encoded)를 관리하면 base64 decoding을 하면 plaintext로 바로 나오기 때문에 더 주의있게 관리가 필요
  • application에서 secret data가 사용될때 로깅이나 전송되지 않게 주의 필요
  • 여러개의 replica와 etcd가 동작할때 비암호화 통신으로 동작함
  • 추가로 설정이 필요
  • 현재는 kubelet을 통해서 누구나 secret data 접근이 가능함
  • 제한 접근 기능을 추가할 예정임
  • 같은 Node에 여러 Pod의 Secret이 존재할 수 있지만 Pod가 요청했던 Secret만 컨테이너에서 접근 가능함, 다른 Pod는 Secret을 액세스 할수 없음
  • 한 Pod에는 여러개의 컨테이너가 있는 경우 컨테이너 끼리는 volumeMounts가 노출됨
  • Pod를 보안 레벨에 따라서 나누는 전략을 사용하는게 안전