GKE RBAC 권장사항


이 페이지에서는 역할 기반 액세스 제어(RBAC) 정책을 계획하기 위한 권장사항을 설명합니다. Google Kubernetes Engine(GKE)에서 RBAC를 구현하는 방법을 알아보려면 역할 기반 액세스 제어 구성을 참조하세요.

RBAC는 Kubernetes의 핵심 보안 기능으로, 이를 통해 사용자와 워크로드가 클러스터의 리소스에 수행할 수 있는 작업을 관리하는 세분화된 권한을 만들 수 있습니다. 플랫폼 관리자는 RBAC 역할을 만들고 이러한 역할을 서비스 계정 또는 Google 그룹스와 같은 인증된 사용자인 주체바인딩합니다.

시작하기 전에

다음 개념을 숙지해야 합니다.

이 가이드의 체크리스트는 체크리스트 요약을 참조하세요.

RBAC 작동 방식

RBAC는 다음 유형의 역할 및 바인딩을 지원합니다.

  • ClusterRole: 모든 네임스페이스 또는 전체 클러스터에 적용할 수 있는 권한 집합입니다.
  • 역할: 단일 네임스페이스로 제한되는 권한 집합입니다.
  • ClusterRoleBinding: 클러스터에서 모든 네임스페이스의 사용자 또는 그룹에 ClusterRole을 바인딩합니다.
  • RoleBinding: 특정 네임스페이스 내에서 사용자 또는 그룹에 Role 또는 ClusterRole을 바인딩합니다.

Role 또는 ClusterRole에서 권한을 rules로 정의합니다. 역할의 각 rules 필드는 API 그룹, 이 API 그룹 내에 있는 API 리소스, 이러한 리소스에 허용되는 동사(작업)로 구성됩니다. 선택적으로 resourceNames 필드를 사용하여 API 리소스의 명명된 인스턴스로 동사의 범위를 지정할 수 있습니다. 예를 들어 특정 리소스 인스턴스로 액세스 제한을 참조하세요.

역할을 정의한 후 RoleBinding 또는 ClusterRoleBinding을 사용하여 역할을 주체에 바인딩합니다. 단일 네임스페이스 또는 다중 네임스페이스에서 권한을 부여할지에 따라 바인딩 유형을 선택합니다.

RBAC 역할 설계

최소 권한의 원칙 사용

RBAC 역할에 권한을 할당할 때 최소 권한의 원칙에 따라 태스크 수행에 필요한 최소 권한을 부여합니다. 최소 권한의 원칙을 사용하면 클러스터가 손상되었을 때 권한 에스컬레이션 가능성이 줄어들고 과도한 액세스로 인해 보안 문제가 발생할 가능성이 줄어듭니다.

역할을 설계할 때는 escalate 또는 bind 동사, PersistentVolumes에 대한 create 액세스, 인증서 서명 요청에 대한 create 액세스와 같은 일반적인 권한 에스컬레이션 위험을 신중하게 고려해야 합니다. 위험 목록은 Kubernetes RBAC - 권한 에스컬레이션 위험을 참조하세요.

기본 역할 및 그룹 사용 안함

Kubernetes는 API 검색에 사용하고 관리형 구성요소 기능을 사용 설정하는 데 사용할 수 있는 기본 ClusterRole 및 ClusterRoleBinding 집합을 만듭니다. 이러한 기본 역할로 부여되는 권한은 역할에 따라 광범위할 수 있습니다. Kubernetes에는 system: 프리픽스로 식별되는 기본 사용자 및 사용자 그룹 집합도 있습니다. 기본적으로 Kubernetes와 GKE는 이러한 역할을 기본 그룹 및 다양한 주체에 자동으로 바인딩합니다. Kubernetes에서 생성되는 기본 역할 및 바인딩에 대한 전체 목록은 기본 역할 및 역할 바인딩을 참조하세요.

다음 표에서는 일부 기본 역할, 사용자, 그룹을 설명합니다. 신중하게 평가하지 않는 한 이러한 역할, 사용자, 그룹과 상호작용하지 않는 것이 좋습니다. 이러한 리소스와 상호작용하면 클러스터의 보안 상황에 의도치 않은 결과가 발생할 수 있기 때문입니다.

이름 유형 설명
cluster-admin ClusterRole 클러스터의 모든 리소스에서 모든 작업을 수행할 수 있는 권한을 주체에게 부여합니다.
system:anonymous 사용자

