Secret
개요
컨피그맵과 거의 비슷하지만, 민감한 데이터를 조금이라도 안전하게 보관할 때 사용하는 오브젝트.
파드에 조금이라도 민감한 데이터를 넣어야 하는 경우에 쓰면 유용하다.
파드와 독립적으로 만들어서 관리되니, 워크로드와는 떨어져 상대적으로 안전하다고 할 수 있다.
또한 민감한 데이터를 휘발적인 영역에서만 관리하기에 탈취로부터 안전성이 향상된다.
주의사항
사실 이건 Etcd에 저장하는 과정이며, 엄밀하게 암호화되지도 않는다.
그래서 사실 여기에 접근할 권한이 있는 모두가 이 데이터를 읽을 수 있다..
다음의 과정을 고려하자.
https://kubernetes.io/docs/concepts/security/secrets-good-practices/
시큐리티에서 더 자세하게 다루겠다
사용처
- ssh키나 비밀번호를 제공하기
- kubelet이 개인 레지스트리의 이미지를 다운받을 때
심지어 클러스터 만들어지는 과정에서 bootstrap도 시크릿이 사용된다.
http 요청을 주고받으면서 hmac[2]을 통해 메시지에 서명을 한다고 쳐보자.
복잡한 로직을 포함하고 있어 원격에서 알려지지 않는 파일을 읽는 것은 공격자에게 개인키를 노출시킬 위험이 있다.
이럴 때 두 컨테이너를 활용할 수 있다.
프론트 컨테이너가 비즈니스 로직을 담당하며, 개인키는 볼 수 없다.
이때 서명 컨테이너가 서명된 내용만 프론트엔드에 넘겨준다.
이러면 공격자는 앱 서버를 속이기 위해 더 귀찮은 작업을 해야만 한다.
대안
- 클라우드 컴포넌트 관련이라면, ServiceAccount를 사용할 수도 있다.
- 서드파티 툴을 사용할 수도 있다.
- 인증을 위해 커스텀 x.509 인증서를 만들고, 요청을 쓸 수도 있다.
- 디바이스 플러그인을 쓸 수도 있다.
오퍼레이터를 마들자. 짧게 사는 토큰.
그리고 이 토큰을 시크릿으로 만든다.
이걸 파드가 사용하게 하면 파드 입장에서는 새로고침과 재발급의 과정을 모르는 식으로 어플리케이션을 구현할 수 있다.
음.. 그럼 이걸 refresh 토큰을 담는 공간으로..?
종류
type 필드에 지정하여 어떤 유형인지 명시할 수 있다.
몇가지 내장으로 지원한다.
이 정도가 있고, Opaque가 흔히 직접 사용하게 되는 시크릿일 것이다.
이런 타입 지정을 통해 들어가는 데이터에 대한 요구사항을 체크받는다.
외부 목적으로 쓸 때는 앞에 도메인을 붙이는 관례를 따르도록 권장된다.
Opaque
타입이 명시되지 않는 타입이다.
그냥 만들면 이걸로 만들어진다.
service-account-token
ServiceAccount를 식별하는 토큰을 담는 유형이다.
근데 이건 과거의 방식이고, 1.22 이후에는 TokenRequest 를 사용한다.
현재 방식은 이렇게 쓸 수 있다.
- 해당 api를 이용해 토큰을 요청한다.
- 이걸 projected volume으로 가져온다.
이러면 파드가 지워질 때 알아서 토큰도 없어진다.
이걸 쓸 수 없다면 시크릿으로 만드는 수밖에 없다.[3]
이걸 쓸 때는 존재하는 서아에 대한 kubernetes.io/service-account.name
어노테이션이 확실히 있어야 한다.
그래서 서아랑 같이 만든다면, 서아를 반드시 먼저 만들라.
만들어지고 나면 나머지 필드는 쿠버에서만들어준다.
apiVersion: v1
kind: Secret
metadata:
name: secret-sa-sample
annotations:
kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
extra: YmFyCg==
dockercfg
도커 설정 관련 정보다.
컨테이너 이미지 레지스트리 관련 설정을 할 때 사용한다.
구체적으로는 두 가지 타입이 있다.
apiVersion: v1
kind: Secret
metadata:
name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
.dockercfg: |
eyJhdXRocyI6eyJodHRwczovL2V4YW1wbGUvdjEvIjp7ImF1dGgiOiJvcGVuc2VzYW1lIn19fQo=
이게 기본 방식.
apiVersion: v1
kind: Secret
metadata:
name: secret-dockercfg
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: |
eyJhdXRocyI6eyJodHRwczovL2V4YW1wbGUvdjEvIjp7ImF1dGgiOiJvcGVuc2VzYW1lIn19fQo=
이렇게 하고 ~/.docker/config.json
의 값을 넣어주면 된다.
전부 base64로 인코딩되는데, 이게 싫으면 stringData 필드를 쓰면 된다.
api 서버는 이에 대해 명확하게 데이터 필드의 내용이 유효한 json으로 파싱될 수 있는지, docker 설정 파일 양식에 맞는지 확인한다.
kubectl create secret docker-registry secret-tiger-docker \
--docker-email=tiger@acme.example \
--docker-username=tiger \
--docker-password=pass1234 \
--docker-server=my-registry.example:5000
이런 식으로 도커 설정 파일을 만ㄷ르 수 ㅣㅇㅆ다.
이게 dockerconfigjson과 같은 방식으로 만들어준다.
{
"auths": {
"my-registry.example:5000": {
"username": "tiger",
"password": "pass1234",
"email": "tiger@acme.example",
"auth": "dGlnZXI6cGFzczEyMzQ="
}
}
}
이런 식으로 나오면 유효한 것이다.
basic-auth
기본적인 인증을 위해 쓰일 법한 타입.
stringData:
username: admin # required field for kubernetes.io/basic-auth
password: t0p-Secret # required field for kubernetes.io/basic-auth
base64로 인코딩할 거면 data필드에 넣어주면 되고, 아니면 이렇게 stringData라고 써줘도 된다.
그냥 Opaque랑 다를 게 뭐냐?
당연히 없다만.. 그래도 username과 password라는 키가 있을 것이라는, 컨벤션 합의를 편하게 이룬다는 장점은 있다.
ssh-auth
SSH에 쓰이는 키를 넣을 수 있다.
data:
# the data is abbreviated in this example
ssh-privatekey: |
UG91cmluZzYlRW1vdGljb24lU2N1YmE=
이런 식으로 쓰면 된다.
개인키만으로는 신뢰있는 통신이 힘들기에, 중간자 공격으로부터 넘어가려면 knonw_hosts같은 것도 쓰자.
tls
TLS 인증에 사용되는 키를 담는 유형.
보통은 인그레스에 활용된다.
data:
tls.crt: |
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNVakNDQWJzQ0FnMytNQTBHQ1NxR1NJYjNE
RklDQVRFLS0tLS0K
# In this example, the key data is not a real PEM-encoded private key
tls.key: |
RXhhbXBsZSBkYXRhIGZvciB0aGUgVExTIGNydCBmaWVsZA==
이런 식으로 넣어주면 된다.
crt에는 pem 확장자 파일의 내용물이 들어가면 된다.
token
bootstrap.kubernetes.io/token
은 노드를 부트스트랩할 때 사용된다.
이건 kube-system
네임스페이스에 보통 있다.
apiVersion: v1
kind: Secret
metadata:
name: bootstrap-token-5emitj
namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
expiration: "2020-09-13T04:39:10Z"
# 위의 name에 들어가는 값과 같다.
token-id: "5emitj"
token-secret: "kq4gihvszzgn1p0r"
usage-bootstrap-authentication: "true"
usage-bootstrap-signing: "true"
- token-id - 토큰을 식별하는 6자리 글자.
- token-secret - 실제 비밀을 담는 16글자.
- description - 설명할 수 있는 선택 필드
- expiration - 만기 되는 절대 시간값
- usage-bootstrap-<usage> - 어디에 쓰이는 지를 나타내는 bool 값
- auth-extra-groups -
system:bootstrappers
그룹으로 추가시키고 싶은 그룹 리스트
양식 작성법
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
앞에 .을 붙이면 파일로 마운팅될 때도 .이 같이 붙어서 숨김 파일이 된다.
data로 값을 넣을 때는 base64로 인코딩해서 넣어줘야 한다.
그냥 평문으로 넣고 싶다면 .stringData
로 넣으면 되는데, 이게 서버사이드 적용이 잘 안 된다고 한다.
volumes:
- name: foo
secret:
secretName: mysecret
optional: true
파드에서 시크릿을 가져올 때, optional 필드를 주면 없다고 파드에서 에러가 나지 않는다.
파드의 이미지를 받을 때, imagePullSecrets 필드를 써서 개인 레지스트리에서 가져오도록 인증을 할 수 있다.
서비스어카운트의 필드에 붙여두면 알아서 붙게 만들 수도 있다.
보안
그래서 이 놈이 정말 보안적으로 좋은가?
컨피그맵보다는 조금 더 보안성을 제공하긴 한다.
일단 대체로 클러스터 관련해서 사용되는 중요한 정보들을 담는데 쓰인다.
시크릿은 이를 요청하는 노드로 간다.
그리고는 kublete이 해당 노드에 tmpfs에 복사 저장한다.
그리고 시크릿이 삭제되면 복사된 데이터도 없애버린다.
노드에 있다고 해도, 시크릿을 접근할 수 있는 것은 이를 요청한 파드뿐이다.
(컨맵은 아니라는 건가)
특별 권한을 가진 파드는 노드에 저장된 모든 시크릿에 접근할 수 있다.
모범 사례
보안을 잘 챙기는 사례에 대해 소개한다.[4]
시크릿은 일단 base64로 인코딩될 뿐이나, 저장 간 암호화를 할 수 있다.
관리자와 개발자 양단에서 방법이 있다.
클러스터 관리자 설정
저장 시 암호화
Etcd에 암호화하여 저장하자
https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
시크릿에 대해 최소한의 권한만 주기
쿠버 RBAC를 이용해 시크릿에 접근할 권한을 적게 주자.
컴포넌트에 대해서, watch
, list
는 시스템 레벨에서만 허용하고 다른 것들은 get
에서 그쳐야 한다.
(list
도 내용물을 볼 수 있게 만든다.)
사람에 대해서는 세 개 다 금지하고 관리자만 etcd에 읽기 접근을 허용한다.
시크릿을 파드에 연결해 생성할 수 있는 사람은 결국 시크릿의 내용물도 볼 수 있는 것이다.
그래서 시크릿의 수명을 짧게 하고, 감사 룰을 만드는 것이 좋을 것이다.
시크릿은 네임스페이스를 따로 분리해서 아예 마운팅을 못하게 막는 것도 방법이다.
etcd 관리
더 이상 사용되지 않는 데이터면 etcd에서 깔끔하게 지우는 식의 관리도 생각해볼 수 있다.
etcd가 HA돼 있다면 통신 간에는 패킷 암호화를 하자.
외부 시크릿 제어
Vault와 같은 외부 인터페이스에 저장을 하는 것도 방법이다.
CSI로서 시크릿을 저장할 수 있는 저장소를 마련하려는 움직임도 있다.[5]
개발자 설정
사실 보안에 대해서는 관리자가 할 영역이 많다.
어디에든 통용되는 것은 아니나, 관리자가 애초에 개발자가 가급적 기밀한 데이터에 접근 못하게 하는 것이 바람직하다.
그럼에도 몇 가지 고려할 만한 사항은 있다.
- 시크릿 접근에 대한 컨테이너 제한
- 컨테이너 개나 소나 시크릿에 접근 가능하게 하지 말고, 아예 시크릿에 접근하는 사이드카를 만드는 식으로 명확하게 프로세스를 분리하는 것도 좋다.
- 읽기 후 시크릿 보호
- 시크릿을 사용하는 어플리케이션은 이를 자체적으로 저장하는 로직을 포함하지 않는 편이 좋다.
- 시크릿 양식 공유 회피
- 시크릿을 양식으로 만들었다면, 이 파일 자체를 누군가 읽지 못하도록 잘 막아야 한다.
관련 문서
이름 | noteType | created |
---|---|---|
Secret | knowledge | 2025-01-12 |