서비스 어카운트 (Service Account), 롤(Role), 클러스터 롤 (Cluster Role)

niyu·2022년 8월 8일
2

쿠버네티스 기초

목록 보기
13/15
post-thumbnail
post-custom-banner

서비스 어카운트

API 서버에서 클라이언트가 서버에서 작업을 수행하기 전에 인증 과정을 거쳐야 한다. 그리고 secret 볼륨 상의 각 컨테이너의 파일 시스템에 마운트된 token 파일의 내용을 전송해 파드가 인증된다.

모든 파드는 파드에서 실행중인 애플리케이션을 식별할 수 있는 서비스어카운트와 관련되어 있고 위 토큰 파일은 서비스어카운트의 인증 토큰을 가지고 있다.

서비스어카운트 사용자 이름은 다음과 같은 형식을 갖는다.

system:serviceaccount:<namespace>:<service account name>

각 파드는 정확히 하나의 서비스어카운트와 연관돼 있지만 여러 파드에서 같은 서비스어카운트를 사용할 수 있다. 파드는 동일한 네임스페이스의 서비스어카운트만 사용할 수 있다.

각 파드는 파드의 네임스페이스에 있는 단일 서비스어카운트와 연관돼 있다
각 파드는 파드의 네임스페이스에 있는 단일 서비스어카운트와 연관돼 있다 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/12)

파드에 각기 다른 서비스어카운트를 할당하면 각 파드에 접근할 수 있는 리소스를 제어할 수 있다.

API 서버는 토큰을 사용해 요청을 보낸 클라이언트를 인증한 다음 관련 서비스어카운트가 요청된 작업을 수행할 수 있는지 여부를 결정한다. 이때 역할 기반 엑세스 제어(RBAC) 플러그인과 같은 인가 플러그인으로 처리가 가능하다.

서비스어카운트 생성

서비스어카운트는 각 네임스페이스마다 default 서비스어카운트가 자동으로 생성된다.

왜 번거롭게 모든 파드에 디폴트 서비스어카운트를 사용하지 않고 서비스어카운트를 생성하는 것일까? 그 이유는 클러스터 보안 때문이다. 클러스터 메타 데이터를 읽을 필요가 없는 파드는 클러스터에 배포된 리소스를 검색하거나 수정할 수 없는 제한된 계정에서 실행해야 한다.

다음은 서비스어카운트 생성하는 방법이다.

# 서비스어카운트 생성
# kubectl create serviceaccount foo

serviceaccount 'foo' created

사용자 지정 토큰 시크릿이 만들어지고 서비스어카운트와 연결된다.

파드에 서비스어카운트 할당

파드 정의의 spec.serviceAccountName 필드에 서비스어카운트의 이름을 명시하여 할당할 수 있다. 서비스어카운트는 반드시 파드가 생성될 때 지정해야 한다. 그 이후에는 변경할 수 없다.

apiVersion: v1
kind: Pod
metadata:
  name: curl-custom-sa
spec:
  serviceAccountName: foo  # default 대신 다른 서비스 어카운트 사용
  containers:
  - name: main
    image: tutum/curl
    command: ["sleep", "9999999"]
  - name: ambassador
    image: luksa/kubectl-proxy:1.6.2

롤 기반 접근 제어 (RBAC)

RBAC는 권한이 없는 사용자가 클러스터 상태를 보거나 수정할 수 없도록 한다. 사용자의 역할에 따라 허용 가능한 액션을 부여한다. 여러 역할을 가진 사용자는 자신의 역할이 허용하는 모든 작업을 수행할 수 있다.

예를 들어 시크릿 업데이트와 같은 사용자 역할이 사용자에게 권한이 없는 경우, API 서버는 사용자가 시크릿에서 PUT 또는 PATCH 요청을 수행하지 못하도록 한다.

인증 가능한 동사

REST 클라이언트는 GET, POST, PUT, DELETE HTTP 요청을 통해 리소스를 조회하거나 생성, 수정할 수 있다.

HTTP 메서드단일 리소스에 관한 동사컬렉션에 관한 동사
GET, HEADget (a d watch for watching)list (and watch)
POSTcreaten/a
PUTupdaten/a
PATCHpatchn/a
DELETEdeletedelete collection

RBAC 리소스

