Authorization

개요

kube-apiserver로 가는 요청이 쿠버네티스 인증을 마친 이후, 다음으로 일어나는 과정은 Authorization, 즉 인가이다.
일단 해당 요청이 인증은 돼서 들어오긴 했는데, 다음으로 그 요청이 할 수 있는 동작은 무엇인지 가늠해야 한다.
가령 일개 개발자로 인증되어 들어온 요청이 관리자만이 수행할 수 있는 클러스터의 세밀한 운영 동작을 하게 된다면 위험할 수 있다.
그래서 해당 유저가 어떤 것에 어떤 권한을 가지고 있는지 평가하는 단계가 바로 인가이다.

자기 인가 정보 확인하기

쿠버네티스 인증에서는 SubjectReview api를 통해서 자신의 정보를 확인할 수 있었다.
인가에서는 SelfSubjectAccessReview api를 써서 인가 정보를 확인할 수 있다.

kubectl auth can-i create deploy -n dev

kubectl로는 auth can-i를 통해 간단하게 확인할 수 있다.

인가에 사용되는 속성

기본적으로 인가를 위해 사용되는 속성은 총 3가지이다.
RBAC를 안다면 상당히 익숙한 개념일 것이다.
인가를 위해서 고려해야 하는 건 누가? 무엇을? 어떻게? 이다.

주체

쿠버네티스 인가 단계에서 넘어온 user, group, extra값이 주체가 된다.

대상

API 요청은 사실 두 가지 대상으로 나뉜다.
이 개념을 명확히 하려면 kube-apiserver를 본다.

리소스

흔히 말하는 오브젝트, 그리고 오브젝트에 대한 각종 파라미터나 경로 값을 말한다.
가령 파드, 서비스와 같은 것들이 여기에 속하는데, 구체적으로는 /api, /apis로 시작하는 엔드포인트를 말한다.
이들은 api 그룹을 가지고 있고, (대부분) 네임스페이스도 가지고 있다.
경우에 따라서는 서브 리소스를 가지고 있기도 하다.

비리소스

/healthz, /statusz 와 같이 컴포넌트 자체의 상태를 나타내는 경로들은 비리소스이다.

동사

위에서 리소스와 비리소스를 나눴는데, 이 둘을 구분하는 이유는 이에 대한 동작을 따로 지정할 수 있기 때문이다.
먼저 비리소스에 대해서는 단순 HTTP 메서드가 동사가 된다.
그리고 리소스에 대해서는 API request verb라 하여 다른 방식으로 동사를 규정한다.
결국 요청이 들어오는 건 HTTP 방식이니 이들도 HTTP 메서드를 가지고는 있다.
그러나 리소스에 대해서는 조금 더 세부적인 메서드를 동사로 활용하는 것이다.
image.png
이런 식으로 매칭된다.
GET, DELETE 부분을 보면 알겠지만 개별적으로 가져올지, 지켜볼지 등으로 조금 더 세분화한 것으로 보면 된다.

주의!

근데 참고로 어떤 유저가 list 동사가 있고 get 동사가 없다고 쳐도 결국 받아볼 수 있는 정보의 범위는 동일하다!
실제로 들어오는 데이터 상으로는 내부의 모든 데이터를 볼 수 있게 된다.
단순하게 현재 존재하는 시크릿에 list 동작을 해도 데이터를 json으로 뜯어보면 내부 데이터를 다 볼 수 있다는 것.

여기에, 인가와 관련된 특수 동사들이 존재한다.

몇 가지 특수한 것들이 있기는 하나, 전반적으로는 인가에 관련한 속성들은 모두 일반적인 rest api에서 활용되는 값들이다.
그래서 클러스터 인가 설정을 하면서도, 동시에 다른 운영 요소들에 대한 인가 설정을 동시에 적용할 수 있다는 장점이 있다.
가령 하나의 인가 관련 툴을 쓰고 있다면, 이걸로 클러스터 뿐만 아니라 다른 서비스에서도 활용을 할 수 있다는 말이다.

인가 모드

쿠버네티스 인증 모듈과 마찬가지로 인가 역시 여러 개의 모듈을 넣을 수 있다.
또 이것도 순서대로 인가 체크가 이뤄지며, 어느 한 모듈이 허가, 혹은 거부 등의 결정을 내리는 순간 해당 요청은 그대로 적용된다.
다만 다른 점은, 모든 모듈이 결정을 내리지 않은 요청은 무조건 403 에러로 거부된다는 것이다.

