Google Cloud IAM 액세스 제어에 대해 살펴볼텐데 Cloud IAM 액세스 제어가 구성되는 방법에 대해 알아보고 또한 Cloud IAM 정책 및 역할을 사용하여 제어하는 방법도 알아볼 것이다.
Cloud IAM 액세스 제어에는 누가, 무엇을, 무엇
이라는 세 가지 요소가 정의된다.
누가
는 요청하는 사람의 신원
무엇을
은 부여된 권한 집합
무엇
은 이 정책이 적용되는 리소스
Cloud IAM에서는 구성원에게 액세스 권한을 부여하며 Cloud IAM은 클라우드 ID에 대해 사용자를 인증한다.
Google 계정, G Suite 도메인 계정 및 GCP 서비스 계정을 관리하는 서비스로서의 클라우드 ID를 잊지말고 이러한 ID를 그룹으로 모을 수 있다는 사실과 그리고 그룹 자체는 자체 권한을 가질 수 있다
➡️ AWS 의 IAM 이랑 거의 동일하다고 볼 수 있을 것 같다.
만약 어떤 회사에서 Active Directory를 사용하거나 ELD 앱 서버가 있는 경우에는 어떻게 할까?
GCDS로 알려진 Google 클라우드 디렉토리 동기화를 구성하여 디렉토리 서비스의 데이터를 Google 도메인과 단방향 동기화 작업할 수 있다.
여기서 단방향 동기화 작업이란 Google 사용자, 그룹 및 공유 연락처가 디렉토리 서비스의 Maxie 정보와 동기화되고 렉터리 서비스의 데이터는 수정되지 않는다.
Cloud IAM을 사용하여 구성원에게 권한을 부여하고 특정 리소스에 대해 특정 작업을 수행할 수 있도록 한다.
GCB 권한은 일반적인 사용자 흐름에 따라 역할로 그룹화되는데 구성원에게 권한을 개별적으로 할당할 수 없는 대신 구성원에게 역할이 할당된다.
GCP 리소스에 대한 모든 작업은 API 호출
을 사용하여 수행되며 권한을 사용하여 액세스를 제어한다.
작업을 수행하려는 구성원에게 API 호출에 필요한 권한이 없으면 거부되는 격이다.
예시를 잠깐 보자면, 컴퓨팅 뷰어라는 역할이 있고 이를 통해 컴퓨팅 엔진 리소스에 대한 다양한 종류의 정보를 가져올 수 있다.
이 역할은 compute.instance.get
과 같은 이름을 가진 많은 권한으로 구성된다.
이러한 이름은 GCP 서비스의 약칭, 일종의 GCP 리소스 및 동사로 구성되는 것은 짐작해 볼 수 있다.
GCP 관리자(Root 계정을 말하는 것 같다
)는 example@gmail.com 이라는 회원에게 이 역할을 부여할 수 있으며 GCP 프로젝트 수준에서 구성원이 프로젝트의 모든 컴퓨팅 엔진 리소스를 검사할 수 있다.
이제 전체 Cloud IAM 스토리를 함께 살펴보자.
GCP에 수행하려는 리소스에 대해 수행하려는 작업에 대한 권한을 요청할 때마다 Cloud IAM 은 사용자를 인증하고 필요한 정보가 있는지 확인한다.
클라우드 IAM 정책은 바인딩 목록이며 각 바인딩에서 구성원 집합은 하나 이상의 역할에 바인딩된다.
그러면 IAM 정책을 특정 리소스
, 프로젝트
, 프로젝트 폴더
또는 전체 조직
에 연결할 수 있다.
GCP 리소스는 조직에서 시작하여 계층적으로 구성
되는데 조직 내에서 여러 프로젝트 등을 포함하는 여러 폴더를 가질 수 있고 조직 수준에 연결된 IAM 정책은 자동으로 모든 폴더, 모든 프로젝트, 모든 관련 리소스에 대한 액세스 권한을 갖는다.
이러한 방식으로 조직 계층 내의 모든 수준에서 액세스 제어를 설정할 수 있고 각 IAM 정책에 가장 적합한 수준을 선택한다.
GCP 조직 계층의 상위 수준에 적용된 Cloud IAM 정책은 해당 계층 아래의 리소스에 상속된다.
예를 들어 조직 수준에서 적용되는 정책은 모든 프로젝트에 적용되고 해당 프로젝트의 모든 리소스에 적용된다.
정책을 설계할 때 염두에 두어야 할 핵심 요소는 계층 구조의 상위 수준에서 권한을 부여한 다음 아래에서 제거할 방법이 없다.
일반적으로 더 높은 수준에서 적용하는 정책은 매우 적은 수의 권한을 부여
해야 한다.
그리고 낮은 수준에서 적용하는 정책은 필요한 사람에게만 추가 권한을 부여
해야 한다.
Cloud IAM 에는 기본(Basic), 사전 정의(Pre-Defined), 커스텀(Custom)
의 세 가지 역할 유형이 있다.
기본 역할은 Cloud IAM이 도입되기 전에 존재했지만 여전히 Cloud IAM과 함께 사용할 수 있다.
이러한 역할을 사용하여 사용자에게 프로젝트 내의 모든 Google Cloud 리소스에 대한 전역 프로젝트 수준 액세스 권한
을 부여할 수 있으며 세 가지 기본 역할이 있다.
뷰어(Viewer)
역할
편집자(Editor)
역할
소유자(Owner)
역할
이러한 역할은 App Engine, Compute Engine, Cloud Storage와 같은 모든 리소스에 액세스할 수 있다.
기본 역할은 매우 광범위하기 때문에 우리는 최소 권한의 원칙
, 즉 사용자가 작업을 수행하는 데 필요한 최소한의 권한 집합을 부여받아야 한다.
기본 역할을 사용하는 경우 최소권한 원칙을 준수하지 않을 수 있으며 운영자 실수 및 노출이 발생될 수 있다.
GKE는 Kubernetes Engine 리소스에 대한 세분화된 액세스를 제공하는 몇 가지 사전 정의된 Cloud IAM 역할을 제공한다.
GKE 뷰어 역할
은 감사에 필요할 수 있는 읽기 전용 액세스 권한을 부여한다.
일반적으로 사용되는 이 역할을 통해 클러스터를 볼 수 있지만
역할 기반 액세스 제어는 클러스터 내에서 수행할 수 있는 작업을 결정한다.
GKE 개발자 역할
은 클러스터 내의 모든 리소스에 대한 모든 권한을 부여하기 때문에 개발자 및 출시 엔지니어에게 적합하다.
GKE 관리자 역할
은 클러스터 및 클러스터 내부의 Kubernetes Engine 리소스에 대한 전체 액세스 권한을 부여한다.
만약에 프로젝트 소유자이고 시스템 관리자 및 대기 중인 엔지니어라면 GKE 클러스터 관리자 역할
은 클러스터를 생성, 삭제, 업데이트, 확인하는 데 사용되지만 Kubernetes 리소스에 대한 액세스 권한은 제공하지 않는다.
이러한 모든 역할은 클러스터 수준이 아닌 프로젝트 수준
에서 정의된다는 것을 짐작할 수 있다.
기본 역할 및 GKE 사전 정의된 역할이 너무 관대하거나 가정이 비즈니스 사례에 맞지 않는 경우, 보다 세분화된 제어로 사용자 지정 역할을 만들 수 있다.
사용자 지정 역할을 만들려면 해당 역할
을 구성할 각각의 개별 권한
을 지정해야 한다.
예를 들어 관리에만 사용할 특정 사용자 계정을 생성해야 한다고 가정한다면 특정 GKE 클러스터 내에서 실행되지만 Google Cloud 리소스를 볼 수 있는 액세스 권한이 없어야 하는 소프트웨어같은 클러스터에 인증하는 데 필요한 권한만 있는 사용자 지정 IAM 역할을 생성할 수 있다.
RBAC는 사용자 계정 권한을 관리하기 위한 세분화된 도구를 제공하는 기본 Kubernetes 보안 기능이다.
GKE 환경에서 RBAC는 클러스터 내 Kubernetes 리소스에 대한 제어를 제공하여 Cloud IAM 보안을 확장하고 GKE 및 클러스터 수준에서 액세스를 제어할 수 있도록 Cloud IAM 에서 직접 제공하는 제어 기능이다.
Kubernetes 역할 기반 액세스 제어에는 주제, 리소스 및 동사
의 세 가지 주요 요소가 있다.
Kubernetes RBAC를 사용하면 어떤 작업(동사)을 어떤 개체, 리소스, 누구, 주제에 대해 실행할 수 있는지 정의할 수 있다.
주체
리소스
동사
이러한 세 가지 요소는 두 가지 유형의 RBAC API 객체인 Role
및 RoleBinding
을 생성하여 연결할 수 있다.
Role
은 API 리소스와 동사를 연결
하며 RoleBinding
은 역할을 주제에 연결
한다.
또한, 역할 및 RoleBinding은 클러스터 또는 네임스페이스 수준에서 적용할 수 있다.
사용자에게 특정 Kubernetes 네임스페이스에 대한 액세스 권한을 부여
해야 하는 경우 Kubernetes RBAC가 유용하다.
사용자를 주체로 추가하고 적절한 권한으로 역할을 구성한 다음 개체에 바인딩할 수 있으며 이 경우 클러스터의 네임스페이스이다.
Kubernetes에는 두 가지 유형의 역할이 있는데 앞에서 언급한 Role
과 ClusterRole
이다.
RBAC 역할
은 네임스페이스 수준에서 정의되
고 RBAC ClusterRole
은 클러스터 수준에서 정의
된다.
상위 개념은 Cluster Role 인 듯하다.
위의 매니페스트는 네임스페이스 수준에서 RBAC 역할을 정의한다.
여기서는 ClusterRole이 아닌 역할 유형이 정의되며 역할이 해당 네임스페이스에 적용되도록 default 네임스페이스가 지정되었고 역할에 대해 단일 네임스페이스만 정의
할 수 있다.
규칙에서 apiGgroups는 비어 있는 것으로 정의되어 역할이 핵심 API 그룹에 적용됨을 나타낸다.
리소스는 포드로 지정되며 동사 get, list 및 watch가 할당되었다.
이렇게 세 개의 동사를 Role에 넣으면 Role을 가진 주체에게 Pod의 상태를 검사할 수 있는 권한이 부여 되는데 항상 get, list 및 watch를 함께 할당하는 것이 일반적이다.
규칙은 순전히 부가적이며, '거부' 규칙은 없으며
지정된 리소스에 대해 사용자에게 동사를 부여하는 규칙이 없으면 해당 사용자는 작업을 수행할 수 없는 방식이다.
다음으로 ClusterRole이 정의된 매니페스트 파일을 살펴보자.
ClusterRole은 클러스터 수준에서 권한을 부여하기 떄문에 네임스페이스를 지정할 필요가 없다.
API 그룹은 역할 생성 시 정의한 것과 동일하게 정의하며 리소스는 노드 및 스토리지 클래스와 같은 클러스터 범위이거나 포드 및 디플로이먼트와 같은 네임스페이스 리소스일 수 있다.
다음으로 Role 또는 ClusterRole에서 다루는 리소스 및 동사를 지정하는 규칙 집합의 몇 가지 예시를 살펴보자.
왼쪽 상단
왼쪽 하단
우측 상단
범위를 리소스의 특정 인스턴스로 제한하기 위해 리소스 이름을 지정하고 패치 및 업데이트로 지정된 동사를 지정했으며 이들은 또한 일반적으로 하나의 단위로 취급되어 함께 적용되는 동사 그룹이다.
get, update, delete 및 patch
와 같은 동사만 명명된 리소스에 사용할 수 있다.
오른쪽 하단
다음은 다양한 주제 유형을 참조하는 방법에 대한 몇 가지 예시이다.
왼쪽 상단부터 작업하면 사용자와 그룹을 지정하는 예가 표시됐으며, 왼쪽 하단에는 ServiceAccount
이름이 'default' 이고 네임스페이스가 'kube-system'으로 지정된 ServiceAccount 주제의 예가 있다.
Google groups 에는 Kubernetes RBAC 권한을 할당할 수 없으며
Cloud IAM 권한은 Google 그룹에만 할당할 수 있다.
그러나 Kubernetes는 그룹도 지원하
므로 Kubernetes RBAC 권한을 Kubernetes 그룹
에 할당할 수 있다.
이 예에서 관리자가 '개발자'라는 그룹을 생성한 것을 볼 수 있고 오른쪽 열에는 더 많은 그룹 유형이 있다.
이 예시에는 모든 네임스페이스의 모든 서비스 계정이 포함되며, 이것은 시스템 그룹이므로 GKE 환경에서 RBAC에 사용할 수 있다.
오른쪽 중간 예시에는 QA 네임스페이스의 서비스 계정만 포함되어 있고, 오른쪽 하단의 예에는 모든 인증된 사용자가 포함된다.
이제 ClusterRole을 정의했으므로 RoleBindings 및 ClusterRoleBindings를 해당 역할에 연결할 수 있다.
먼저 RoleBinding을 연결해 보자.
네임스페이스를 정의한 다음 주체(subjects)를 정의하는데 Kubernetes에서 주체 종류는 사용자, 그룹 또는 서비스 계정
일 수 있다.
주체 이름은 대소문자를 구분하며 GKE의 RBAC를 사용하여 액세스를 제어할 수 있는 계정 유형은 Google 계정, GCP 서비스 계정 또는 Kubernetes 서비스 계정이며 이메일 주소를 사용하여 식별된다.
역할 종류와 역할 이름을 정의하여 roleRef를 사용하여 주체 및 RoleBinding에 역할을 바인딩한다.
RoleBinding은 역할 또는 ClusterRole을 참조할 수 있으며 ClusterRole이 RoleBinding에 바인딩된 경우 전체 클러스터가 아니라 RoleBinding에 대해 정의된 특정 네임스페이스의 리소스에 대한 권한만 부여 된다.
여기선 ClusterRole이 정의되는데, ClusterRole은 클러스터 수준
에서 권한을 부여하므로 네임스페이스를 지정할 필요가 없으며 ApiGroups는 Role 생성 시 정의한 것과 동일하게 정의한다.
리소스는 노드 및 스토리지 클래스와 같은 클러스터 범위이거나 포드 및 디플로이먼트와 같은 네임스페이스 안의 리소스일 수 있다.
ClusterRoleBinding은 항상 클러스터 전체 범위를 갖기 때문에 네임스페이스는 언급되지 않으며, ClusterRoleBinding은 역할이 아닌 ClusterRole만 참조
할 수 있다.
Kubernetes RBAC를 사용하면 네임스페이스 수준과 클러스터 수준 모두에서 사용자 및 그룹을 사용하는 사람과 서비스 계정을 사용하는 컨테이너에 대한 세분화된 권한을 관리할 수 있다.
리소스 및 동사는 Roles 또는 ClusterRoles를 사용하여 바인딩되며 역할 및 ClusterRole은 RoleBinding 또는 ClusterRoleBinding을 사용하여 주체에 바인딩된다.
다양한 ClusterRole 및 ClusterRoleBinding도 RBAC 시스템 내에서 사전 정의될 수 있다.
사전 정의된 정보들은 아래 Kubernetes Docs에 자세히 설명되어 있다.
https://kubernetes.io/docs/reference/access-authn-authz/rbac/
역할 및 ClusterRole을 생성할 때 리소스가 네임스페이스와 연결되어 있는지 또는 클러스터 수준에서 정의되어 있는지 여부를 고려해야 한다.
kubectl api-resources
명령을 사용하여 'NameSpaced' 리소스
와 그렇지 않은 리소스를 나열할 수 있으므로 일반적으로 클러스터 수준 리소스는 ClusterRole을 사용하여 관리해야 하며 NameSpaced 리소스는 역할을 사용하여 관리해야 한다.
그러나 여러 네임스페이스
에서 RBAC 권한을 정의해야 하는 경우 ClusterRole
을 사용해야 한다.
GKE에서 API 서버, etcd 데이터베이스, 컨트롤러 관리자와 같은 모든 제어 영역 구성요소는 Google에서 관리한다.
각 클러스터에는 자체 루트 인증 기관(줄여서 'CA'
)이 있는데 내부 Google 서비스는 이 CA의 루트 키를 관리한다.
컨트롤 플레인과 클러스터의 노드 간의 보안 통신은 CA에서 발급한 인증서에서 제공하는 Root Trust Share에 의존한다.
GKE는 별도의 클러스터별 CA를 사용하여 클러스터 내의 etcd 데이터베이스에 대한 인증서를 제공하는데 이 기능을 켜기 위해 아무 것도 할 필요가 없으며 구글에서 자동 설정된다.
이는 클러스터에서 실행 중인 서비스와 클러스터 작동을 유지하는 데이터베이스 간에 공유 신뢰가 필요하지 않음을 의미하기 때문에 유용하다.
Kubernetes API 서버
와 노드의 기본 Kubernetes 에이전트인 kubelets
은 서로 통신할 때 보안 네트워크 통신 프로토콜(TLS 및 SSH)을 사용하는데 이러한 프로토콜을 지원하기 위해 클러스터의 루트 CA에서 발급한 인증서를 사용한다.
개별 클러스터마다 별도의 CA가 사용되기 때문에 한 클러스터의 손상된 CA를 사용하여 다른 클러스터를 손상시킬 수 없는건 당연하다
Kubernetes 클러스터의 새 노드가 생성되면 생성의 일부로 노드에 공유 암호가 주입
이 되고 그런 다음 이 시크릿은 kubelet에서 인증서 서명 요청을 클러스터 루트 CA에 제출하는 데 사용된다.
이런 식으로 노드가 생성될 때 클라이언트 인증서를 얻을 수 있고 갱신 또는 교체가 필요할 때 새 인증서를 얻을 수 있다.
Kubelets는 이러한 클라이언트 인증서를 사용하여 API 서버와 안전하게 통신하는 방식이다.
이 시크릿은 메타데이터 은닉이 활성화되지 않은 경우
파드 및 확장된 컨테이너에서 액세스할 수 있다.
클러스터의 보안 태세를 개선하고 자격 증명을 손상시키는 잠재적인 보안 위반의 영향을 제한하려면 이러한 자격 증명을 주기적으로 교체하는 것이 중요하는데 이것은 Trade-Off
이다.
클러스터가 고가치 자산을 관리하는 경우 자격 증명을 자주 교체하는 것이 좋다.
하지만 교체할 때는 클러스터의 kube-APIserver를 잠시 사용할 수 없기 때문에 조직의 보안 정책에 적합한 절충안을 만들어야 한다.
다음 실습 중에 API 서버 및 클라이언트에서 사용하는 자격 증명을 수동으로 교체하는 방법을 배울 예정인데 Google에서 전적으로 관리하기 때문에 GKE에서 etcd 인증서는 수동으로 순환할 수 없다.
이 프로세스는 기존 IP 주소(10.1.1.1)와 함께 클러스터 컨트롤 플레인에 대한 새 IP 주소(10.1.1.2_를 생성하는 것으로 시작되며 새 자격 증명이 컨트롤 플레인에 발급된다.
Pod는 계속 실행되지만 API 서버는 이 기간 동안 사용할 수 없다.
제어 영역이 재구성되면 새 IP와 사용자 인증 정보를 사용하도록 GKE에서 노드를 자동으로 업데이트하며 이로 인해 GKE도 노드 버전을 지원되는 가장 가까운 버전으로 자동 업그레이드된다.
노드가 다시 생성되기 때문에 컨트롤러 없이 실행 중인 모든 포드
는 사용할 수 없게 된다.
클러스터 외부의 모든 API 클라이언트도 새 자격 증명을 사용하도록 업데이트해야 한다.
클러스터 컨트롤 플레인이 새 IP 주소 및 새 자격 증명 제공을 시작하고 이전 IP 주소 및 이전 자격 증명을 제거하려면 주소 순환을 완료해야 한다.
순환을 수동으로 완료하지 않으면 GKE가 7일 후에 순환을 자동으로 완료시킨다.
사용자 인증 정보 순환을 시작하려면 'start-credential-rotation'
gcloud 명령어를 실행하여 새 사용자 인증 정보를 만들고 이 사용자 인증 정보를 제어 영역에 발급한다.
순환을 완료하려면 새 사용자 인증 정보로만 제공하도록 제어 영역을 구성하는 'complete-credential-rotation'
gcloud 명령어를 실행하면 된다.
클러스터의 IP 주소를 순환할 수도 있으며 컨트롤 플레인 IP 주소가 변경될 때 인증서를 갱신해야 하지만 위에 표시된 것과 같이 다른 명령을 사용하기 때문에 기본적으로 동일한 프로세스를 거친다.
포드는 노드 구성에 사용되는 노드 시크릿과 같이 실행 중인 노드의 메타데이터에 액세스할 수 있다.
Pod가 손상되면 의도하지 않은 방식으로 사용될 가능성이 있기 때문에 노출을 방지하기 위해 취할 수 있는 몇 가지 조치가 있다.
최소한의 권한으로 노드에 대한 Cloud IAM 서비스 계정을 항상 구성
'compute.instances.get' 권한
을 사용하지 않는 것수행해야 하는 두 번째 단계는 레거시 메타데이터 API 비활성화
/0.1/ 및 /v1beta1/
을 사용하는 Compute Engine API 엔드포인트는 메타데이터 쿼리를 지원함
/v1/
API는 메타데이터 검색을 제한한다.
GKE 버전 1.12부터 레거시 Compute Engine 메타데이터 엔드포인트는 기본적으로 사용 중지되며 이전 버전에서는 새 클러스터를 만들거나 기존 클러스터에 새 노드 풀을 추가해야만 비활성화할 수 있음
메타데이터 숨김을 활성화
기본적으로 포드가 노드의 메타데이터에 액세스하지 못하도록 하는 방화벽
kube-env(kubelet 자격 증명 포함) 및 가상 머신의 인스턴스 ID 토큰에 대한 액세스를 제한하여 이를 수행
이는 향후 더 나은 보안 개선 사항이 개발됨에 따라 더 이상 사용되지 않는 임시 솔루션임
기본적으로 사용자는 권한 승격을 허용하고 호스트 파일 시스템 및 호스트 네트워크에 액세스할 수 있는 포드 내부에 컨테이너를 배포할 수 있다.
이러한 기능은 편리한 경우도 있지만 보안 관점에서 바람직하지 않은 경우도 있을 수 있기 때문에 securiy context
를 사용하여 Pod의 컨테이너가 수행할 수 있는 작업에 대한 제한을 정의할 수 있으며 특정 보안 수단을 사용하도록 강제할 수도 있다.
보안 컨텍스트는 Pod 사양에 정의된 보안 설정 집합이다.
Pod 내부의 securityContext 정의는 첫 번째 프로세스가 사용자 ID 1,000으로 실행되도록 지정하고 Pod의 모든 컨테이너 및 그룹 ID 2,000은 Pod의 모든 컨테이너와 연결된다.
이는 컨테이너에 대한 특정 사용자 및 그룹 컨텍스트를 제공하며 이 컨텍스트에서 가장 중요한 점은 1,000이 0이 아니라는 것
!
Linux 시스템에서 0은 권한이 있는 루트 사용자의 사용자 ID
이며 컨테이너 내부에서 실행되는 코드에서 루트 권한을 제거하면 손상 시 수행할 수 있는 작업이 제한된다.
포드 수준에서 보안 컨텍스트를 정의하면 포드의 모든 컨테이너에 적용되며 Pod 정의에서 보안 컨텍스트를 사용하면 호스트 네임스페이스 사용에 대한 많은 제어를 실행할 수 있다.
네트워킹, 파일 시스템 및 볼륨 유형, 권한이 있는 컨테이너를 실행할 수 있는지 여부 및 컨테이너의 코드가 루트 권한으로 에스컬레이션될 수 있는지 여부같은 제어를 실행할 수 있고 다른 보안 설정을 제어할 수도 있다.
예를 들어 Seccomp
를 활성화하여 컨테이너에서 실행 중인 코드가 알고 있는 시스템 호출을 수행하지 못하도록 차단할 수 있고 합법적으로 만들어서는 안 되는 경우 보안 프로필을 사용하여 개별 프로그램의 작업을 제한하는 AppArmor
를 활성화할 수 있다.
또한, 프로세스에 일부 권한만 부여하여 일부 Linux 기능에 대한 액세스를 제한할 수도 있다.
만약, 수십 또는 수백 개의 Pod가 있는 경우 어떻게 제어해야 할까?
각 개별 포드에서 보안 컨텍스트를 직접 구성하는 것은 많은 작업이 될 수 있기 때문에 보안 구성을 별도로 정의하고 관리한 다음 이를 필요로 하는 포드에 적용하는 것이 더 쉬울 수 있다.
PodSecurityPolicies
를 정의하여 재사용 가능한 보안 컨텍스트를 만들고 각 Pod 정의에서 해당 세부 정보를 지정하고 관리할 필요 없이 PodSecurityPolicies를 여러 Pod에 적용할 수 있다.
PodSecurityPolicy 는 각각의 개체와 허용 컨트롤러로 구성되는데 PodSecurityPolicy 개체는 제한 사항, 요구 사항 및 기본값의 집합이며 동일한 정책에 정의되어 있다.
Pod 내부의 보안 컨텍스트로 사용되며 동일한 보안 기능을 제어하는데 사용할 수 있다.
Pod가 클러스터에 허용되려면 PodSecurityPolicy에 정의된 모든 보안 조건을 충족해야 한다.
이러한 규칙은 Pod가 생성되거나 업데이트될 때만 적용된다.
PodSecurityPolicy 컨트롤러
는 하나 이상의 보안 정책에 대해 포드 생성 또는 업데이트 요청을 확인하거나 수정하는 승인 컨트롤러이다.
앞부분에서 특정 작업을 수행하기 위한 요청이 인증되고 승인되어야 한다는 것을 배웠지만 허용 제어
라고 하는 추가 단계가 있다.
유효성을 검사하거나 변경하지 않는 허용 컨트롤러는 요청의 유효성만 검사
한다.
변경 허용 컨트롤러
는 필요한 경우 요청을 수정할 수 있으며 요청의 유효성을 검사할 수도 있다.
요청은 여러 컨트롤러를 통해 전달될 수 있으며 어느 시점에서 요청이 실패하면 전체 요청이 즉시 거부되고 최종 사용자에게 오류를 공지한다.
PodSecurityPolicy 승인 컨트롤러
는 포드의 생성 및 수정
에 대해 작동하며 요청된 보안 컨텍스트 및 사용 가능한 PodSecurityPolicies를 기반으로 포드를 승인해야 하는지 여부를 결정한다.
이러한 정책은 Pod 생성 또는 업데이트 중에 적용되지만 보안 컨텍스트는 컨테이너 런타임에 의해 적용
된다.
PodSecurityPolicy를 정의한 후에는 권한을 부여해야 하며 그렇지 않으면 포드가 생성되지 않는다.
Kubernetes RBAC 를 사용하여 정책을 승인할 수 있는데 위의 예시에서 ClusterRole은 'demo-psp'라는 PodSecurityPolicy를 사용할 수 있도록 허용한다.
다음으로 RoleBinding
을 정의하여 이전 ClusterRole을 사용자 또는 그룹에 바인딩한다.
위의 예시에서는 RoleBinding에 대한 두 개의 주제가 있는데 'demo' 네임스페이스 내의 모든 서비스 계정을 포함하는 그룹과 'demo' 네임스페이스의 특정 서비스 계정이다.
RoleBinding은 디플로이먼트, ReplicaSet 또는 기타 템플릿 컨트롤러일 수 있는 포드 생성자에게 권한을 부여하거나 생성된 포드 서비스 계정에 권한을 부여할 수 있다.
정책에 대한 컨트롤러 액세스 권한을 부여하면 해당 정책에 의해 생성된 모든 Pod에 대한 액세스 권한이 부여된다.
따라서 정책을 승인하는 기본 방법은 Pod의 서비스 계정에 대한 액세스 권한을 부여
하는 것이다.
PodSecurityPolicy 컨트롤러가 없으면 PodSecurityPolicy는 아무 의미가 없으므로 정책을 정의하고 PodSecurityPolicy 컨트롤러를 활성화
하려면 둘 다 필요하다.
정책을 정의하기 전에 PodSecurityPolicy 컨트롤러를 활성화하면 아무 것도 배포할 수 없도록 한것이나 다름없다.
GKE에서 PodSecurityPolicy 컨트롤러는 기본적으로 사용 중지되어 있으며 PodSecurityPolicies를 사용하기로 선택한 경우 먼저 이를 정의한 다음 여기에 표시된 상위의 gcloud 명령어로 컨트롤러를 사용 설정하면 된다.
GKE에서, 특히 GKE 클러스터에서 최신 버전의 Kubernetes를 실행하도록 선택한 경우 Kubernetes에서 추가 보안 조치를 취할 수 있으며 이러한 조치 중 다수는 기본적으로 활성화된다.
예를 들어 노드에 Google의 컨테이너 최적화 OS를 사용하는 GKE의 기본 정책은 노드 보안을 향상시킨다는 장점이 있다.
범용 Linux 배포와 달리 Container-Optimized OS는 최소 읽기 전용 파일 시스템을 구현하고 시스템 무결성 검사를 수행하며 방화벽, 감사 로깅 및 자동 업데이트를 구현하기 때문이다.
자동 노드 업그레이드를 활성화하여 모든 노드를 최신 버전의 Kubernetes로 업데이트할 수 있다.
외부 IP 주소가 없는 노드를 포함하는 비공개 클러스터를 실행하도록 선택할 수 있거나 공개적으로 연결할 수 있는 엔드포인트가 없는 프라이빗 클러스터에 대해 클러스터 제어 플레인을 실행하도록 선택할 수도 있다.
기본적으로 비공개 클러스터는 Google Cloud IP 주소가 클러스터 제어 영역 엔드포인트에 액세스하는 것을 허용하지 않지만 승인된 네트워크에서 비공개 클러스터를 사용하면 선택한 특정 IP 주소 범위에서만 클러스터 제어 평면에 도달할 수 있다.
클러스터의 VPC 네트워크에 있는 노드는 여전히 제어 영역에 액세스할 수 있으며 이를 관리하는 Google의 내부 프로덕션 작업도 마찬가지이다.
민감한 구성 정보를 ConfigMaps에 저장하는 대신 암호화된 비밀을 활용하여 저장하는 것을 잊지 말고 또한, 가능하면 개인이 아닌 그룹에 권한을 부여하자.
이는 Google 그룹에 역할을 부여할 수 있는 Cloud IAM과 Kubernetes 그룹에 역할을 바인딩할 수 있는 Kubernetes RBAC 모두에 적용된다.
여러 위치에서 'Pat'이라는 관리자에게 권한을 부여한 후 Pat이 회사를 떠난다고 가정해보자.
이제 Pat이 권한을 갖고 있는 모든 위치를 추적하고 제거해야 하는데 이는 지루하고 오류가 발생하기 쉽상이다.
항상 사용자가 아닌 그룹에 권한을 부여하는 모범 사례를 따랐다면 Pat을 관리자 그룹에서 제외하는 것만으로 Pat의 액세스 권한을 제거할 수 있다는 편리성이 있다.
쿠버 대시보드는 이용하지 말자
Kubernetes 대시보드는 Kubernetes의 기존 추가 기능인데 원래는 관리자가 클러스터를 관리할 수 있는 편리한 웹 기반 방법을 제공하기 위한 것이었다.
과거에 Kubernetes 대시보드는 기본적으로 권한이 높은 Kubernetes 서비스 계정의 지원을 받았지만 종종 인터넷에서 액세스할 수 있는 상태로 유지되었으며 이는 심각한 문제였다.
기본 구성은 원격 공격에 취약할 수 있는 인터페이스를 노출시켰기 때문에 GKE는 이러한 위험을 줄이기 위한 조치를 취했다.
Kubernetes 버전 1.7 이상을 실행하는 GKE 클러스터는 Kubernetes 대시보드 관리자 액세스 권한을 부여하지 않고 Kubernetes 버전 1.10 이상에서 실행되는 GKE 클러스터는 기본적으로 Kubernetes 대시보드를 완전히 사용 중지했다.
Kubernetes 대시보드를 사용하는 대신 이 전문 분야에서 해왔던 것처럼 Cloud Console의 기본 제공 GKE 대시보드 또는 kubectl 명령어를 대신 사용하도록 하자.
Cloud IAM을 사용하여 GKE 액세스 제어
Pod 보안 정책을 만들고 사용하여 Pod 생성을 제어
IP 주소 및 자격 증명 순환 수행
평소와 같이 제공된 사용자 이름 1 로 시크릿 창에서 Google Cloud Console에 로그인 (두 사용자 이름 모두 동일한 암호)
Google Cloud Console 제목 표시줄에서 Cloud Shell 활성화 ( Cloud Shell 아이콘)를 클릭
시크릿 창에서 다른 탭 열기
console.cloud.google.com 으로 이동
화면 오른쪽 상단 모서리에 있는 사용자 아이콘을 클릭한 다음 계정 추가를 클릭
제공된 사용자 이름 2 로 Google Cloud Console에 로그인
참고: 사용자 이름 2 Google Cloud Console 탭 에 있는지 확인
사용자 이름 2 로 로그인한 상태에서 탐색 메뉴 ( 탐색 메뉴 아이콘) 에서 Kubernetes Engine > 클러스터 를 클릭
페이지 상단에서 랩 프로젝트 ID가 선택되어 있는지 확인
클러스터 생성 옵션이 비활성화되어 있음
사용자 이름 2는 현재 프로젝트에 대한 액세스 권한이 있지만
뷰어 역할만 소유하여 프로젝트의 모든 리소스를 볼 수 있지만 읽기 전용이다.
사용자 이름 2가 기본 역할을 사용하여 GKE 클러스터를 만들고 워크로드를 배포하여 이 프로젝트에서 모든 GKE 클러스터를 관리하고 해당 클러스터 내의 리소스를 관리할 수 있는 사용자 권한을 사용자에게 부여하도록 허용한다.
사용자 이름 1 계정에는 프로젝트 소유자 권한이 있으며 해당 계정을 사용하여 사용자 이름 2 에 추가 권한을 부여할 것이다.
참고: 사용자 이름 1 Google Cloud Console 탭 에 있는지 확인
탐색 메뉴 ( 탐색 메뉴 아이콘) 에서 IAM 및 관리자 > IAM 을 클릭
IAM 콘솔 에서 Username 2 에 해당하는 행을 찾은 다음 해당 행의 오른쪽 끝에 있는 연필 아이콘을 클릭하여 해당 사용자의 권한을 편집
사용자 이름 2 에는 현재 프로젝트 내의 모든 리소스에 대한 읽기 액세스 권한을 제공하는 뷰어 역할이 있으며 다른 역할 추가를 클릭하여 역할에 대한 다른 드롭다운 선택 항목을 추가
역할 선택 드롭다운 상자 에서 Kubernetes Engine > Kubernetes Engine 클러스터 관리자를 선택 후 저장
참고: 이제 사용자 이름 2는 프로젝트의 모든 GKE 클러스터를 관리하고
해당 클러스터 내의 리소스를 관리할 수 있는 액세스 권한을 가진다.
이 액세스 수준이 조직에 너무 광범위하면 Kubernetes 역할 기반 액세스 제어를 사용하여
GKE 클러스터 내에서 사용자 권한을 제한할 수 있다.
사용자 이름 2 Google Cloud Console 탭 으로 다시 전환
사용자 이름 2 로 로그인한 상태에서 탐색 메뉴 ( 탐색 메뉴 아이콘) 에서 Kubernetes Engine > 클러스터 를 클릭
이제 클러스터 생성 옵션이 활성화된 것을 볼 수 있다.
만들기를 클릭하여 GKE 클러스터 만들기를 시작합니다.
표준에 대한 구성 옵션 선택
클러스터 이름을 standard-cluster-1 로 설정
리전이 아닌 영역 클러스터 선택
us-central1-a 영역을 선택
다른 모든 값은 기본값으로 두고 만들기를 클릭
클러스터가 프로비저닝을 시작하지만 곧 실패하는데 사용자 이름 2에는 여전히 클러스터를 배포하는 데 필요한 일부 권한이 없다.
이는 GKE가 노드에 Google Cloud Compute Engine 인스턴스를 활용하기 때문입니다.
GKE 클러스터를 배포하려면 사용자에게 Compute Engine 기본 서비스 계정에 대한 iam.serviceAccountUser 역할도 할당되어야 한다.
이제 사용자 이름 2가 GKE 클러스터를 성공적으로 배포할 수 있도록 IAM을 사용하여 사용자 이름 2에 iam.serviceAccountUser 역할을 부여해야 한다.
사용자 이름 1 Google Cloud Console 탭 으로 다시 전환
탐색 메뉴 ( 탐색 메뉴 아이콘) 에서 IAM & admin > 서비스 계정을 클릭
IAM 콘솔 에서 Compute Engine 기본 서비스 계정 에 해당하는 행을 클릭하여 선택
권한을 클릭하여 권한 정보 패널을 열어 권한 페이지에서 액세스 권한 부여 를 클릭
새 주체 상자에 사용자 이름 2 의 사용자 이름을 입력
역할 선택 상자 에서 서비스 계정 > 서비스 계정 사용자(Service Account User)가 선택되어 있는지 확인하고 저장
사용자 이름 2 Google Cloud Console 탭 으로 다시 전환
사용자 이름 2 로 로그인한 상태에서 탐색 메뉴 (탐색 메뉴 아이콘) 에서 Kubernetes Engine > 클러스터 를 클릭
표준에 대한 구성 옵션 선택
클러스터 이름을 standard-cluster-1 로 설정
리전이 아닌 영역 클러스터가 선택되었는지 확인
us-central1-a 영역을 선택
다른 모든 값은 기본값으로 두고 만들기를 클릭
export my_zone=us-central1-a
export my_cluster=standard-cluster-1
source <(kubectl completion bash)
gcloud container clusters get-credentials $my_cluster --zone $my_zone
PodSecurity 승인 컨트롤러를 사용하려면 특정 모드에서 특정 네임스페이스에 특정 Pod 보안 표준을 적용해야 한다.
kubectl create ns baseline-ns
kubectl create ns restricted-ns
baseline-ns: 허용 워크로드의 경우
limited-ns: 고도로 제한된 워크로드용
Pod 보안 표준을 적용
baseline: 경고 모드에서 기준선-ns에 적용
limited: 강제 모드에서 limited-ns에 적용
kubectl label --overwrite ns baseline-ns pod-security.kubernetes.io/warn=baseline
kubectl label --overwrite ns restricted-ns pod-security.kubernetes.io/enforce=restricted
이러한 명령은 다음 결과를 얻는다.
레이블이 추가되었는지 확인하면
$ kubectl get ns --show-labels
NAME STATUS AGE LABELS
baseline-ns Active 47s kubernetes.io/metadata.name=baseline-ns,pod-security.kubernetes.io/warn=baseline
default Active 5m24s kubernetes.io/metadata.name=default
gmp-public Active 4m41s addonmanager.kubernetes.io/mode=Reconcile,kubernetes.io/metadata.name=gmp-public
gmp-system Active 4m41s addonmanager.kubernetes.io/mode=Reconcile,kubernetes.io/metadata.name=gmp-system
kube-node-lease Active 5m24s kubernetes.io/metadata.name=kube-node-lease
kube-public Active 5m24s kubernetes.io/metadata.name=kube-public
kube-system Active 5m24s kubernetes.io/metadata.name=kube-system
restricted-ns Active 46s kubernetes.io/metadata.name=restricted-ns,pod-security.kubernetes.io/enforce=restricted
PodSecurity 승인 컨트롤러가 의도한 대로 작동하는지 확인하려면 기준 및 제한된 정책을 위반하는 워크로드를 두 네임스페이스 모두에 배포한다.
다음 예제 매니페스트는 권한 에스컬레이션을 허용하는 nginx 컨테이너를 배포하는 매니페스트 파일이다.
$ nano psa-workload.yaml
student_03_8f64b7958f47@cloudshell:~ (qwiklabs-gcp-00-99785c58bd47)$ cat psa-workload.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
### 기준선 정책은 Pod가 네임스페이스에 배포되도록 허용
$ kubectl apply -f psa-workload.yaml --namespace=baseline-ns
Warning: would violate PodSecurity "baseline:latest": privileged (container "nginx" must not set securityContext.privileged=true)
pod/nginx created
student_03_8f64b7958f47@cloudshell:~ (qwiklabs-gcp-00-99785c58bd47)$ kubectl get pods --namespace=baseline-ns -l=app=nginx
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 18s
### 포드는 네임스페이스에 배포되지 않으며 감사 항목이 로그에 추가됨
$ kubectl apply -f psa-workload.yaml --namespace=restricted-ns
Error from server (Forbidden): error when creating "psa-workload.yaml": pods "nginx" is forbidden: violates PodSecurity "restricted:latest": privileged (container "nginx" must not set securityContext.privileged=true), allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
resource.type="k8s_cluster"
protoPayload.response.reason="Forbidden"
protoPayload.resourceName="core/v1/namespaces/restricted-ns/pods/nginx"
클러스터에서 IP 및 자격 증명 순환을 수행하는데 자격 증명 수명을 줄이기 위해 정기적으로 수행하는 것이 안전한 방법이다.
서빙 IP와 사용자 인증 정보를 회전하는 별도의 명령이 있지만 사용자 인증 정보를 회전하면 IP도 추가로 회전한다.
gcloud container clusters update $my_cluster --zone $my_zone --start-credential-rotation
-> Y(yes)
Cloud Shell에서 명령이 완료되면 클러스터는 각 노드를 업데이트하는 프로세스를 시작하는데 15분 정도 걸릴 수 있다.
또한 이 프로세스는 현재 사용자의 kubeconfig 항목을 자동으로 업데이트하며 이제 클러스터 마스터는 원래 주소 외에 새 IP 주소를 일시적으로 제공한다.
gcloud container clusters update $my_cluster --zone $my_zone --complete-credential-rotation
이렇게 하면 순환 프로세스가 완료되고 원래 클러스터 IP 주소가 제거된다.
gcloud container clusters upgrade $my_cluster --node-pool=default-pool --zone $my_zone
-> Y(yes)
gcloud container clusters update $my_cluster --zone $my_zone --complete-credential-rotation
이번엔 GKE 클러스터 내에 네임스페이스를 만든 다음역할 기반 액세스 제어(RBAC)
를 사용하여 관리자가 아닌 사용자가 특정 네임스페이스에서 Pod를 사용할 수 있도록 허용해보자.
사용자가 클러스터 리소스에 대한 액세스를 제어할 수 있도록 네임스페이스 생성
네임스페이스 내에서 액세스를 제어하는 역할 및 RoleBinding
생성
사용자 계정 1 과 사용자 계정 2 콘솔에 로그인하여 Cloud Shell 활성화
export my_zone=us-central1-a
export my_cluster=standard-cluster-1
source <(kubectl completion bash)
gcloud container clusters get-credentials $my_cluster --zone $my_zone
git clone https://github.com/GoogleCloudPlatform/training-data-analyst
ln -s ~/training-data-analyst/courses/ak8s/v1.1 ~/ak8s
cd ~/ak8s/RBAC/
$ cat my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production
$ kubectl apply -f my-namespace.yaml
$ kubectl get namespaces
NAME STATUS AGE
default Active 6m
kube-node-lease Active 6m
kube-public Active 6m
kube-system Active 6m
production Active 4s
$ kubectl describe namespaces production
Name: production
Labels:
Annotations:
Status: Active
Resource Quotas
Name: gke-resource-quotas
Resource Used Hard
-------- --- ---
count/ingresses.extensions 0 100
count/jobs.batch 0 5k
pods 0 1500
services 0 500
No LimitRange resource.
$ cat my-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
$ kubectl apply -f ./my-pod.yaml --namespace=production
$ kubectl get pods --namespace=production
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 20s
이 작업에서는 샘플 사용자 지정 역할을 만든 다음 Username 2 에게 프로덕션 네임스페이스의 편집자 역할을 부여하는 RoleBinding을 만든다.
역할은 pod-reader-role.yaml제공된 파일에 정의되어 있으며 pod-reader이 매니페스트는 네임스페이스 의 포드 객체에 대한 생성, 가져오기, 나열 및 감시 권한을 제공하는 라는 production 역할을 정의할 수 있으며 이 역할은 포드를 삭제할 수 없다.
$ cat pod-reader-role.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: production
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "get", "list", "watch"]
역할을 생성하려면 계정에 할당되는 역할에 부여된 권한이 있어야 합니다. 클러스터 관리자의 경우 다음 RoleBinding을 생성하여 자신의 사용자 계정에 클러스터 관리자 역할을 부여하면 이 작업을 쉽게 수행할 수 있습니다.
사용자 이름 1 계정에 클러스터 관리자 권한을 부여하려면 다음 명령을 실행하여 사용자 이름 1 계정 [USERNAME_1_EMAIL]
의 이메일 주소로 바꾼다
$ kubectl create clusterrolebinding cluster-admin-binding --clusterrole cluster-admin --user [USERNAME_1_EMAIL]
$ kubectl apply -f pod-reader-role.yaml
$ kubectl get roles --namespace production
NAME AGE
Pod-reader 3m
역할은 권한을 할당하는 데 사용되지만 그 자체로는 아무 작업도 수행하지 않으며 역할은 RoleBinding에서 수행되는 사용자 및 개체에 바인딩되어야 한다.
username2-editor-binding.yaml 매니페스트 파일은 이전에 생성한 역할 에 대한 두 번째 랩 사용자에 대해 호출된 RoleBinding을 생성한다.
해당 역할 username2-editorpod-reader은 Pod를 만들고 볼 수 있지만 삭제할 수는 없다.
$ cat username2-editor-binding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: username2-editor
namespace: production
subjects:
- kind: User
name: [USERNAME_2_EMAIL] ### 사용자이름 2 의 메일주소로 바꿔야함
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
$ export USER2=[USERNAME_2_EMAIL]
$ sed -i "s/\[USERNAME_2_EMAIL\]/${USER2}/" username2-editor-binding.yaml
나중에 이 RoleBinding 을 적용할 것이다.
이제 Username 2가 Manifest 파일(production-pod.yaml)을 사용하여 Pod를 생성하기 위해 Username 2를 사용하여 프로덕션 네임스페이스에서 Pod를 생성할 수 있는지 여부를 테스트한다.
이 매니페스트는 단일 nginx 컨테이너로 간단한 포드를 배포하는 매니페스트 파일이다.
$ cat production-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: production-pod
labels:
name: production-pod
namespace: production
spec:
containers:
- name: production-pod
image: nginx
ports:
- containerPort: 8080
$ export my_zone=us-central1-a
$ export my_cluster=standard-cluster-1
$ source <(kubectl completion bash)
$ gcloud container clusters get-credentials $my_cluster --zone $my_zone
$ git clone https://github.com/GoogleCloudPlatform/training-data-analyst
$ ln -s ~/training-data-analyst/courses/ak8s/v1.1 ~/ak8s
$ cd ~/ak8s/RBAC/
$ kubectl get namespaces
NAME STATUS AGE
default Active 29m
kube-node-lease Active 29m
kube-public Active 29m
kube-system Active 29m
production Active 23m
Cloud Shell에서 다음 명령을 실행하여 production이라는 네임스페이스에 리소스를 생성
$ kubectl apply -f ./production-pod.yaml
사용자 이름 2에 포드를 생성할 수 있는 올바른 권한이 없음을 나타내는 오류가 발생된다.
사용자 이름 2는 아직 해당 계정에 다른 역할을 바인딩하지 않았기 때문에 이 시점에서는 뷰어 권한만 가진다 이제 롤바인딩을 통해 바꿔볼 것이다.
$ kubectl apply -f username2-editor-binding.yaml
$ kubectl get rolebinding --namespace production
NAME AGE
username2-editor 23s
$ kubectl apply -f ./production-pod.yaml
$ kubectl get pods --namespace production
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 16m
production-pod 1/1 Running 0 20s
$ kubectl delete pod production-pod --namespace production
### 사용자 이름 2에 포드에 대한 삭제 권한이 없기 때문에 실패