RBAC 인증 규칙은 네 가지 리소스로 구성되고 두 가지 그룹으로 그룹화할 수 있다.

  • 리소스에서 수행할 수 있는 동사를 지정하는 클러스터롤
  • 위의 롤을 특정 사용자, 그룹 또는 서비스어카운트에 바인딩하는 롤바인딩클러스터롤바인딩

롤은 수행할 수 있는 작업을 정의하고, 바인딩은 누가 이를 수행할 수 있는지를 정의한다.

롤과 롤바인딩은 네임스페이스 수준 리소스이지만, 클러스터롤과 클러스터롤바인딩은 클러스터 수준의 리소스이다.

단일 네임스페이스 공간에 롤바인딩이 여러 개 존재할 수 있다. 마찬가지로 여러 클러스터바인딩 및 클러스터롤을 만들 수 있다. 또한 롤바인딩이 네임스페이스 수준 리소스임에도 클러스터롤을 참조할 수 있다.

롤과 롤바인딩
롤과 롤바인딩 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/12)


롤과 롤바인딩, 클러스터롤과 클러스터롤바인딩
롤과 롤바인딩, 클러스터롤과 클러스터롤바인딩 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/12)

🔎 네임스페이스와 파드를 통한 실습

# foo 네임스페이스 생성
kubectl create ns foo

# foo - pod 생성
# kubectl run --image=luksa/kubectl-proxy -n foo

# bar 네임스페이스 생성
kubectl create ns bar

# bar - pod 생성
kubectl run --image=luksa/kubectl-proxy -n bar

# 파드 sh 접속
kubectl exec -it test-145485760-ttq36 -n foo sh

# 파드 서비스 목록 나열
# minikube version: v1.12.1을 사용하는 경우 즉각적으로 권한이 없기 때문에 조회가 불가능하다.
curl localhost:8001/api/v1/namespaces/foo/services

User "system:serviceaccount:foo:default" cannot list servies in the namespace "foo".

kubectl 프록시 프로세스가 리스닝하고 있는 localhost:8001에 연결 중이다. API 서버는 파드가 동일한 네임스페이스에서 실행 중이더라도 서비스어카운트가 foo 네임스페이스의 서비스의 리스트 요청을 허용할 수 없다고 응답했다. 서비스어카운트의 디폴트 사용 권한으로 어떤 리소스든 리스트하거나 수정하는 것을 허용할 수 없다.

서비스어카운트를 허용해보자.

롤과 롤바인딩 생성

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
 namespace: foo
 name: service-reader
rules:
- apiGroups: [""]  # 서비스는 이름이 없는 core apiGroup의 리소스이므로 ""
 verbs: ["get", "list"]  # 개별 서비스를 이름으로 get하고, 모든 항목을 list 하는 것이 허용된다
 resources: ["services"]  # 이 룰은 서비스와 관련있다. 리소스 지정시에는 복수형 사용해야 한디

foo 네임스페이스에서 롤 리소스가 생성된다. 다른 네임스페이스에 해당되는 서비스는 가져오거나 나열하는 것을 허용하지 않는다.

# bar 네임스페이스에도 서비스리더 롤 생성
kubectl create role service-reader --verb=get --verb=list --resource=services -n bar

이 두 가지 롤을 사용하면 foo와 bar 네임스페이스 각각 실행하는 두 개의 파드 내에 foo와 bar 네임스페이스에서 서비스의 리스트를 허용할 수 있다.

서비스어카운트에 롤 바인딩

롤을 수행할 수 있는 액션을 정의하지만 누가 수행할 수 있는지는 지정하지 않았다. 이를 수행하려면 롤을 사용자, 서비스 어카운트 또는 그룹이 될 수 있는 주체에 바인딩해야 한다.

# foo 네임스페이스에 있는 "foo:default" 서비스어카운트에 "service-reader" role을 바인딩하는 롤바인딩 리소스 생성
kubectl create rolebinding test --role=service-reader --serviceaccount=foo:default -n foo

foo 네임스페이스에서 롤바인딩을 생성했다.

service-reader 롤을 가진 default 서비스어카운트를 바인딩하는 test 롤바인딩
service-reader 롤을 가진 default 서비스어카운트를 바인딩하는 test 롤바인딩 (출처: dinonotes.com/archives/1560)

롤바인딩의 YAML은 다음과 같다.

$ kubectl edit rolebinding test -n foo yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: test
  namespace: foo
