파드 시큐리티 스탠다드를 클러스터 수준에 적용하기

파드 시큐리티 어드미션(PSA, Pod Security Admission)은 베타로 변경되어 v1.23 이상에서 기본적으로 활성화되어 있다. 파드 시큐리티 어드미션은 파드가 생성될 때 파드 시큐리티 스탠다드(Pod Security Standards)를 적용하는 어드미션 컨트롤러이다. 이 튜토리얼은 baseline 파드 시큐리티 스탠다드를 클러스터 수준(level)에 적용하여 표준 구성을 클러스터의 모든 네임스페이스에 적용하는 방법을 보여 준다.

파드 시큐리티 스탠다드를 특정 네임스페이스에 적용하려면, 파드 시큐리티 스탠다드를 네임스페이스 수준에 적용하기를 참고한다.

만약 쿠버네티스 버전이 v1.31이 아니라면, 해당 버전의 문서를 확인하자.

시작하기 전에

워크스테이션에 다음을 설치한다.

적용할 알맞은 파드 시큐리티 스탠다드 선택하기

파드 시큐리티 어드미션을 이용하여 enforce, audit, 또는 warn 모드 중 하나로 내장 파드 시큐리티 스탠다드를 적용할 수 있다.

현재 구성에 가장 적합한 파드 시큐리티 스탠다드를 고르는 데 도움이 되는 정보를 수집하려면, 다음을 수행한다.

  1. 파드 시큐리티 스탠다드가 적용되지 않은 클러스터를 생성한다.

    kind create cluster --name psa-wo-cluster-pss --image kindest/node:v1.24.0
    

    다음과 비슷하게 출력될 것이다.

    Creating cluster "psa-wo-cluster-pss" ...
    ✓ Ensuring node image (kindest/node:v1.24.0) 🖼
    ✓ Preparing nodes 📦  
    ✓ Writing configuration 📜
    ✓ Starting control-plane 🕹️
    ✓ Installing CNI 🔌
    ✓ Installing StorageClass 💾
    Set kubectl context to "kind-psa-wo-cluster-pss"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-psa-wo-cluster-pss
    
    Thanks for using kind! 😊
    
  2. kubectl context를 새로 생성한 클러스터로 설정한다.

    kubectl cluster-info --context kind-psa-wo-cluster-pss
    

    다음과 비슷하게 출력될 것이다.

     Kubernetes control plane is running at https://127.0.0.1:61350
    
    CoreDNS is running at https://127.0.0.1:61350/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    
  3. 클러스터의 네임스페이스 목록을 조회한다.

    kubectl get ns
    

    다음과 비슷하게 출력될 것이다.

    NAME                 STATUS   AGE
    default              Active   9m30s
    kube-node-lease      Active   9m32s
    kube-public          Active   9m32s
    kube-system          Active   9m32s
    local-path-storage   Active   9m26s
    
  4. --dry-run=server를 사용하여 다른 파드 시큐리티 스탠다드가 적용되었을 때 어떤 것이 변경되는지 확인한다.

    1. Privileged
      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=privileged
      

    다음과 비슷하게 출력될 것이다.

    namespace/default labeled
    namespace/kube-node-lease labeled
    namespace/kube-public labeled
    namespace/kube-system labeled
    namespace/local-path-storage labeled
    
    1. Baseline
      kubectl label --dry-run=server --overwrite ns --all \
      pod-security.kubernetes.io/enforce=baseline
      

    다음과 비슷하게 출력될 것이다.

    namespace/default labeled
    namespace/kube-node-lease labeled
    namespace/kube-public labeled
    Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "baseline:latest"
    Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes
    Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes
    Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged
    namespace/kube-system labeled
    namespace/local-path-storage labeled
    
    1. Restricted
     kubectl label --dry-run=server --overwrite ns --all \
     pod-security.kubernetes.io/enforce=restricted
    

    다음과 비슷하게 출력될 것이다.

    namespace/default labeled
    namespace/kube-node-lease labeled
    namespace/kube-public labeled
    Warning: existing pods in namespace "kube-system" violate the new PodSecurity enforce level "restricted:latest"
    Warning: coredns-7bb9c7b568-hsptc (and 1 other pod): unrestricted capabilities, runAsNonRoot != true, seccompProfile
    Warning: etcd-psa-wo-cluster-pss-control-plane (and 3 other pods): host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true
    Warning: kindnet-vzj42: non-default capabilities, host namespaces, hostPath volumes, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
    Warning: kube-proxy-m6hwf: host namespaces, hostPath volumes, privileged, allowPrivilegeEscalation != false, unrestricted capabilities, restricted volume types, runAsNonRoot != true, seccompProfile
    namespace/kube-system labeled
    Warning: existing pods in namespace "local-path-storage" violate the new PodSecurity enforce level "restricted:latest"
    Warning: local-path-provisioner-d6d9f7ffc-lw9lh: allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfile
    namespace/local-path-storage labeled
    

위의 출력에서, privileged 파드 시큐리티 스탠다드를 적용하면 모든 네임스페이스에서 경고가 발생하지 않는 것을 볼 수 있다. 그러나 baselinerestricted 파드 시큐리티 스탠다드에 대해서는 kube-system 네임스페이스에서 경고가 발생한다.

모드, 버전, 및 파드 시큐리티 스탠다드 설정