Kubernetes는 제공된 인증 정보가 없는 API 서버 요청에 이 사용자를 할당합니다.

이 사용자에게 역할을 바인딩하면 인증되지 않은 사용자에게 해당 역할에서 부여한 권한이 부여됩니다.

system:unauthenticated 그룹

Kubernetes는 제공된 인증 정보가 없는 API 서버 요청에 이 그룹을 할당합니다.

이 그룹에 역할을 바인딩하면 인증되지 않은 사용자에게 해당 역할에서 부여한 권한이 부여됩니다.

system:authenticated 그룹

GKE는 모든 Gmail 계정을 포함하여 Google 계정으로 로그인한 모든 사용자의 API 서버 요청에 이 그룹을 할당합니다. 실제로 누구나 Google 계정을 만들 수 있기 때문에 system:unauthenticated와 유의미하게 다르지 않습니다.

이 그룹에 역할을 바인딩하면 모든 Gmail 계정을 포함하여 Google 계정이 있는 모든 사용자에게 해당 역할에서 부여한 권한이 부여됩니다.

system:masters 그룹

Kubernetes는 시스템 기능을 사용 설정하도록 기본적으로 이 그룹에 cluster-admin ClusterRole을 할당합니다.

이 그룹에 자체 주체를 추가하면 해당 주체에게 클러스터의 모든 리소스에 대한 작업을 수행할 수 있는 액세스 권한이 부여됩니다.

가능한 경우 기본 사용자, 역할, 그룹과 관련된 바인딩을 만들지 마세요. 이로 인해 클러스터의 보안 상황에 의도치 않은 결과가 발생할 수 있습니다. 예를 들면 다음과 같습니다.

  • 기본 cluster-admin ClusterRole을 system:unauthenticated 그룹에 바인딩하면 인증되지 않은 모든 사용자가 보안 비밀을 포함하여 클러스터의 모든 리소스에 액세스할 수 있습니다. 권한이 높은 이러한 바인딩은 대량 멀웨어 캠페인과 같은 공격의 표적이 됩니다.
  • 커스텀 역할을 system:unauthenticated 그룹에 바인딩하면 인증되지 않은 사용자에게 해당 역할에서 부여한 권한이 부여됩니다.

가능한 경우 다음 가이드라인을 따르세요.

  • system:masters 그룹에 자신의 주체를 추가하지 않습니다.
  • system:unauthenticated 그룹을 RBAC 역할에 바인딩하지 않습니다.
  • system:authenticated 그룹을 RBAC 역할에 바인딩하지 않습니다.
  • system:anonymous 사용자를 RBAC 역할에 바인딩하지 않습니다.
  • cluster-admin ClusterRole을 자신의 주체 또는 기본 사용자 및 그룹에 바인딩하지 마세요. 애플리케이션에 여러 권한이 필요한 경우 필요한 권한을 정확하게 판단하고 해당 목적에 맞는 특정 역할을 만듭니다.
  • 주체를 바인딩하기 전 다른 기본 역할로 부여되는 권한을 평가합니다.
  • 이러한 그룹의 구성원을 수정하기 전 기본 그룹에 바인딩된 역할을 평가합니다.

기본 그룹 사용 방지

gcloud CLI를 사용하여 system:unauthenticatedsystem:authenticated 그룹 또는 system:anonymous 사용자를 참조하는 클러스터에서 기본이 아닌 RBAC 바인딩을 사용 중지할 수 있습니다. 새 GKE 클러스터를 만들거나 기존 클러스터를 업데이트할 때 다음 플래그 중 하나 또는 둘 다를 사용합니다. 이러한 플래그를 사용해도 이러한 그룹을 참조하는 기본 Kubernetes 바인딩은 사용 중지되지 않습니다. 이러한 플래그를 사용하려면 GKE 버전 1.30.1-gke.1283000 이상이 필요합니다.

기본 역할 및 그룹 사용 감지 및 삭제

ClusterRoleBinding 및 RoleBinding을 모두 사용하여 system:anonymous 사용자 또는 system:unauthenticated 또는 system:authenticated 그룹을 바인딩했는지 여부를 확인하기 위해 클러스터를 평가해야 합니다.