roleRef:  # 롤바인딩은 service-reader 룰을 참조
  apiGroup: rbac.authorization.k8s.io
  kind: Role  
  name: service-reader
subjects:  # foo 네임스페이스상에서 default 서비스어카운트에 바인드
- kind: ServiceAccount 
  name: default
  namespace: foo
- kind: ServiceAccount
  name: default
  namespace: bar

bar 네임스페이스에서 실행 중인 파드 내부에서 foo 네임스페이스의 서비스를 리스트할 수 있도록 롤바인딩을 수정한다. 두 서비스어카운트는 foo 네임스페이스의 서비스를 get하거나 list하도록 허용한다.

같은 룰에 다른 네임스페이스를 가진 서비스어카운트
같은 룰에 다른 네임스페이스를 가진 서비스어카운트 (출처: dinonotes.com/archives/1560)

클러스터롤과 클러스터롤바인딩 생성

일반 롤은 롤이 위치한 동일한 네임스페이스의 리소스에만 접근할 수 있기 때문에 다른 네임스페이스와 리소스에 접근하려면 각 네임스페이스에 롤과 롤바인딩을 생성해야 한다.

또한, 노드, 영구볼륨, 네임스페이스 등의 특정 리소스는 네임스페이스와 연관이 없으며, 일반적인 롤은 /healthz와 같은 리소스를 나타내지 않는 URL에 접근 권한을 부여할 수 없다.

이런 경우들에는 이 아닌 클러스터롤을 이용한다.

클러스터에서 파드가 영구볼륨을 나열하도록 허용해보자.

# 영구볼륨 대한 클러스터롤 생성
kubectl create clusterrole pv-reader --verb=get,list --resource=persistentvolumes

# pod 컨테이너 sh 진입
kubectl exec -it test -n bar sh

# api 조회 (데이터 조회 불가)
curl localhost:8001/api/v1/persistentvolumes

# 클러스터롤 바인딩
kubectl create clusterrolebinding pv-test --clusterrole=pv-reader --serviceaccount=foo:default

# api 조회 (데이터 조회 가능)
curl localhost:8001/api/v1/persistentvolumes

클러스터 수준 리소스에 접근 권한을 부여할 때는 클러스터롤과 클러스터롤바인딩을 사용해야 한다.

클러스터롤과 클러스터롤바인딩
클러스터롤과 클러스터롤바인딩 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/12)

클러스터롤이 항상 클러스터롤바인딩과 바인딩할 필요는 없다. 롤바인딩으로 바인딩될 수 있다. 이때 파드는 foo 네임스페이스에 나열할 수 있지만 다른 특정 네임스페이스나 모든 네임스페이스에는 나열할 수 없다.

클러스터롤과 롤바인딩
클러스터롤과 롤바인딩 (출처: github.com/sungsu9022/study-kubernetes-in-action/issues/12)

롤과 클러스터롤 정리

접근롤 타입사용할 바인딩 타입
클러스터 수준 리소스 (노드, 영구 볼륨)클러스터롤클러스터롤바인딩
비리소스 URL (/api, /healthz, ...)클러스터롤클러스터롤바인딩
여러 네임스페이스에 있는 네임스페이스 리소스 (그리고 모든 네임스페이스에 걸쳐있는)클러스터롤클러스터롤바인딩
특정 네임스페이스에 있는 네임스페이스 리소스 (다수의 네임스페이스에 동일한 클러스터를 재사용)클러스터롤롤바인딩
특정 네임스페이스에 있는 네임스페이스 리소스 (롤은 각 네임스페이스에서 정의)롤바인딩

인증 권한을 현명하게 부여하기

기본적으로 파드는 클러스터의 상태를 볼 수 없는 것이 쿠버네티스 기본 정책이다. 적절한 권한을 부여하는 것은 사용자의 몫이다. 모든 사람에게 자신의 일을 하는 데 꼭 필요한 권한만 최소한으로 주고, 한 가지 이상의 권한을 주지 않는 것이 좋다.

각 파드를 위한 특정 서비스어카운트를 생성한 다음 롤바인딩으로 맞춤형 롤과 연계하는 것이 바람직하다. 원치 않는 사람이 결국 서비스어카운트의 인증 토큰을 손에 넣을 수 있다는 가능성을 예상해야 하며, 실제로 피해를 입지 않도록 항상 서비스어카운트에 제한을 둬야 한다.


References

post-custom-banner

0개의 댓글