본문 바로가기

Cloud/Kubernetes

쿠버네티스 인 액션 스터디 - 12장

 책이 집에 있어서 원문 PDF로 공부공부...

12. Securing the Kubernetes API server

- Understanding authentication

- What ServiceAccounts are and why they're used

- Understanding the role-based access control (RBAC) plugin

- Using Roles and RoleBindings

- Using ClusterRoles and ClusterRoleBindings

- Understanding the default roles and bindings

 

12.1 Understanding authentication

 Kubernetes의 API 서버는 인증 플러그인을 사용하여 클라이언트의 신원을 보증한다.

  인증 방법

- Client Certificate

- Authentication token passed in an HTTP header

- HTTP authentication

- Others

 인증을 완료하면 사용자의 사용자 이름과 그룹을 반환하며 Kubernetes는 해당 정보를 저장해 두지 않는다. Kubernetes는 두 종류의 클라이언트를 구분한다.

- Actual humans (User) : SSO와 같은 외부 시스템에서 권한 관리

- Pods (running inside them) : serviceaccount를 사용하여 권한 관리

 유저는 리소스가 아니기 때문에 API 서버를 통해 사용자를 생성, 업데이트, 삭제할 수 없다. 이 장에서는 ServiceAccounts에 대해 살펴본다. User와 ServiceAccount (이하 SA)는 하나 이상의 그룹에 속할 수 있으며 인증 시 이름과 그룹을 같이 반환한다. 그룹은 여러 사용자에게 한 번에 권한을 부여하는 데 사용되지만 권한은 개별적으로 부여된다. 그룹은 임의적이지만 (정하기 나름이지만) 기본적으로 제공되는 그룹은 의미를 가지고 있다.

- system:unauthenticated -> 인증되지 않은 사용자

- system:authenticated ->  인증된 사용자

- system:serviceaccounts ->  모든 서비스 계정

- system:serviceaccounts:<namespace> -> 특정 네임스페이스의 모든 서비스 계정

 그룹은 Rolebinding이나 ClusterRoleBinding에 바인딩되며 subject 아래에 반영된다. 예를 들면, 아래와 같은 rolebinding이 있다면 .subjects.kind에 Group이라는 kind로 바인딩되는 것이다.

apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role #this must be Role or ClusterRole
  name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io

 이런 식으로 바인딩된다.

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
  
subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io
  
subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io
 
subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

 

 Pod는 생성될 때 /var/run/secrets/kubernetes.io/serviceaccount 볼륨을 마운트 하며 올라가며, 여기에 SA의 토큰 값을 가지고 있다. Pod는 해당 SA의 토큰을 통해 API 서버에 연결되며 인증 플러그인이 SA를 인증해준다. SA의 이름은 아래와 같은 형식을 가진다.

system:serviceaccount:<namespace>:<service account name>

 아무런 옵션 없이 Pod를 생성한 다음, Yaml을 보면 default-token 값이 있는 것을 볼 수 있다.

kind: Pod
apiVersion: v1
metadata:
  name: kubernetes-test-downward
spec:
  containers:
    - resources: {}
      name: client-container
      command:
        - sh
        - '-c'
      imagePullPolicy: Always
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
        - name: default-token-85mmr
          readOnly: true
          mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      terminationMessagePolicy: File
      image: k8s.gcr.io/busybox
      args:
        - >-
          while true; do if [[ -e /etc/podinfo/name ]]; then echo -en '\n\n';
          cat /etc/podinfo/name; fi; if [[ -e /etc/podinfo/namespace ]]; then
          echo -en '\n\n'; cat /etc/podinfo/namespace; fi; sleep 5; done;
  serviceAccount: default
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: name
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
          - path: namespace
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        defaultMode: 420
    - name: default-token-85mmr
      secret:
        secretName: default-token-85mmr
        defaultMode: 420

 API 서버는 Pod가 수행하려는 작업이 해당 SA가 수행할 수 있는지 판단한다. SA는 네임스페이스를 범위로 가지며 자동으로 생성된다(default). 필요한 경우 추가로 생성할 수 있으며 각 포드는 한 개의 SA만 사용할 수 있지만 한 개의 SA는 여러 포드와 이어질 수 있다. 또한 Pod는 동일한 네임스페이스에 위치한 SA만 사용이 가능하다.

 Pod에 SA를 지정할 수 있으며 지정하지 않으면 자동으로 default SA가 등록된다. SA를 등록하고 싶다면 아래와 같은 YAML을 사용하면 된다. SA는 반드시 파드 생성 이전에 생성되어야 한다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot

 네임스페이스는 자체적으로 deafult SA를 가지고 있지만 추가로 생성이 가능하다. 클러스터 보안을 위하여 default SA 대신 Pod는 제한된 권한을 가진 SA를 통해 실행되어야 한다. 전용 명령어를 통해 SA를 생성할 수 있다.