ClusterRoleBindings
  1. 소유자가 system:anonymous, system:unauthenticated 또는 system:authenticated인 ClusterRoleBinding의 이름을 나열합니다.

    kubectl get clusterrolebindings -o json \
      | jq -r '["Name"], ["-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
    

    출력에는 다음 ClusterRoleBinding만 나열됩니다.

    Name
    ----
    "system:basic-user"
    "system:discovery"
    "system:public-info-viewer"
    

    출력에 기본이 아닌 추가 바인딩이 포함된 경우 각 추가 바인딩에 대해 다음을 수행합니다. 출력에 기본이 아닌 바인딩이 없으면 다음 단계를 건너뜁니다.

  2. 바인딩과 연결된 역할의 권한을 나열합니다.

    kubectl get clusterrolebinding CLUSTER_ROLE_BINDING_NAME -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml'
    

    CLUSTER_ROLE_BINDING_NAME을 기본이 아닌 ClusterRoleBinding의 이름으로 바꿉니다.

    출력은 다음과 비슷합니다.

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

    출력의 권한을 기본 사용자 또는 그룹에 부여해도 안전하다고 판단되면 추가 조치를 취하지 않아도 됩니다. 바인딩에 의해 부여된 권한이 안전하지 않다고 판단되면 다음 단계를 진행합니다.

  3. 클러스터에서 안전하지 않은 바인딩을 삭제합니다.

    kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
    

    CLUSTER_ROLE_BINDING_NAME을 삭제할 ClusterRoleBinding의 이름으로 바꿉니다.

RoleBindings
  1. 소유자가 system:anonymous, system:unauthenticated 또는 system:authenticated인 RoleBinding의 네임스페이스 및 이름을 나열합니다.

    kubectl get rolebindings -A -o json \
      | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
    

    클러스터가 올바르게 구성되었으면 출력이 상태로 반환됩니다. 출력에 기본이 아닌 바인딩이 포함된 경우 각 추가 바인딩에 대해 다음 단계를 수행합니다. 출력이 비어 있으면 다음 단계를 건너뜁니다.

    RoleBinding 이름만 알면 다음 명령어를 사용하여 모든 네임스페이스에서 일치하는 rolebinding을 찾을 수 있습니다.

    kubectl get rolebindings -A -o json \
      | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(.metadata.name == "ROLE_BINDING_NAME") | [.metadata.namespace, .metadata.name]) | @tsv'
    

    ROLE_BINDING_NAME을 기본이 아닌 RoleBinding의 이름으로 바꿉니다.

  2. 바인딩과 연결된 역할의 권한을 나열합니다.

    kubectl get rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml --namespace ROLE_BINDING_NAMESPACE'
    

    다음을 바꿉니다.

    • ROLE_BINDING_NAME: 기본이 아닌 RoleBinding의 이름입니다.
    • ROLE_BINDING_NAMESPACE: 기본이 아닌 RoleBinding의 네임스페이스입니다.

    출력은 다음과 비슷합니다.

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

    출력의 권한을 기본 사용자 또는 그룹에 부여해도 안전하다고 판단되면 추가 조치를 취하지 않아도 됩니다. 바인딩에 의해 부여된 권한이 안전하지 않다고 판단되면 다음 단계를 진행합니다.

  3. 클러스터에서 안전하지 않은 바인딩을 삭제합니다.

    kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
    

    다음을 바꿉니다.

    • ROLE_BINDING_NAME: 삭제할 RoleBinding의 이름입니다.
    • ROLE_BINDING_NAMESPACE: 삭제할 RoleBinding의 네임스페이스입니다.

네임스페이스 수준으로 권한 범위 지정

워크로드 또는 사용자의 요구에 따라 다음과 같이 바인딩 및 역할을 사용합니다.

  • 하나의 네임스페이스에서 리소스에 액세스를 부여하려면 RoleBinding과 함께 Role을 사용합니다.
  • 하나를 초과하는 네임스페이스에서 리소스에 액세스를 부여하려면 각 네임스페이스에 대해 RoleBinding과 함께 ClusterRole을 사용합니다.
  • 모든 네임스페이스에서 리소스에 액세스를 부여하려면 ClusterRoleBinding과 함께 ClusterRole을 사용합니다.

가능한 한 적은 수의 네임스페이스로 권한을 부여합니다.

와일드 카드 사용 안함