참고로 주체가 system:masters 그룹인 경우 이 주체는 인가 검증을 받지 않는다!
이 그룹은 api 서버에 제한 없이 접근할 수 있는 쿠버네티스 내장 그룹으로 설정됐기 때문이다.
그래서 유저 계정을 이 그룹에 넣는 것은 무조건 지양해야 한다.
비슷한 권한을 가지면서 인가 검증을 받을 수 있도록, cluster-admin 클러스터롤을 제공하니 이걸 사용하자.

다음의 모드가 있고, 이들을 여러 개 나열하여 사용할 수 있다.
api 서버에서 --authorization-mode=RBAC,ABAC 이런 식으로 인자로 주면 된다.

RBAC(Role-Based Access Control)

가장 기본적으로 사용되는 방식으로, 유저의 역할에 따라 인가를 결정한다.
쿠버에서는 이를 위한 api를 따로 두고 있어서, 흔히 이게 사용된다.
내용이 길어서 자세한 건 쿠버 RBAC에서 담는다.

유의점이 있는 게, RBAC는 허가만 할 뿐 거부하지 않는다.
어떤 유저는 어떤 것에 어떤 동작을 할 수 있다를 지정하지만 어떤 것을 할 수 없다를 지정하지 않는다는 말이다.
그래서 아래 있는 [[#AlwaysAllow]]와 같이 사용할 경우 실질적으로 모든 요청이 인가되니까 주의가 필요하다.

Webhook

원격 서비스에 동기적인 웹훅을 날려 응답을 받고, 이를 통해 인가를 수행하는 모드이다.[1]
--authorization-webhook-config-file에 웹훅 서버 관련 설정을 넣으면 된다.
웹훅 서버에 대한 설정은 kubeconfig 파일 형식으로 작성해야 한다.

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "kittensandponies",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}

쿠버네티스 인증에서는 TokenReview가 날아갔지만 인가에서는 SubjectAccessReview가 날아간다.
그리고 관련한 주체, 대상, 동사에 대한 정보도 함께 날아가도록 돼있다.

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": false,
	"denied": true,
    "reason": "user does not have read access to the namespace"
  }
}

그럼 웹훅 서버는 이런 식으로 status필드에 응답을 작성하면 된다.
reason을 작성해서 첨부할 수도 있다.
웹훅 모드는 굉장히 중요한 포인트를 하나 가지는데, 그것은 바로 명시적 거부가 가능하다는 것이다!
RBAC도, 아래의 ABAC도 명시적 허용만 가능하고 거부하는 것이 불가능하다.
그러나 웹훅 모드는 denied: true 를 넣어서 보내면 명확하게 인가가 거부된다.
이렇게 되면 이후 다른 모듈에서 추가적으로 인가 검증을 하는 일이 발생하지 않기 때문에 활용도가 높다고 할 수 있겠다.

{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "verb": "list",
      "group": "",
      "resource": "pods",
      "fieldSelector": {
        "requirements": [
          {"key":"spec.nodeName", "operator":"In", "values":["mynode"]}
        ]
      },
      "labelSelector": {
        "requirements": [
          {"key":"example.com/mykey", "operator":"In", "values":["myvalue"]}
        ]
      }
    },
    "user": "jane",
    "group": [
      "group1",
      "group2"
    ]
  }
}

추가적으로 Kubernetes v1.32 - Penelope 기준 베타인 기능으로 라벨이나 필드를 통해 오브젝트를 선별해서 인가 결정을 내릴 수 있다.
이를 통해 훨씬 세밀한 인가 제어가 가능할 것이다.
응답 반환은 똑같은 방식으로 해주면 된다.

왜 RBAC는 셀렉터로 설정 안 해줌 빼액

이라고 생각했는데, 관련한 논의가 이미 진행된 것이 있다.[2]
이미 다양한 사람들이 이 기능을 바라고 있는데, 왜인지 개발이 진행되지 않고 있다.
이런 걸 안 해주니까 쿠버 RBAC을 사람들이 안 쓰려 하는 거 아니겠나

ABAC(Attribute-Based Access Control)