kubectl create serviceaccount [sa]

SA는 생성과 동시에 token 값을 secret으로 가진다. Secret의 데이터를 보면 CA 인증서, 네임스페이스, 토큰이 포함되어 있다.

[root@ ~]# kubectl get secret
NAME                                      TYPE                                  DATA   AGE
default-token-85mmr                       kubernetes.io/service-account-token   3      63d

SA는 프라이밋 이미지 저장소에서 컨테이너 이미지를 가져오기 위한 자격 증명 또한 포함할 수 있다. 이미지 자격증명을 가지고 있는 SA를 사용하면 Pod에 일일이 해당 이미지 시크릿을 추가하지 않아도 된다.

 특정 SA를 통해 Pod를 생성하고 해당 파드가 API 서버와 통신할 수 있는지 알아본다.

kubectl create sa study

apiVersion: v1
kind: Pod
metadata:
  name: curl-custom-sa
  namespace: tsis
spec:
  serviceAccountName: study
  containers:
  - name: main
    image: tutum/curl
    command: ["sleep", "9999999"]
  - name: ambassador
    image: luksa/kubectl-proxy:1.6.2

파드 내부에 접속 한 다음 token 값을 살펴본다.

# cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCIBsT3p1V3lnNzNolLWFjW50OnRzaXM6c3R1ZHkifQ~

파드 내부에 접속한 다음 curl 명령어를 통해 내부 API 서버에 접속할 수 있는지 확인한다.

# curl localhost:8001/api/v1/pods
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "pods is forbidden: User \"system:serviceaccount:default:study\" cannot list resource \"pods\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "pods"
  },
  "code": 403
}

 권한을 부여하지 않았기 때문에 접근은 되지만 403 에러가 나타나는 것을 볼 수 있다. 이제 RBAC를 통해 추가한 SA에 적당한 룰을 내려본다.

12.2 Securing the cluster with role-based access control

Kubernetes 1.6.0 부터 클러스터 보안이 향상되었으며 1.8.0부터는 RBAC가 일반 공급으로 전환되면서 많은 클러스터에서 기본적으로 활성화된다. RBAC는 권한이 없는 사용자가 클러스터 상태를 보거나 수정하는 것을 방지하며 기본 SA는 추가 권한을 부여하지 않는 한 클러스터의 상태를 볼 수 없다. Kubernetes API 서버와 통신하는 앱을(Pod를) 작성하려면 RBAC를 통해 권한 부여 및 관리 방법에 대해 알아야한다.

(1) Introducing the RBAC authorization plugin

 REST API는 GET, POST, PUT, DELETE 등의 HTTP 요청을 특정 REST Resource 로 보내고 Kubernetes에서는 Pod, Service, Secrets 등등이 있다. RBAC는 클라이언트가 해당 리소스에 대해 동작을 수행할 수 있는지 판단한다. RBAC는 전체 리소스 유형 외에도 특정 인스턴스에 적용할 수 있다.

 RBAC는 User, SA와 연결되어 각 역할이 권한을 행사할 수 있도록 결정해준다.

 (2) Inroducing RBAC resources

 RBAC는 4개의 리소스를 통해 구성되고 두 그룹으로 그룹화 할 수 있다. 단일 네임스페이스 여러 롤/롤 바인딩이 존재할 수 있으며 단일 클러스터에도 여러 클러스터 롤/클러스터 롤 바인딩이 존재할 수 있다. 

- Role / ClusterRole

 - RoleBindings / ClusterRoleBindings

 책에서는 실습을 통해 이해하자고 했는데 많이 해봐서 스킵한다. RBAC가 적용되어 있는지는 /etc/kubernetes/manifests/kube-apiserver.yaml 아래에서 --authorization-mode=RBAC가 존재하는지 확인하면 된다.

 기본적으로 존재하는 clusterRole과 clusterRoleBinding은 아래와 같다. (버전별로 차이가 있을 수 있음)

12.3 Summary

12장에서는 이런 것들을 배웠다.

- API 서버의 클라이언트는 Pod와 User가 있다.

- Pod는 SA와 연결된다.

- 사용자와 SA는 모두 Group과 연결되어 있다.

- 기본적으로 Pod는 각 네임스페이스에 대해 미리 존재하는 SA를 통해 생성된다.

- 추가 SA는 수동으로 생성하고 Pod와 연결할 수 있다.

- SA를 사용하여 레지스트리 시크릿을 첨부할 수 있다.

- RBAC는 특정 리소스에 대해 수행할 수 있는 작업을 정의한다.

- RoleBindings과 ClusterRoleBindings는 Role과 ClusterRoles를 사용자, 그룹, SA에 바인딩한다.

- 각 클러스터는 ClusterRoles와 ClusterRoleBindings을 기본으로 제공한다.