•
인증에 대한 이해 (understanding authentication)
•
service account 란 무엇이며 사용하는 이유
•
역할 기반 access 제어(RBAC) plugin 이해
•
using Roles and RoleBindings
•
클러스터롤과 클러스터롤 바인딩 사용
•
default role 과 바인딩 이해
8장에서 잠깐 언급했던 serviceAccount 에 대해서 주로 다루게 되며, 그 외에도 cluster 를 사용하는 다른 주제에 권한을 설정하는 방법을 학습한다
인증(authentication) 이해
11장에서 API 서버를 하나 이상의 authentication plugin 으로 구성할 수 있다고 했다.
요청을 받으면 여러 authentication plugin 을 거친다.
•
첫번째로 거치는 플러그인에서 요청을 보낸 사람이 누구인가를 알아내 API 서버 코어에 반환한다
◦
사용자 이름, id, client 가 그룹
•
이어 API 서버는 나머지 authentication plugin 호출을 중지하고, 인가 단계를 진행한다
authentication plugin 은 다음 방법을 사용해 client 의 identity 를 얻는다
•
client 인증서
•
HTTP header 로 전달된 인증 토큰
•
기본 HTTP 인증
•
기타
authentication plugin 은 API 서버를 시작할 때 CLI 옵션을 통해 활성화 할 수 있다.
사용자와 그룹
•
authentication plugin(인증 플러그인) 은 사용자의 이름, 그룹을 반환한다
•
k8s 는 해당 정보를 어디에도 저장하지 않는다.
•
k8s 는 단지 사용자가 작업을 수행할 권한이 있는지만 확인한다
사용자
k8s API server 에 접속하는 두 종류의 client 를 구분한다
•
실제 사람(사용자)
•
pod (구체적으로 파드 내부에서 실행되는 application)
이 두가지 유형 모두 authentication plugin 을 사용해 인증한다.
•
사용자는 SSO(Single Sign on) 과 같은 외부 시스템에 의해 관리 돼야 한다
◦
k8s 내에 사용자 계정을 나타내는 자원은 없다
◦
즉, k8s 가 사용자를 CRUD 할 수 없다.
•
pod 는 serviceAccount 사용한다
◦
이는 cluster 에 serviceAccount 라는 resource 로 생성되고 저장된다.
serviceAccount 는 pod 를 실행하는데 필수적이므로 자세히 살펴볼 것이다.
그룹
•
한번에 여러 사용자에게 권한을 부여하는데 사용되는 개념이다.
•
human 사용자와 serviceAccount 는 하나 이상의 그룹에 속할 수 있다.
•
인증 plugin 이 반환하는 그룹은 임의의 그룹을 나타내는 문자열이지만, built-in 그룹은 특별한 의미가 있다.
그룹의 의미
•
system:unauthenticated: 어떤 authentication plugin 에서도 client 를 인증할 수 없다.
•
system:authenticated: 성공적으로 인증된 사용자 이다.
•
system:serviceaccounts: 시스템의 모든 serviceAccount 를 포함한다
•
system:serviceaccounts:<namespace>: 특정 namespace 의 모든 serviceAccount
Introducing ServiceAccounts
8장에서 proxy 를 거치지 않고 API 서버로 직접 호출 했던것을 기억하는가?
API 서버에 작업을 수행하기 전에 그 작업을 수행하는 자신을 인증해야 했다.
이때 사용했던 방식은 secret volume 의 /var/run/secrets/kubernetes.io/serviceaccount/token 파일의 내용을 전송하여 인증했다 이 파일이 의미하는 것은 무엇일까?
•
모든 pod 는 pod 에서 실행 중인 application 의 identity 를 나타내는 serviceaccount 와 연계돼 있다
•
이 token file 은 serviceAccount 의 인증 토큰을 갖고 있다.
•
serviceAccount 를 인증하고, serviceAccount 의 사용자 이름을 API core server 로 전달한다
system:serviceaccount:<namespace>:<service account name> → serviceAccount 사용자 이름
API 서버는 authentication plugin 에 사용자 이름을 전달하며, authorize plugin 이 수행하고자 하는 작업의 수행 가능 여부를 결정한다
serviceAccount 는 API server 에 자신을 인증하는 방법에 지나지 않는다.
Understanding the ServiceAccount Resource
•
serviceAccount 는 pod, secret, config map 과 같은 resource이다
•
개별 namespace 로 범위가 지정된다.
•
각 namespace 마다 default serviceAccount 가 자동으로 생성된다
다른 resource 와 마찬가지로 serviceAccount 를 나열할 수 있다.
serviceAccount 약어는 sa 이다.
•
현재 namespace 에는 default 만 갖고 있다.
•
필요한 경우 serviceAccount 를 추가할 수 있다.
•
각 pod 는 딱 하나의 service 와 연계되지만, 여러 파드가 같은 serviceAccount를 사용할 수 있다.
각 파드는 pod의 namespace 에 있는 하나의 serviceAccount 와 연계된다.
serviceAccount 가 인가와 어떻게 밀접하게 연계돼 있는지 이해하기
•
pod menifest 에 serviceAccount 의 이름을 지정해 할당할 수 있다
◦
명시적인 할당이 없으면 default serviceAccount 를 사용한다
•
pod 에 서로 다른 serviceAccount 를 할당하면 pod 가 access 할 수 있는 resource를 제어할 수 있다
•
API 서버 토큰을 사용해 요청을 보낸 client 를 인증한 다음 관련 serviceAccount 가 요청된 작업을 수행할 수 있는지 여부를 결정한다
•
인가 플러그인 중 하나는 역할 기반 access 제어 플러그인(RBAC) 이다 k8s 1.6부터는 RBAC 를 대부분의 cluster 에서 사용해야 한다
serviceAccount 생성
모든 namespace 는 고유한 default serviceAccount 가 포함돼 있지만, 필요한 경우 추가로 만들 수 있다.
어떤 경우에 만들어야 할까?
목적은 cluster 의 보안 때문이다.
•
cluster 의 metadata 를 읽을 필요가 없는 pod 는 cluster 에 배포된 resource 를 검색, 수정이 불가능한 제한된 serviceAccount 로 실행해야 한다
•
resource metadata 를 검색하는 pod는 metadata 를 읽을 수 있는 serviceAccount를 사용한다
•
object 를 수정해야 하는 pod 는 API object를 수정할 수 있는 serviceAccount로 실행해야 한다
serviceAccount 생성(예제)
kubectl create serviceaccount foo
YAML
복사
kubectl describe serviceaccount
YAML
복사
sa 로 serviceaccount 를 축약할 수 있다.
Name: foo
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none> # 이 sa를 사용하는 파드에 이 필드 값이 자동으로 추가된다
Mountable secrets: foo-token-z5qnm
Tokens: foo-token-z5qnm
Events: <none>
YAML
복사
kubectl 의 describe를 통해 serviceAccount 검사하기
•
Mountable secrets: mount 가능한 secret 이 강제화된 경우 이 serviceAccount를 사용하는 파드만 해당 secret 을 마운트 할 수 있다
•
Tokens: 인증 토큰, 첫 번째 토큰이 container 에 mount 된다
kubectl describe secret foo-token-z5qnm
YAML
복사
Name: foo-token-z5qnm
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: foo
kubernetes.io/service-account.uid: 9088f30f-3bf8-4260-9b49-d6f40e247292
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1111 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImlyNjhKMXR2UnVoY05lekEtOWZDYTRoNmJfUFNpUHpIV3NST2tkdm5OTGcifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImZvby10b2tlbi16NXFubSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJmb28iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI5MDg4ZjMwZi0zYmY4LTQyNjAtOWI0OS1kNmY0MGUyNDcyOTIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpmb28ifQ.EElmvfJe83seQ9QwtKULpVAPof67YvcG25bFGQPA6XQzPXbQZQHtvpN-d5PveyrP3Ke0g8tY3MSYwW-WshEFb-rhwJVIhfeDYgoRrF84nQSpiKF8vCOLuY9tmpNt2yPf-lByuzcahDldvjbH34_4dUP_rFqwI5MMSudIsi833cnImlonT0xuPQF4gPn3BPugNwbamB6Jdse3B3a7FB6XddB--UXcLvHM1mG8Di8YF8nbtyfDKgME1qrFk5P7NxF51yLS5a8zuPc1x-qo4e5NGGmmam2i2N2IdoMKQLzW0voEiNsT4QuUkq_K-fw29vl3Az3NKjYC11PqxAey9fgMVQ
YAML
복사
사용자 정의 token secret
serviceAccount 의 mount 가능한 secret 이해
Mountable secrets 목록이 무엇을 표시하는 걸까?
•
pod 는 원하는 모든 secret 을 mount 할 수 있다.
•
하지만 pod 는 serviceAccount 의 mountable secret 만 mount 하도록 serviceAccount 를 설정할 수 있다
•
이 기능을 사용하려면 kubernetes.io/enforce-mountable-secrets="true" annotation 을 포함해야 한다
◦
이 annotation 이 포함되면 pod 는 serviceAccount 의 mount 가능한 secret 만 mount 할 수 있다
serviceAccount 의 Image pull secret 이해
Image pull 은 private repository 에서 container 이미지를 가져오는 데 필요한 자격증명을 갖고 있는 secret 이다
다음 예제는 7장에서 생성한 이미지 풀 secret 을 포함하는 serviceAccount 정의의 예제이다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
imagePullSecrets:
- name: my-dockerhub-secret
YAML
복사
Image pull secret 을 갖는 serviceAccount
mountable secret 과 약간 다르게 동작하는데
•
image pull secret 은 어떤 image pull secret 을 사용할 수 있는지를 결정하는게 아니라 모든 pod 에 특정 image pull secret 을 자동으로 추가한다
•
mountable secret 은 각각의 pod 가 어떤 secret 을 사용하는지 할당한다
pod 에 serviceAccount 할당
추가 serviceAccount 를 만들었다면 이를 pod 에 할당해야 한다
spec.serviceAccountName 필드에 serviceAccount 이름을 설정하면 된다
단, pod 를 생성할 때 accountService 를 설정해야 한다
나중에 변경이 안된다.
사용자 정의 serviceAccount 를 사용하는 pod 생성
이전에 사라진 tutum/curl 이미지 기반의 container 와 함께 엠베서더 container 를 실행하는 pod 를 배포했다
이 파드를 이용해 API 서버의 interface 를 탐색했다. (kubectl proxy)
이제는 foo serviceAccount 를 사용하도록 pod 를 수정하자
apiVersion: v1
kind: Pod
metadata:
name: curl-custom-sa
spec:
serviceAccountName: foo # default 대신 foo 를 사용한다
containers:
- name: main
image: tutum/ubuntu
command: ["sleep", "9999999"]
- name: ambassador
image: luksa/kubectl-proxy:1.6.2
YAML
복사
이제 shell 로 붙어서 serviceAccount 내부의 token 값을 확인해보자
root@curl-custom-sa:/# ls
bin dev home lib64 mnt proc run sbin srv tmp var
boot etc lib media opt root run.sh set_root_pw.sh sys usr
root@curl-custom-sa:/# cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImlyNjhKMXR2UnVoY05lekEtOWZDYTRoNmJfUFNpUHpIV3NST2tkdm5OTGcifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImZvby10b2tlbi16NXFubSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJmb28iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI5MDg4ZjMwZi0zYmY4LTQyNjAtOWI0OS1kNmY0MGUyNDcyOTIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpmb28ifQ.EElmvfJe83seQ9QwtKULpVAPof67YvcG25bFGQPA6XQzPXbQZQHtvpN-d5PveyrP3Ke0g8tY3MSYwW-WshEFb-rhwJVIhfeDYgoRrF84nQSpiKF8vCOLuY9tmpNt2yPf-lByuzcahDldvjbH34_4dUP_rFqwI5MMSudIsi833cnImlonT0xuPQF4gPn3BPugNwbamB6Jdse3B3a7FB6XddB--UXcLvHM1mG8Di8YF8nbtyfDKgME1qrFk5P7NxF51yLS5a8zuPc1x-qo4e5NGGmmam2i2N2IdoMKQLzW0voEiNsT4QuUkq_K-fw29vl3Az3NKjYC11PqxAey9fgMVQ
YAML
복사
token 을 봤을때 앞에서 나왔던 token 과 동일할까? jwt를 decode해보자
{
"alg": "RS256",
"kid": "ir68J1tvRuhcNezA-9fCa4h6b_PSiPzHWsROkdvnNLg"
}
YAML
복사
{
"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "default",
"kubernetes.io/serviceaccount/secret.name": "foo-token-z5qnm",
"kubernetes.io/serviceaccount/service-account.name": "foo",
"kubernetes.io/serviceaccount/service-account.uid": "9088f30f-3bf8-4260-9b49-d6f40e247292",
"sub": "system:serviceaccount:default:foo"
}
YAML
복사
일치하는 것을 확인할 수 있다
API 서버와 통신하기 위해 사용자 정의 serviceAccount token 사용
•
이 token 을 사용해 API 서버와 통신할 수 있을까?
•
엠베서더 container 를 통해서 토큰을 테스트 해보자
kubectl exec -it curl-custom-sa — curl localhost:8001/api/v1/pods
YAML
복사
이것은 사용자 정의 serviceAccount 로 pod 를 나열할 수 있다는 뜻이다
cluster 가 적절한 인가를 사용하지 않는 경우 default serviceAccount 만으로 모든 작업을 수행할 수 있으므로 굳이 추가적인 serviceAccount 를 생성하고 사용하는 것은 의미가 없다
역할 기반 access 제어로 cluster 보안
•
k8s 1.6.0 부터는 cluster 보안이 크게 향상 되었다.
•
pod 중 하나는 인증 토큰을 획득하는데 성공하면 이를 이용해 cluster에서 어떤 작업이든 수행할 수 있었다
◦
그래서 경로 탐색 취약점을 이용해 token 을 가져와 안전하지 않은 k8s cluster 의 악성 파드를 실행하는 데모를 볼 수 있다
•
k8s 1.8.0 부터는 RBAC 인가 플러그인이 GA(General Avalialbility) 로 승격되어 많은 cluster 에 기본적으로 활성화돼 있다
•
RBAC 는 권한이 없는 사용자가 cluster 상태를 보거나 수정하지 못하게 한다
RBAC 인가 plugin 소개
•
k8s API 서버는 인가 plugin 을 통해 action 을 요청하는 사용자가 action 을 사용할 수 있는지 점검한다
•
API 서버가 REST interface 를 제공하므로 사용자는 서버에 HTTP 요청을 보내 액션을 수행한다
•
사용자는 자격증명을 포함하여 자신을 인증한다
액션 이해하기
액션은 GET, POST, PUT, DELETE 유형의 HTTP 요청을 특정 resource의 REST 의 endpoint 로 호출한다
k8s 에선 이러한 리소스에 pod, service, secret 등이 있다.
•
파드 가져오기 GET
•
서비스 생성하기 CREATE
•
시크릿 업데이트 UPDATE
•
기타 등등
권한 부여 동사와 HTTP 메서드 매핑
API 서버 내에서 실행되는 RBAC 와 같은 인가 플러그인은 client 가 요청한 자원에서 요청한 동사를 수행할 수 있는지 그러한 권한이 있는지 확인한다
그 권한 외에도 특정 resource instance 에도 적용할 수 있따
resource 가 아닌 URL 경로에도 권한을 적용할 수 있다