회사에서 Role과 RBAC을 이해하며 기능을 구현해야 하는 일이 있었는데, 프론트는 막상 기능을 구현하더라도 화면을 만들기 때문에 내부적으로 이 기능이 어떻게 설계되었는지 모르는 경우가 많다.
하지만 그렇게 껍데기만 만들다보면 실제로 기획이 어디가 비어있는지, API를 어떻게 써야하는지 조금씩 빈 구멍이 생기기 때문에 이렇게 실제로 쓰진 않더라도 구현에 대해 이해하는 것이 좋은 것 같다.
그에 따라 쿠버네티스 인 액션 12장을 공부하고, 아래와 같이 요약 정리를 남긴다.
클라이언트의 인증서, HTTP 헤더로 전달된 인증 토큰, 기본 HTTP 인증 등을 통해 클라이언트의 아이덴티티를 얻음
인증 플러그인은 인증된 사용자의 이름과 그룹을 반환(저장하지 않음)하고 이를 사용해 사용자가 작업을 수행할 권한이 있는 지 여부 확인
사용자
그룹
모든 파드는 파드에서 실행 중인 애플리케이션의 아이덴티티를
나타내는 서비스어카운트와 연계되어 있음
앞선 8장에서 등장한 /var/run/secrets/kubernetes.io/serviceaccount/token(토큰 파일) 파일은 서비스어카운트의 인증 토큰을 갖고 있음
(서비스 어카운트의 사용자 이름 형식 → system:serviceaccount::)
서비스어카운트는 개별 네임스페이스로 범위가 지정되며 각 네임스페이스마다 default 서비스어카운트가 자동으로 생성됨
(현재 네임스페이스는 default만 가지고 있고, 필요한 경우 추가 가능)
각 파드는 딱 하나의 서비스와 연계되지만 여러 파드가 같은 서비스어카운트 사용 가능 (같은 네임스페이스꺼만)
파드에 default 서비스어카운트를 사용하지 않고 서비스어카운트를 생성하는 이유 → 클러스터 보안
생성하기
describe를 통해 서비스어카운트 검사하기
Image pull secrets: 이 필드의 값은 이 서비스어카운트를 사용하는 파드에 자동으로 추가됨
Mountable secrets: 마운트 가능한 시크릿이 강제화된 경우 이 서비스 어카운트를 사용하는 파드만 해당 시크릿을 마운트 할 수 있음
Tokens: 인증 토큰이며 첫 번째 토큰이 컨테이너에 마운트됨 (배열로 들어올 수 있는건가..?)
사용자 정의 sa의 sercret을 검사함
(위의 token은 jwt 토큰이라고 함..)
파드가 서비스어카운트의 Mountable Secret 목록에 있는 시크릿만 마운트하도록 파드의 서비스어카운트를 설정 가능
이 기능을 사용하려면 서비스어카운트가 kubernetes.io/enforce-mountable-secrets=”true”. 어노테이션을 포함하고 있어야 함
이미지 풀 시크릿은 프라이빗 이미지 리포지터리에서 컨테이너 이미지를 가져오는 데 필요한 자격증명을 갖고 있는 시크릿
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
imagePullSecrets:
- name: my-dockerhub-secret
위와 같이 작성한다.
마운트 가능한 시크릿과 달리 이미지 풀 시크릿은 각각의 파드가 어떤 이미지 풀 시크릿을 사용할 수 있는지를 결정하는 것이 아니라 서비스어카운트를 사용해 모든 파드에 특정 이미지 풀 시크릿을 자동으로 추가하기 때문에 서비스어카운트에 이미지 풀 시크릿을 추가하면 각 파드에 개별적으로 추가할 필요가 없음
파드 정의의 spec.serviceAccountName 필드에서 서비스어카운트 이름 설정
파드를 만들 때 파드의 서비스어카운트를 설정해야 함. 나중에 변경 불가능
apiVersion: v1
kind: Pod
metadata:
name: curl-custom-sa
spec:
serviceAccountName: foo // default 대신 foo 사용
containers:
- name: main
image: tutum/curl
command: ["sleep", "9999999"]
- name: ambassador
image: luksa/kubectl-proxy:1.6.2
파드 생성 후 파드의 컨테이너에 마운트된 토큰을 위와 같이 확인할 수 있음
위의 토큰을 사용해 API 서버와 통신할 수 있음
RBAC은 권한이 없는 사용자가 클러스터 상태를 보거나 수정하지 못하게 하여 보안을 강화한다.
디폴트 서비스어카운트는 추가 권한을 부여하지 않는 한 클러스터 상태를 볼 수 없으며 어떤 식으로든 수정할 수 없다.
(RBAC 외에도 속성 기반, 웹훅 플러그인 등등의 여러 인가 플러그인이 있으나 RBAC이 표준)
API 서버는 REST 인터페이스를 제공하므로 사용자는 서버에 HTTP 요청을 보내 액션을 수행하고, 요청에 자격증명을 포함시킨다.
동사는 클라이언트가 수행한 HTTP 메서드에 매핑되고, 명사(파드, 서비스, 시크릿)는 쿠버네티스 리소스와 매핑됨
RBAC 규칙 적용 가능 범위
사용자가 액션을 수행할 수 있는지 여부를 결정하는 핵심 요소로 사용자 롤을 사용
주체는 하나 이상의 롤과 연계되어 있으며 각 롤은 특정 리소스에 특정 동사 수행 가능
사용자에게 여러 롤이 있는 경우 롤에서 허용하는 모든 작업 수행 가능
RBAC 인가 규칙은 네 개의 리소스로 구성되며 두 개의 그룹으로 분류 가능
minikube delete 후 다시 start
네임스페이스 생성
파드 실행
파드에서 서비스 목록을 나열해보려 했으나, default 서비스어카운트를 사용하기 때문에 리소스를 나열할 수 없음
아래와 같이 롤을 정의한다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: foo
name: service-reader
rules:
- apiGroups: [""]
verbs: ["get", "list"]
resources: ["services"]
리소스를 지정할 때는 복수형을 사용해야 함
롤 정의에서는 각 규칙 내에 나열된 리소스의 apiGroup을 지정해야 함.
여러 API 그룹에 속한 리소스에 관한 액세스를 허용하는 경우 여러 규칙을 사용함
yaml 말고도 아래와 같은 명령어로도 롤을 생성할 수 있음
foo 네임스페이스의 default 서비스어카운트에 service-reader 롤을 바인딩하는 롤바인딩을 만듦
롤은 수행할 수 있는 액션을 정의하지만 누가 수행할 수 있는지는 지정하지 않기 때문에 주체(사용자, 서비스 어카운트 혹은 그룹)에 바인딩함
(--serviceaccount, --user, --group 인수를 통해 원하는 주체에게 바인딩 가능)
롤바인딩은 항상 하나의 롤을 참조하지만 여러 주체에 롤을 바인딩할 수 있음
rolebinding 후엔 403 에러가 뜨지 않고 서비스가 잘 나열됨
kubectl edit rolebinding test -n foo
위와 같이 bar serviceaccount도 추가한다.
bar 네임스페이스에서 실행되는 파드의 내부에서 foo 네임스페이스의 서비스를 나열 가능
요약
네임스페이스 foo에 롤바인딩이 있으며 이는 service-reader 롤을 참조하고 foo와 bar 네임스페이스 모두에서 default 서비스어카운트를 바인딩함
어떤 리소스(ex. 노드, 퍼시스턴트볼륨, 네임스페이스..)는 전혀 네임스페이스를 지정하지 않고 API 서버는 리소스를 나타내지 않는 일부 URL 경로(ex. /healthz)를 노출하는데, 일반적인 롤로는 이런 리소스나 리소스가 아닌 URL에 관한 액세스 권한을 부여할 수 없음 → 클러스터롤의 등장
클러스터롤
위의 명령은 아래의 yaml과 같다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pv-reader
rules:
- apiGroups: [""]
verbs: ["get", "list"]
resources: ["persistentvolumes"]
파드가 퍼시스턴트볼륨을 나열하도록 허용한다.
현재는 role이 바인딩되어있지 않아 나열할 수 없음
이렇게 해도 나열할 수 없음..
롤바인딩을 생성하고 클러스터롤을 참조해서 네임스페이스가 지정된 리소스에 액세스하게 할 수 있지만(이전의 서비스 나열 예제처럼..) 클러스터 수준 리소스에는 동일한 방식을 사용할 수 없음
클러스터 수준 리소스에 액세스 권한을 부여하려면 항상 클러스터롤바인딩을 사용해야 함
API 서버는 리소스가 아닌 URL도 노출하며, 이 URL에 관한 액세스 권한도 명시적으로 부여해야 함
일반적으로 이는 system:discovery 클러스터롤/클러스터롤바인딩을 통해 자동으로 수행됨
이 클러스터롤이 리소스 대신 URL을 참조하는 것을 알 수 있음
리소스가 아닌 URL의 경우 create나 update 대신 post, put, patch가 사용됨
클러스터 수준 리소스와 마찬가지로 리소스가 아닌 URL의 클러스터롤은 클러스터롤바인딩으로 바인딩되어야 함
클러스터롤바인딩은 system:discovery 클러스터롤을 참조
모든 사용자 (인증된 사용자/인증되지 않은 사용자)를 이 클러스터롤에 바인딩함
(-k로 우회했는데도 403..)
클러스터롤은 항상 클러스터롤바인딩과 바인딩될 필요는 없고, 네임스페이스를 갖는 일반적인 롤바인딩과 바인딩될 수도 있음
view라는 clusterrole에 있는 규칙
ConfigMaps, Endpoints, PersistentVolumeClaims 등 네임스페이스가 지정된 리소스를 가져오고 나열하고 볼 수 있음
클러스터롤은 클러스러롤바인딩과 롤바인딩 중 어디에 바인드되느냐에 따라 달라짐(둘 중 하나만 바인딩 가능)
clusterrolebinding을 만들어 파드의 서비스어카운트에 바인딩하면 파드가 foo 네임스페이스에 있는 파드 나열 가능
foo 네임스페이스말고 bar 네임스페이스의 파드도 나열 가능
이제 rolebinding으로 교체한다.
다른 네임스페이스
해당 네임스페이스는 가능하나 다른 네임스페이스나 전체 네임스페이스는 조회 불가능
쿠버네티스는 디폴트 롤과 바인딩을 제공
가장 중요한 롤은 view, edit, admin과 cluster-admin 클러스터롤
view 클러스터롤
리소스에 읽기 전용 액세스 허용하기
롤, 롤바인딩과 시크릿을 제외한 거의 모든 리소스 읽을 수 있음
시크릿을 읽지 못하게 하여 권한 상승 방지
edit 클러스터롤
네임스페이스 내의 리소스 수정, 시크릿 수정 등 가능
but 롤, 롤바인딩을 보거나 수정하는 것 허용하지 않음(권한 상승 방지)
admin 클러스터롤
네임스페이스에 제어 권한 허용하기
네임스페이스에서 롤과 롤바인딩 보고 수정 가능
그 밖의 디폴트 클러스터롤
각 파드를 위한 특정 서비스어카운트를 생성한 다음 롤바인딩으로 맞춤형 롤(or 클러스터롤)과 연계하는 것이 바람직함
클러스터롤바인딩으로 파드에서 다른 네임스페이스의 리소스에 접근할 수 있기 때문에 클러스터롤바인딩으로 하면 안됨
항상 서비스어카운트에 제한을 둬야 함
Reference
쿠버네티스 인 액션 12장