* 문자는 모든 항목에 적용되는 와일드 카드입니다. 규칙에 와일드 카드를 사용하지 마세요. RBAC 규칙에서 API 그룹, 리소스, 동사를 명시적으로 지정합니다. 예를 들어 verbs 필드에 *를 지정하면 리소스에 get, list, watch, patch, update, deletecollection, delete 권한이 부여됩니다. 다음 표에서는 규칙에서 와일드 카드를 추가하는 예시를 보여줍니다.

권장 권장하지 않음
- rules:
    apiGroups: ["apps","extensions"]
    resources: ["deployments"]
    verbs: ["get","list","watch"]

특히 appsextensions API 그룹에 get, list, watch 동사를 부여합니다.

- rules:
    apiGroups: ["*"]
    resources: ["deployments"]
    verbs: ["get","list","watch"]

모든 API 그룹에서 deployments에 동사를 부여합니다.

- rules:
    apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]

appsextensions API 그룹의 배포에 get, list, watch 동사만 부여합니다.

- rules:
    apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["*"]

patch 또는 delete를 포함하여 모든 동사를 부여합니다.

별도의 규칙을 사용하여 특정 리소스에 대해 최소 권한의 액세스 부여

규칙을 계획할 때 각 역할에서 보다 효율적인 최소 권한의 규칙 설계를 위해 다음과 같은 상위 단계를 수행하세요.

  1. 각 리소스에서 주체가 액세스해야 하는 각 동사에 대해 별도의 RBAC 규칙 초안을 작성합니다.
  2. 규칙 초안을 작성한 후에는 규칙을 분석하여 여러 규칙에 동일한 verbs 목록이 포함되는지 확인합니다. 이러한 규칙을 단일 규칙으로 결합합니다.
  3. 남은 규칙은 서로 개별적으로 유지합니다.

이 접근 방식은 보다 체계적인 규칙 설계를 가져옵니다. 여기에서 여러 리소스에 동일한 동사를 부여하는 규칙은 하나로 결합되고 리소스에 서로 다른 동사를 부여하는 규칙은 개별적으로 유지됩니다.

예를 들어 워크로드에 deployments 리소스에 대해 가져오기 권한이 필요하지만 daemonsets 리소스에 대해서는 listwatch가 필요한 경우에는 역할을 만들 때 개별 규칙을 사용해야 합니다. RBAC 역할을 워크로드에 바인딩할 때는 deployments에서 watch를 사용할 수 없습니다.

또 다른 예시로, 워크로드에 pods 리소스와 daemonsets 리소스 모두 getwatch가 필요한 경우 워크로드에서 두 리소스 모두 동일한 동사가 필요하므로 이를 단일 규칙으로 결합할 수 있습니다.

다음 표에서는 두 규칙 설계가 모두 작동하지만 요구에 따라 분할 규칙이 리소스 액세스를 더 세부적으로 제한합니다.

권장 권장하지 않음
- rules:
    apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get"]
- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets"]
    verbs: ["list", "watch"]

Deployments에 대해 get 액세스를 부여하고 DaemonSets에 대해 watchlist 액세스를 부여합니다. 주체는 Deployments를 나열할 수 없습니다.

- rules:
    apiGroups: ["apps"]
    resources: ["deployments", "daemonsets"]
    verbs: ["get","list","watch"]

Deployments 및 DaemonSets 모두에 동사를 부여합니다. deployments 객체에 대해 list 액세스가 필요하지 않은 주체에도 이 액세스가 부여됩니다.

- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets", "deployments"]
    verbs: ["list", "watch"]

daemonsetsdeployments 리소스 모두에 대해 주체에 동일한 동사가 필요하기 때문에 2개 규칙을 결합합니다.

- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets"]
    verbs: ["list", "watch"]
- rules:
    apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["list", "watch"]

이러한 분할 규칙은 결합된 규칙과 동일한 결과를 가져오지만 역할 매니페스트에 불필요한 혼란을 초래할 수 있습니다.

특정 리소스 인스턴스에 대한 액세스 제한

RBAC를 사용하면 규칙에서 resourceNames 필드를 사용하여 리소스의 특정 명명된 인스턴스로 액세스를 제한할 수 있습니다. 예를 들어 seccomp-high ConfigMap을 update하고 다른 것은 업데이트하지 않는 RBAC 역할을 작성할 경우 resourceNames를 사용하여 해당 ConfigMap만 지정할 수 있습니다. 가능한 경우 resourceNames를 사용합니다.

