Cloud/Kubernates

# k8s Pod Security Policy 적용 - 퍼블릭 클라우드 환경을 사용하지 않을 때

skysoo1111 2020. 2. 4. 14:48

Pod Security Policy (이하 PSP) 란?

k8s에는 SecurityContext라는 기능이 존재한다. SecurityContext는 Container의 보안 기능을 정의하는 것인데, 개인적으로 PSP는 이 SecurityContext의 확장판이라고 생각한다.

 

PSP(포드 보안 정책)은 포드 사양의 보안 관련 측면을 제어하는 ​​클러스터 수준 리소스이다. PSP 객체는 관련 필드에 대한 기본값 뿐만 아니라 시스템에 적용하기 위해 포드가 실행해야 하는 조건 세트를 정의한다. 관리자는 다음을 제어 할 수 있다.

                                 제어 목록                                   필드명
Container의 실행 권한 privileged
호스트 네임 스페이스의 사용 hostPID, hostIPC
호스트 네트워킹 및 포트의 사용 hostNetwork, hostPorts
볼륨 유형의 사용법 volumes
호스트 파일 시스템의 사용 allowedHostPaths
FlexVolume 드라이버의 화이트리스트 allowedFlexVolumes
포드의 볼륨을 소유 FSGroup 할당 fsGroup
읽기 전용 루트 파일 시스템의 사용을 요구하는 readOnlyRootFilesystem
Container의 사용자와 그룹 ID runAsUser, runAsGroup, supplementalGroups
루트 권한을 제한 에스컬레이션 allowPrivilegeEscalation, defaultAllowPrivilegeEscalation
리눅스 기능 defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities
Container의 SE 리눅스 문맥 seLinux
컨테이너에 대한 허용 PROC 마운트 타입 allowedProcMountTypes
Container에서 사용되는 AppArmor의 프로파일 annotations
Container에서 사용되는 프로파일 기는 seccomp annotations
Container에서 사용되는 프로파일의 sysctl forbiddenSysctls,allowedUnsafeSysctls

 

즉 PSP는 여러 제어 기능으로 컨테이너가 불필요하게 과도한 권한을 가지는 것을 정책적으로 제어하는 것이 목적이라고 할 수 있겠다.

 

PSP 적용 - 클러스터 내 ROOT 권한 컨테이너 기동 제한

여러가지 PSP의 제어 기능 중, ROOT 권한으로 컨테이너를 기동하는 것을 막는 기능을 직접 설정해보고 테스트 해 볼 것이다.

 

환경 - 필자는 Gcloud나 AWS Cloud와 같은 퍼블릭 클라우드 환경이 아닌 물리 서버에 직접 k8s를 구축하였기 때문에 퍼블릭 클라우드 환경에서의 PSP 활성화 방식과 다르다. (퍼블릭 클라우드는 버튼 하나면 PSP 기능을 활성화 할 수 있다.)

PSP 적용 절차

  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다한국어 -> 영어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다한국어 -> 영어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다한국어 -> 영어...
       
    • 새로운 단어 목록 생성...
  • 복사

Step 1. NonRoot PSP 정의

  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다한국어 -> 영어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사
$ vi 1.nonroot-psp.yaml
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: nonroot-psp
spec:
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: MustRunAsNonRoot
#    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'

Step 2. 서비스 계정 생성

$ 2.nonroot-sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nonroot-sa
 

Step 3. ClusterRole 생성

$ 3.nonroot-clusterrole.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nonroot-clusterrole
rules:
- apiGroups:
  - policy
  resources:
  - podsecuritypolicies
  resourceNames:
  - nonroot-psp
  verbs:
  - use

 

Step 4. ClusterRoleBinding 생성

$ 4.nonroot-clusterrolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: nonroot-clusterrole-bindings
subjects:
- kind: ServiceAccount
  name: nonroot-sa
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nonroot-clusterrole

 

Step 5. k8s PSP with RBAC 설정 적용

$ kubectl apply -f default-psp-with-rbac.yaml
$ kubectl create -f nonroot-sa.yaml
$ kubectl apply -f nonroot-clusterrole.yaml
$ kubectl apply -f nonroot-clusterrolebinding.yaml


# PSP,RBAC 및 서비스 계정 기동 확인
$ kubectl get psp
$ kubectl get sa
$ kubectl get clusterrole
$ kubectl get clusterrolebinding

Step 6. Admission Controller 활성화

k8s apiserver를 수정해줘야 한다.

$ vi /etc/kubernetes/manifests/kube-apiserver.yaml

$ kubectl apply -f kube-apiserver.yaml
$ kubectl get pods -n kube-system

 

Step 7. PSP 적용 Deployment 생성

$ vi 1.hello-deployment.yaml

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: hello-deployment
spec:
  replicas: 3
  minReadySeconds: 5
  selector:
    matchLabels:
      app: hello-deployment
  template:
    metadata:
      name: hello-deployment-pod
      labels:
        app: hello-deployment
    spec:
      serviceAccountName: nonroot-sa
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: hello-deployment
        image: 192.168.10.7:5000/pss/hello:v1
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hello-deployment-svc
spec:
  selector:
    app: hello-deployment
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 8080
  type: LoadBalancer
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사

 

※ 중요

NonRoot로 기동할 것이기 때문에 서비스 계정(serviceAccountName)뿐만 아니라 securityContext에서 기동할 User를 반드시 명시해줘야 한다.

 

Step 8. 기동 테스트

# 기동
$ kubectl create -f 1.hello-deployment.yaml

# 확인
$ kubectl get pods

kubectl get pods 결과

# 컨테이너 진입
$ kubectl exec -it nonroot-deploy-56d47694df-8hnjh /bin/bash

컨테이너 진입 결과

=> 생성된 파일들이 root가 아닌 appuser라는 것을 확인

( 사용자 pid 1000을 사용했다면 node가 사용자 계정이 될 것이다. 여기서 appuser는 docker 이미지 빌드시 필자가 지정해준 사용자이다. )

 

-끝-

============================================================================

 

만약 Step 6. 에서 Admission Controller가 활성화 되지 않고 kube-apiserver pod가 pending이나 error상태라면 아래 default PSP를 먼저 생성해주고 다시 Admission Controller를 적용해봐라.

 

default PSP 정의

$ vi 0.default-psp-with-rbac.yaml

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  annotations:
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
  name: default
spec:
  allowedCapabilities: []  # default set of capabilities are implicitly allowed
  allowPrivilegeEscalation: false
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  hostIPC: false
  hostNetwork: false
  hostPID: false
  privileged: false
  readOnlyRootFilesystem: false
  runAsUser:
    rule: 'MustRunAsNonRoot'
  seLinux:
    rule: 'RunAsNonRoot'
  supplementalGroups:
    rule: 'RunAsNonRoot'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  volumes:
  - 'configMap'
  - 'downwardAPI'
  - 'emptyDir'
  - 'persistentVolumeClaim'
  - 'projected'
  - 'secret'
  hostNetwork: false
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

---

# Cluster role which grants access to the default pod security policy
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: default-psp
rules:
- apiGroups:
  - policy
  resourceNames:
  - default
  resources:
  - podsecuritypolicies
  verbs:
  - use

---

# Cluster role binding for default pod security policy granting all authenticated users access
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: default-psp
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: default-psp
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:authenticated
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다한국어 -> 영어...
       
    • 새로운 단어 목록 생성...
  • 복사
$ kubectl create -f 0.default-psp-with-rbac.yaml
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사
  • 단어장에 추가
     
    • 다음에 대한 단어 목록이 없습니다영어 -> 한국어...
       
    • 새로운 단어 목록 생성...
  • 복사