이 섹션에서는, 다음의 파드 시큐리티 스탠다드를 latest 버전에 적용한다.

  • baseline 파드 시큐리티 스탠다드는 enforce 모드로 적용
  • restricted 파드 시큐리티 스탠다드는 warnaudit 모드로 적용

baseline 파드 시큐리티 스탠다드는 예외 목록을 간결하게 유지하고 알려진 권한 상승(privilege escalations)을 방지할 수 있는 편리한 절충안을 제공한다.

추가적으로, kube-system 내의 파드가 실패하는 것을 방지하기 위해, 해당 네임스페이스는 파드 시큐리티 스탠다드가 적용되지 않도록 제외할 것이다.

사용 중인 환경에 파드 시큐리티 어드미션을 적용할 때에는 다음의 사항을 고려한다.

  1. 클러스터에 적용된 위험 상태에 따라, restricted와 같은 더 엄격한 파드 시큐리티 스탠다드가 더 좋을 수도 있다.

  2. kube-system 네임스페이스를 적용 대상에서 제외하면 이 네임스페이스의 파드가 privileged로 실행될 수 있다. 실제 사용 환경에서는, 최소 권한 원칙을 준수하도록, 접근을 kube-system 네임스페이스로 제한하는 엄격한 RBAC 정책을 적용할 것을 강력히 권장한다.

  3. 파드 시큐리티 어드미션 컨트롤러가 이러한 파드 시큐리티 스탠다드를 구현하는 데 사용할 수 있는 구성 파일을 생성한다.

    mkdir -p /tmp/pss
    cat <<EOF > /tmp/pss/cluster-level-pss.yaml 
    apiVersion: apiserver.config.k8s.io/v1
    kind: AdmissionConfiguration
    plugins:
    - name: PodSecurity
      configuration:
        apiVersion: pod-security.admission.config.k8s.io/v1
        kind: PodSecurityConfiguration
        defaults:
          enforce: "baseline"
          enforce-version: "latest"
          audit: "restricted"
          audit-version: "latest"
          warn: "restricted"
          warn-version: "latest"
        exemptions:
          usernames: []
          runtimeClasses: []
          namespaces: [kube-system]
    EOF
    
  4. API 서버가 클러스터 생성 과정에서 이 파일을 처리할 수 있도록 구성한다.

    cat <<EOF > /tmp/pss/cluster-config.yaml 
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      kubeadmConfigPatches:
      - |
        kind: ClusterConfiguration
        apiServer:
            extraArgs:
              admission-control-config-file: /etc/config/cluster-level-pss.yaml
            extraVolumes:
              - name: accf
                hostPath: /etc/config
                mountPath: /etc/config
                readOnly: false
                pathType: "DirectoryOrCreate"
      extraMounts:
      - hostPath: /tmp/pss
        containerPath: /etc/config
        # optional: if set, the mount is read-only.
        # default false
        readOnly: false
        # optional: if set, the mount needs SELinux relabeling.
        # default false
        selinuxRelabel: false
        # optional: set propagation mode (None, HostToContainer or Bidirectional)
        # see https://kubernetes.io/ko/docs/concepts/storage/volumes/#마운트-전파-propagation
        # default None
        propagation: None
    EOF
    
  5. 이러한 파드 시큐리티 스탠다드를 적용하기 위해 파드 시큐리티 어드미션을 사용하는 클러스터를 생성한다.

     kind create cluster --name psa-with-cluster-pss --image kindest/node:v1.24.0 --config /tmp/pss/cluster-config.yaml
    

    다음과 비슷하게 출력될 것이다.

     Creating cluster "psa-with-cluster-pss" ...
      ✓ Ensuring node image (kindest/node:v1.24.0) 🖼 
      ✓ Preparing nodes 📦  
      ✓ Writing configuration 📜 
      ✓ Starting control-plane 🕹️ 
      ✓ Installing CNI 🔌 
      ✓ Installing StorageClass 💾 
     Set kubectl context to "kind-psa-with-cluster-pss"
     You can now use your cluster with:
    
     kubectl cluster-info --context kind-psa-with-cluster-pss
    
     Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
    
  6. kubectl context를 새로 생성한 클러스터로 설정한다.

     kubectl cluster-info --context kind-psa-with-cluster-pss
    

    다음과 비슷하게 출력될 것이다.

     Kubernetes control plane is running at https://127.0.0.1:63855
     CoreDNS is running at https://127.0.0.1:63855/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
     To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    
  7. 기본 네임스페이스에 생성할 최소한의 구성에 대한 파드 명세를 다음과 같이 생성한다.

    cat <<EOF > /tmp/pss/nginx-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
    spec:
      containers:
        - image: nginx
          name: nginx
          ports:
            - containerPort: 80
    EOF
    
  8. 클러스터에 해당 파드를 생성한다.

     kubectl apply -f /tmp/pss/nginx-pod.yaml
    

    다음과 비슷하게 출력될 것이다.

     Warning: would violate PodSecurity "restricted:latest": 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")
     pod/nginx created
    

정리하기

kind delete cluster --name psa-with-cluster-psskind delete cluster --name psa-wo-cluster-pss 명령을 실행하여 생성했던 클러스터를 삭제한다.

다음 내용

최종 수정 October 15, 2024 at 3:18 AM PST: Merge pull request #48346 from windsonsea/metricy (50a9341)