권장 권장하지 않음
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

주체가 seccomp-high ConfigMap만 업데이트하도록 제한합니다. 주체가 네임스페이스에서 다른 ConfigMap을 업데이트할 수 없습니다.

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["update"]

주체가 네임스페이스에서 seccomp-high ConfigMap 및 다른 ConfigMap을 업데이트할 수 있습니다.

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["list"]
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

seccomp-high를 포함하여 네임스페이스에서 모든 ConfigMap에 list 액세스를 부여합니다. update 액세스를 seccomp-high ConfigMap만으로 제한합니다. 명명된 리소스에 대해 list를 부여할 수 없으므로 규칙이 분할됩니다.

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["update", "list"]

list 액세스와 함께 모든 ConfigMap에 대해 update 액세스를 부여합니다.

서비스 계정이 RBAC 리소스를 수정하도록 허용 안함

rbac.authorization.k8s.io API 그룹에 bind, escalate, create, update, patch 권한이 있는 Role 또는 ClusterRole 리소스를 네임스페이스의 서비스 계정에 바인딩하지 않습니다. 특히 escalatebind는 공격자가 RBAC에 내장된 에스컬레이션 방지 메커니즘을 우회할 수 있게 해줍니다.

Kubernetes 서비스 계정

각 워크로드에 Kubernetes 서비스 계정 만들기

각 워크로드에 개별 Kubernetes 서비스 계정을 만듭니다. 해당 서비스 계정에 최소 권한인 Role 또는 ClusterRole을 바인딩합니다.

기본 서비스 계정 사용 안함

Kubernetes는 모든 네임스페이스에 default라는 서비스 계정을 만듭니다. default 서비스 계정은 매니페스트에서 서비스 계정을 명시적으로 지정하지 않는 포드에 자동으로 할당됩니다. Role 또는 ClusterRoledefault 서비스 계정에 바인딩하지 않도록 합니다. Kubernetes는 이러한 역할에 액세스가 부여될 필요가 없는 포드에 default 서비스 계정을 할당할 수 있습니다.

서비스 계정 토큰을 자동으로 마운트하지 않음

포드 사양의 automountServiceAccountToken 필드는 Kubernetes가 Kubernetes 서비스 계정에 대한 사용자 인증 정보 토큰을 포드에 삽입하도록 지정합니다. 포드는 이 토큰을 사용하여 Kubernetes API 서버에 대해 인증된 요청을 수행할 수 있습니다. 이 필드의 기본값은 true입니다.

모든 GKE 버전에서 포드가 API 서버와 통신할 필요가 없으면 포드 사양에 automountServiceAccountToken=false를 설정합니다.

보안 비밀 기반 토큰보다 임시 토큰 선호

기본적으로 노드의 kubelet 프로세스는 각 포드에 대해 단기 자동 순환 서비스 계정 토큰을 검색합니다. 포드 사양에서 automountServiceAccountToken 필드를 false로 설정하지 않는 한 kubelet은 이 토큰을 포드에 예상 볼륨으로 마운트합니다. 포드에서 Kubernetes API를 호출할 때 이 토큰을 사용하여 API 서버에 인증합니다.

서비스 계정 토큰을 수동으로 검색하는 경우 Kubernetes 보안 비밀을 사용하여 토큰을 저장하지 않아야 합니다. 보안 비밀 기반 서비스 계정 토큰은 만료되지 않고 자동으로 순환되지 않는 레거시 사용자 인증 정보입니다. 서비스 계정에 대해 사용자 인증 정보가 필요하면 TokenRequest API를 사용하여 자동으로 순환되는 단기 토큰을 가져옵니다.

지속적으로 RBAC 권한 검토

RBAC 역할 및 액세스를 정기적으로 검토하여 잠재적인 에스컬레이션 경로 및 중복 규칙을 식별합니다. 예를 들어 삭제된 사용자에게 특별한 권한이 있는 Role을 바인딩하는 RoleBinding을 삭제하지 않는 경우를 생각해 보세요. 공격자가 이 네임스페이스에서 삭제된 사용자와 동일한 이름으로 사용자 계정을 만들면 해당 Role에 바인딩되고 동일한 액세스 권한을 상속합니다. 정기적인 검토는 이러한 위험을 최소화합니다.

체크리스트 요약

다음 단계