ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쿠버네티스 권한관리(Authorization)
    Kubernetes 2018. 8. 16. 09:00
    반응형
    권한관리 기본
    쿠버네티스 클러스터의 api에 접근하기 위해서는 우선 유효한 사용자 인지 인증(authentication)을 거처야 합니다. 인증이 됐으면 그 사용자가 접근하려고하는 api에 권한이 있는지 확인이 된 다음에 api를 사용할 수 있습니다. 하나의 클러스터를 여러명의 사용자가 사용하는 경우에 api나 네임스페이스별로 권한을 구분해서 권한이 있는 곳에만 접근을 가능하게 할 수 있습니다. 특정 자원에 대한 읽기 전용 권한만 추가해서 다른 사람이 관리하는 네임스페이스라고 하더라도 참고용으로 살펴보게 할수도 있습니다. 물론 관리자는 모든 api에 대한 권한을 열어두고 모든 자원에 접근할수가 있습니다. 쿠버네티스에서는 이러한 권한 관리를 위해서 여러가지 방법을 제공하고 있습니다. 
    크게 ABAC(Attribute-based access control)와 RBAC(Role-based access control) 2가지 방법이 있습니다. 어떤 방법을 사용할지는 apiserver를 시작할때 옵션에 --authorization-mode 로 지정하면 됩니다.
    ABAC은 단어의 의미 그대로 속성(Attribute)기반의 권한 관리 입니다. 사용가능한 속성으로는 일반적으로 사용자(user), 그룹(group), 요청 경로(request path), 요청 동사(request verb)등외에도 네임스페이스, 자원등으로 설정할 수도 있습니다. ABAC같은 경우는 권한에 대한 내용을 파일로 관리하기 때문에 권한을 변경하려면 직접 마스터 서버에 들어가서 파일을 변경하고 apiserver를 재시작해주어야 하기 때문에 관리하기 번거로운 측면이 있습니다. 
    RBAC은 역할(Role)기반권한 관리이고 입니다. 대부분의 권한관리 시스템에서 많이 사용하는 방법입니다. 사용자와 역할을 별개로 선언한 다음에 그 2가지를 엮어서(binding)해서 사용자에게 권한을 부여해 줍니다. RBAC은 관리하기 위해서 서버에 접근할 필요없이 kubectl이나 api 를 이용해서 관리하는 것이 가능합니다. 여기서는 RBAC을 이용해서 권한을 관리하는 방법을 알아보도록 하겠습니다.

    RBAC(Role-based access control) 기본개념

    롤(Role)
    롤(Role)은 특정 api나 리소스에 대한 권한들을 명시해둔 규칙들의 집합입니다. 롤에는 그냥 롤(Role)과 클러스터롤(ClusterRole) 2가지 종류가 있습니다. 롤은 그 롤이 속한 네임스페이스에 한곳에만 적용됩니다. 클러스터롤은 특정 네임스페이스에 대한 권한이 아닌 클러스터 전체에 대한 권한을 관리합니다. 그리고 추가로 네임스페이스에 한정되지 않은 자원 및 api들에 대한 권한을 지정할 수 있습니다. 노드들에 대한 권한이라던가 “/healthz” 같은 엔드포인트에 대한 권한도 관리합니다. 
    먼저 롤에 대해 알아보겠습니다. 다음처럼 read-role.yaml을 만들어서 kubectl apply -f read-role.yaml 로 적용합니다.

    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      namespace: default
      name: read-role
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]

    파일의 구조를 보면 공통 설정인 kind와 apiVersion이 나옵니다. 그 다음에 metadata부분에는 이 롤이 속한 네임스페이스를 명시해 줍니다. 여기서는 기본 네임스페이스인 default입니다. 그리고 이 롤의 이름을 name부분에 read-role이라고 적어줍니다. rules엔 이제 이 롤이 가지는 권한에 대한 규칙을 구체적으로 적어 줍니다. 규칙들에는 배열형식으로 구체적인 값들을 나열할 수 있습니다. apiGroups에는 이 롤이 사용할 api 그룹들을 설정하는데 여기서는 따로 지정하지 않았습니다. resources에는 어떤 자원에 접근가능한지를 명시하는데 여기서는 포드에만 접근 가능하게 pods만 적어 주었습니다. verbs는 동사라는 의미를 가지는 단어 그대로의 의미대로 어떤 동작이 가능한지를 설정하는 부분입니다. 여기서는 get, list를 설정했습니다. 전체 설정의 내용을 보면 default네임스페이스의 포드에 대한 get, list동작만 허용가능한 룰이라는걸 알 수 있습니다. 설정가능한 verbs들은 get, list, create, update, patch, watch, proxy, redirect, delete, deletecollection 등이 있습니다. 
    Verb
    의미
    create
    새로운 리소스 생성
    get
    개별 리소스 조회
    list
    여러건의 리소스 조회
    update
    기존 리소스내용 전체 업데이트
    patch
    기존 리소스중 일부 내용 변경
    delete
    개별 리소스 삭제
    deletecollection
    여러 리소스 삭제

    위의 설정은 포드 전체에 대해서 get, list를 할 수 있습니다. 전체 포드가 아니라 지정된 특정 포드에 대해서만 룰을 설정하는 것도 가능합니다. 아래처럼 resourceNames을 지정하면 mypod라는 이름을 가진 포드에 대해서만 조회가 가능해 집니다. resourceNames을 지정하면 get, delete, update, patch등의 verb는 resourceNames에 지정된 리소스에 대해서만 적용됩니다. resourceName이 지정되면 create, watch, list, deletecollection등의 verb는 작동하지 않습니다. verb는 결국 개별 api를 호출하는 패턴을 지정하는건데 이 verb들의 api 패턴은 개별 리소스의 이름을 명시하는 형식이 아니기 때문에 적용되지 않습니다.
    rules:
    - apiGroups: [""]
      resources: ["pods”]
      resourceNames: [“mypod"]
      verbs: ["get", "list"]

    클러스터롤(ClusterRole)
    클러스터롤은 롤과 유사하게 다음처럼 정의할 수 있습니다. read-clusterrole.yaml로 저장한 다음에 적용합니다.
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: read-clusterrole
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]

    파일내용을 보면 kind가 ClusterRole로 롤이 Role로 되어 있던것과 다른걸 확인할 수 있습니다. 그리고 metadata부분에 보면 namespace부분이 없습니다. 클러스터롤은 전체 네임스페이스에 적용되기 때문에 네임스페이스를 별도로 명시하지 않아도 되기 때문입니다. 그 외에는 앞의 read-role과 동일합니다. 이 클러스터롤은 클러스터내의 전체 네임스페이스의 포드에 대한 get, list가 가능합니다.
    클러스터롤은 aggregationRule을 이용해서 다른 클러스터롤들 조합해서 사용할 수 있습니다. 다음 yaml 설정 내용을 보면 어떻게 설정하는지 확인할 수 있습니다. aggregationRule 하위에 clusterRoleSelectors라는 항목이 있고 그 하위에 다시 matchLabels 항목이 있습니다. 쿠버네티스의 다른 시스템과 비슷하게 클러스터롤에서도 클러스터롤에 레이블을 설정하고 그걸 aggregationRule에서 선택해서 사용하도록 되어 있는 것입니다. 여기서는 기본 레이블인 kubernetes.io/bootstrapping: rbac-defaults을 설정했습니다. rules항목에는 아무것도 넣지 않았습니다. 레이블에 맞는 다른 클러스터롤에서 필요한 룰들을 가져오기 때문입니다.
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: admin-aggregation
    aggregationRule:
      clusterRoleSelectors:
      - matchLabels:
          kubernetes.io/bootstrapping: rbac-defaults
    rules: []

    이 내용을 clusterrole-aggregation.yaml파일로 저장하고 적용해 봅니다. 그런 다음 조회해보면 룰을 직접 지정하지 않았지만 다른 클러스터 룰에서 가져와서 룰들이 들어가 있는걸 확인할 수 있습니다.  
    $ kubectl describe clusterrole admin-aggregation
    Name:         admin-aggregation
    Labels:       <none>
    Annotations:  kubectl.kubernetes.io/last-applied-configuration={"aggregationRule":{"clusterRoleSelectors":[{"matchLabels":{"kubernetes.io/bootstrapping":"rbac-defaults"}}]},"apiVersion":"rbac.authorization.k8s.io/v...
    PolicyRule:
      Resources                                                      Non-Resource URLs       Resource Names     Verbs
      ---------                                                      -----------------       --------------     -----
      bindings                                                       []                      []                 [get list watch create]
      configmaps                                                     []                      []                 [create delete deletecollection get list patch update watch get list watch get]
      endpoints                                                      []                      [kube-scheduler]   [delete get patch update]


    마지막으로 지금까지 룰은 대부분 리소스에 대해서 설정했습니다. 하지만 클러스터롤에서는 리소스가 아니라 URL형식으로 룰을 설정하는 것도 가능합니다.  다음처럼 룰에 nonResourceURLs 으로 설정해 줄 수 있습니다. 
    rules:
    - nonResourceURLs: ["/healthcheck”, “/metrics/*"]
      verbs: [“get”, “post"]

    여기 보이는 것 처럼 healthcheck용 URL에 대한 접근을 설정할 수도 있고, 메트릭을 수집하기위해서 '/metrics/*’ 로 설정해서 ‘/metric/cpu’, ‘/metric/memory’ 처럼 여러가지 메트릭을 가져올 수 있는 경로를 한번에 설정하는 것도 가능합니다. URL에 대한 요청을 관리하기 때문에 사용가능한 verbs는 get과 post 2가지 입니다.


    롤바인딩(RoleBinding)
    롤바인딩은 롤과 사용자를 묶어(binding)주는 역할을 합니다. 어떤 사용자가 무슨 롤을 사용하는지를 지정해 주는 겁니다. 권한이 롤과 클러스터롤로 구분되어 있는 것처럼 바인딩로 롤바인딩과 클러스터롤바인딩으로 구분됩니다. 역시나 롤바인딩은 특정 네임스페이스 하나에 적용되고 클러스터롤바인딩은 한번 설정하면 클러스터전체에 걸쳐서 적용됩니다.
    먼저 롤에 바인딩할 사용자를 만들어 주어야 합니다. myuser라는 사용자를 만들어 주겠습니다. 다음 내용을 serviceaccount-myuser.yaml 파일로 만들어서 클러스터에 적용합니다.
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: myuser
      namespace: default

    이제 롤바인딩을 아래처럼 만들어서 적용합니다. read-rolebinding.yaml 파일로 만들어서 저장하고 클러스터에 적용합니다.
    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: read-rolebinding
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: myuser
      apiGroup: ""
    roleRef:
      kind: Role
      name: read-role
      apiGroup: rbac.authorization.k8s.io

    kind를 보면 롤바인딩이라는 것을 알 수 있습니다. metadata에는 이 롤바인딩의 이름을 read-rolebinding로 설정했고 default 네임스페이스에서 사용하도록 명시했습니다. subjects 부분에 보면 어떤 유형의 계정과 연결하려는지 kind를 확인하면 ServiceAccount로 설정한걸 알 수 있습니다. name에는 서비스어카운트의 이름인 myuser를 설정했습니다. roleRef에는 이 사용자에게 어떤 롤을 할당할지를 표시합니다. kind에는 Role과 ClusterRole이 올 수 있는데 여기서는 Role로 설정했습니다. 롤바인딩에 클러스터롤을 설정할수도 있지만 롤바인딩에 설정된 네임스페이스에 한해서만 적용됩니다. name에는 설정하려는 롤의 이름을 넣어줍니다. 앞서 생성했던 read-role을 입력해줍니다. apiGroup에는 rbac api를 사용하기 때문에 rbac.authorization.k8s.io로 설정해 줍니다.

    클러스터롤바인딩(ClusterRoleBinding)
    클러스터롤바인딩은 다음처럼 설정할 수 있습니다. read-clusterrolebinding.yaml 파일로 저장하고 적용합니다. 롤바인딩과 대부분 비슷하지만 차이점이라고 할 만한 부분은 kind가 ClusterRoleBinding으로 변경되었고 클러스터에 관련된 설정이기 때문에 metadata에 있는 네임스페이스가 없습니다. 대신 subjects에 namespace가 있습니다. 서비스어카운트는 네임스페이스에 한정되어 있기 때문에 어떤 네임스페이스에 속한 서비스어카운트인지를 명시해 줍니다. subject의 kind에는 User, Group, ServiceAccount가 올 수 있습니다. 이중 User와 ServiceAccount는 네임스페이스 정보가 필요합니다. Group같은 경우에는 클러스터 전체에 걸쳐서 사용될 수 있기 때문에 따로 네임스페이스를 명시해주지 않아도 됩니다.
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: read-clusterrolebinding
    subjects:
    - kind: ServiceAccount
      name: myuser
      namespace: default
      apiGroup: ""
    roleRef:
      kind: ClusterRole
      name: read-clusterrole
      apiGroup: rbac.authorization.k8s.io

    이제 롤이 잘 적용됐는지 확인하기 위해서 ~/.kube/config에 myuser를 설정하고 kubectl get pods를 해보면 포드 정보를 정상적으로 읽어오는걸 확인할 수 있습니다. 여기서 이 포드의 개수를 2개로 늘려보겠습니다.
    $ kubectl get pods
    kNAME                                    READY     STATUS    RESTARTS   AGE
    kubernetes-simple-app-649f755f6-xkbxs   1/1       Running   0          9d
    $ kubectl scale deploy kubernetes-simple-app --replicas=2
    deployment.extensions "kubernetes-simple-app” scaled

    myuser에게는 권한이 없어서 실패해야 하는데 정상적으로 포드개수가 늘어났습니다. 왜 그럴까요? 도커로 설치한 쿠버네티스에는 모든 serviceaccount에게 관리자 권한을 부여하는 docker-for-desktop-binding이라는 클러스터롤바인딩이 기본값으로 설정되어 있기 때문입니다.
    $ kubectl describe clusterrolebinding docker-for-desktop-binding
    Name:         docker-for-desktop-binding
    Labels:       <none>
    Annotations:  <none>
    Role:
      Kind:  ClusterRole
      Name:  cluster-admin
    Subjects:
      Kind   Name                    Namespace
      ----   ----                    ---------
      Group  system:serviceaccounts


    Group으로 모든 system:serviceaccounts에 cluster-admin이라는 클러스터롤이 부여되어 있는걸 확인할 수 있습니다. 이제 이 클러스터롤을 편집해 보겠습니다.
    kubectl edit clusterrole cluster-admin 명령으로 클러스터롤을 열어서 확인해 보면 다음처럼 모든 권한이 열려 있는걸 확인할 수 있습니다.
    rules:
    - apiGroups:
      - '*'
      resources:
      - '*'
      verbs:
      - '*'
    - nonResourceURLs:
      - '*'
      verbs:
      - '*'

    여기서 apiGroups쪽의 verb부분의 *을 다음처럼 get으로 변경해 보겠습니다.
      verbs:
      - ‘get'

    이 내용을 저장하고 다시 포드를 조회하고 개수를 조정해 보면 다음과 같은 결과를 볼 수 있습니다.
    $kubectl get pods
    NAME                                    READY     STATUS    RESTARTS   AGE
    kubernetes-simple-app-649f755f6-2tn2j   1/1       Running   0          20s
    kubernetes-simple-app-649f755f6-xkbxs   1/1       Running   0          9d
    $ kubectl scale deploy kubernetes-simple-app --replicas=1
    error: Scaling the resource failed with: could not update the scale for deployments.extensions kubernetes-simple-app: deployments.extensions "kubernetes-simple-app" is forbidden: User "system:serviceaccount:default:myuser" cannot update deployments.extensions/scale in the namespace "default"; Current resource version 165112

    verb를 get으로 설정해 뒀기 때문에 포드 조회는 가능하지만 개수조정은 myuser에게 권한이 없다는 메세지가 나오면서 실패합니다. 이제 테스트하기 위해 변경했던 cluster-admin 클러스터롤의 verbs를 get에서 다시 *으로 변경해 줍니다. 이 때 주의해야할건 현재 유저를 myuser가 아니라 tls인증서를 이용한 관리자 유저를 사용해야 한다는 것입니다. myuser는 이미 get권한만 가지고 있도록 제한되었기 때문에 클러스터롤을 편집할수 없기 때문입니다.




    반응형

    댓글

Designed by Tistory.