속성 기반으로 접근 제어를 하는 모드로, 정책을 지정해서 사용한다.
주체와 대상이 어떤 속성을 가지고 있는가를 기준으로 허용할 정책을 설정할 수 있다.
--authorization-policy-file을 인자로 주고 정책 파일을 작성하면 된다.

{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group":"system:authenticated",  "nonResourcePath": "*", "readonly": true}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"admin",     "namespace": "*",              "resource": "*",         "apiGroup": "*"                   }}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"scheduler", "namespace": "*",              "resource": "pods",                       "readonly": true }}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"bob",       "namespace": "projectCaribou", "resource": "*",         "apiGroup": "*", "readonly": true }}

정책 파일은 이런 식으로 json 형태로 오브젝트 스키마를 한 줄로 작성하면 된다.[3]
이건 흔히 사용되지는 않기 때문에 자세히 다루지는 않겠다.
왜냐하면 이 방식은 세밀한 제어는 가능할지 몰라도 일일히 세팅하는 것이 매우 까다롭기 때문이다.

자료도 잘 안 나오는데, 그나마 나오는 글들에서는 선택할 여지는 있다고 이야기한다.[4]
그러나 나는 별로 와닿지 않으므로, 그냥 넘어가겠다.

Node

kubelet에 권한을 주기 위한 특별한 인가 모드이다.[5]
kubelet이 자신이 조작해야 할 무언가들이 있는지 확인하고 상태를 업데이트하기 위해 필요한 권한들이 알아서 지정돼있다.
서비스, 엔드포인트슬라이스, 노드, 파드 등이 여기에 해당한다.
system:nodes 그룹이거나 system:node:<노드이름>인 유저는 자동으로 이 모드가 적용된다.

이건 관리자 레벨에서 굳이 설정하지 않고, 그냥 인가 모드에 항상 넣어주는 게 좋다.
다만 현재 해결되고 있는 이슈 중 하나로, kubelet은 자신의 노드에 대해서만 권한을 가지고 있으면 되는데 다른 노드의 값들도 볼 수 있다.
이건 차츰 쿠버가 발전되면서 명확하게 stable 기능으로서 자신 노드에 대한 권한만 가지도록 제한될 것으로 보인다.

AlwaysAllow

그냥 모든 요청을 받는 설정이다.
당연히 매우 위험하며, 지양해야 한다.
다른 모듈이랑 같이 쓰면 안전하겠지 하는 생각은 버려야 한다.
왜냐, 위에 [[#RBAC(Role-Based Access Control)]]의 경우 거부를 하지 않기 때문에 허가되지 않은 요청은 그냥 결정을 내리지 않는다.
그러면 이 모드가 동작하게 될 것이고, 결국 무조건 모든 요청이 허용되는 꼴이 되어버린다.

AlwaysDeny

그냥 모든 요청을 거부하는 설정이다.
당연히 쓰일 일 없다.

인가 파일 구성

위에서 인가 모드를 넣기 위해 인자로 넣는 방법을 보여줬지만, 설정 파일을 넣는 방법도 있다.
--authorization-config을 넣어주면 된다!
다양한 인가 모듈을 쓰는데 인자를 넣는 게 귀찮기에, 또 파일로 설정하면 한결 편하게 관리할 수 있을 것이다.
여기에는 한꺼번에 웹훅까지, 모든 설정을 박을 수 있고, 순서도 명확히 지정할 수 있어 유용하다!
순서를 명확하게 지정하기에 유용하다.
CEL 표현식을 써서 웹훅을 호출하기 전에 불필요한 요청들을 필터링하는 것도 가능하다.
참고로 인자랑 파일을 같이 설정하면 에러가 난다.

역시 좋은 점은, 설정파일을 변경할 시 api서버가 동적으로 리로드해준다는 것.
60초마다 파일의 변경 사항을 추적하고 설정해준다.
근데 유의할 점은, Webhook 관련 설정만 리로딩해준다는 것이다.
거기에 여러 웹훅을 쓰고, 세부 정책을 지정할 때는 설정 파일만이 유효하다.

apiVersion: apiserver.config.k8s.io/v1 
kind: AuthorizationConfiguration 
authorizers: 
  - type: Webhook 
    # 이름은 라벨과 비슷하게 사용되며, 모니터링 시스템에 명시적으로 사용되는 이름이다.
    # 반드시 명시돼야 한다.
    name: webhook 
    webhook: 
      # 웹훅의 인가 표시를 받은 응답을 캐시하는 시간으로 기본은 5분이다.
      # `--authorization-webhook-cache-authorized-ttl` 와 같다. 
      authorizedTTL: 30s 
      # 웹훅의 비인가 표시를 받은 응답을 캐시하는 시간으로 기본은 30초이다.
      unauthorizedTTL: 30s 
      # 웹훅 요청 타임아웃 시간으로, 최대는 30초이다.
      # 반드시 명시돼야 한다.
      timeout: 3s 
      # 웹훅 날릴 때 사용되는 SubjectAccessReview의 api 버전
      # `--authorization-webhook-version`와 같다.
      # 반드시 명시돼야 하며, 가능한 값은 v1beta1, v1이 있다.
      subjectAccessReviewVersion: v1 
      # CEL 표현 식으로 평가될 SubjectAccessReview의 api 버전
      # 반드시 명시돼야 하며, 가능한 값은 v1이다.
      matchConditionSubjectAccessReviewVersion: v1 
      # 웹훅에서 실패 응답, 혹은 제대로 안 오는 등의 이슈가 발생할 때 적용될 정책.
      # 아래의 matchConditions 조건에서 error를 내뿜을 때도 적용된다.
      # 반드시 명시돼야 하며, 가능한 값은 NoOpinion(이후 인가자에게 위임), Deny
      failurePolicy: Deny 
      connectionInfo: 
        # 웹훅을 날릴 때 통신에 필요한 정보.
        # 가능한 값은 Kubeconfig, InClusterConfig
        # InClusterConfig는 리뷰 api가 api 서버로 호스팅됐을 때 사용? api 서버에 사용할 수 없다?
        type: KubeConfigFile 
        kubeConfigFile: /kube-system-authz-webhook.yaml 
      # 웹훅을 날리기 전, 충족돼야 하는 조건으로, 최대 64개를 적을 수 있다.
      # 순서대로 조건이 확인되며 최소한 한 조건이라도 실패하면 그대로 웹훅은 스킵된다.
      # 만약 조건식 평가에서 에러가 발생하면, 위의 failurePolicy의 정책에 따라 결정된다.
      matchConditions: 
          # CEL 표현식으로 진위를 판별해야 한다.
          # SubjectAccessreview에 접근하여 내용물을 확인한다.
          # 아래 예시는, 일단 리소스 속성이 있고, `kube-system`으로 가는 요청 중 서비스 어카운트 요청만 제외하고
          # 모두 웹훅에 날리겠다는 뜻이다.
         - expression: has(request.resourceAttributes) 
         - expression: request.resourceAttributes.namespace == 'kube-system' 
         - expression: "!('system:serviceaccounts:kube-system' in request.groups)" 
  - type: Node 
    name: node 
  - type: RBAC 
    name: rbac 
  - type: Webhook 
    name: in-cluster-authorizer 
    webhook: 
      authorizedTTL: 5m 
      unauthorizedTTL: 30s 
      timeout: 3s 
      subjectAccessReviewVersion: v1 
      failurePolicy: NoOpinion 
      connectionInfo: 
        type: InClusterConfig

만약 HA 구성을 한다면 모든 컨트롤 플레인 노드가 같은 파일을 가지게 보장하자.
그리고 쿠버 버전 업데이트 시에 이 파일 형식과 설정이 유효한지 항상 고려해야 한다.

리로딩은 참고로 웹훅 인가자 설정을 위한 것이다.
Node와 RBAC 설정에 대해서는 순서를 바꾸는 건 되지만, 없애거나 추가하는 것은 불가능하다.

워크로드 생성과 수정에 대한 권한 상승

파드를 만들 수 있는 유저는 쉽게 권한 상승을 할 여지가 있다.

관련 문서

이름 noteType created

참고


  1. https://kubernetes.io/docs/reference/access-authn-authz/webhook/ ↩︎

  2. https://github.com/kubernetes/kubernetes/issues/44703 ↩︎

  3. https://kubernetes.io/docs/reference/access-authn-authz/abac/ ↩︎

  4. https://overcast.blog/mastering-kubernetes-attribute-based-access-control-abac-bb6a732cd561 ↩︎

  5. https://kubernetes.io/docs/reference/access-authn-authz/node/ ↩︎