AWS 기술 블로그

Amazon EKS 환경에서 Pod Security Standard 구현하기

쿠버네티스로의 안전한 마이그레이션은 클러스터에 대한 의도하지 않은 설정 변경을 방지하는 것을 포함합니다. 의도하지 않은 설정 변경은 클러스터의 운영환경에 악영향을 끼치거나 심지어 클러스터 자체의 무결성을 위협할 수도 있습니다.

예를 들어, 적절하지 않은 보안 설정을 포함한 포드(Pod)는 의도하지 않은 클러스터 설정 변경의 대표적인 사례 중 하나입니다. 쿠버네티스에서는 이러한 포드에 대한 보안 설정을 통제하기 위해서 Pod Security Policy(PSP)라는 기능을 제공하고 있습니다.

PSP는 클러스터 내에서 포트가 생성되거나 업데이트 될 때 반드시 지켜야 하는 보안 설정들을 지정하는데 사용되는 기능입니다. 하지만 쿠버네티스가 1.21 이 되면서 PSP 는 기능적으로 더 이상 사용하지 않는 것으로 발표되었으며 1.25 버전에서는 쿠버네티스에서 완전히 삭제되게 됩니다.

쿠버네티스의 프로젝트 문서를 보시면 쿠버네티스에서 왜 PSP 를 더 이상 사용하지 않도록 결정했는지에 대한 이유들이 설명되어 있습니다. 이 문서에는 다양한 정보들이 포함되어 있지만 간단히 요점만 언급하면 PSP는 PSP를 사용하는 대부분의 사용자들에게 혼란을 초래하는 부분이 있었습니다.

그리고 이러한 헷갈리는 부분들이 결과적으로는 클러스터내에 의도하지 않은 수 많은 잘못된 설정들을 야기하게 되었습니다. 예를 들어, 클러스터가 지나치게 제한적으로 설정되거나 지나치게 허용하게 하는 설정으로 인해 관리자의 의도와는 다르게 결함을 포함하고 있거나  적절하게 보호되지 못하는 상태로 클러스터가 운영되게 되었습니다.

이런 상황이 발생하는 이유는 PSP가 적용되는 방식이 많은 사용자에게 직관적이지 않기 때문입니다. 또한, PSP에는 드라이런이나 감사모드처럼 운영 중인 클러스터의 영향도를 측정하면서 기존 클러스터에 좀 더 쉽고 안전하게 정책을 추가할 수 있는 기능들이 부족했습니다.

마지막으로 PSP 의 구현 방식의 제약사항으로 인하여 PSP를 클러스터의 기본값을 통해 활성화할 수 없었습니다. 이러한 점들이 결국 포드 보안을 위한 새롭고 사용자에게 좀 더 친화적인 솔루션의 필요성을 야기했다고 볼 수 있습니다. 그리고 사용자에게 필요한 새로운 솔루션은 효율성을 극대화하기 위하여 쿠버네티스에 내장된 형태로 제공되어야 했습니다.

이를 위해 쿠버네티스에서 PSP는 Pod Security Admission(PSA)로 교체되었습니다. PSA는 쿠버네티스에 내장된 Admission Controller로서 Pod Security Standards(PSS)에 정의된 보안 통제 항목을 구현하는데 사용됩니다. PSA와 PSS는 모두 쿠버네티스 1.23 버전에서 “Beta” 상태이며 Amazon Elastic Kubernetes Service(Amazon EKS) 1.23 버전에도 기본값으로 활성화되어 있습니다.

참고: 여러분들은 PSA 의 대체 솔루션으로 오픈 소스 커뮤니티에서 제공하는 Policy-as-Code(PaC) 솔루션을 오픈소스 형태로도 사용할 수 있습니다. PaC 솔루션과 관련하여 여러분의 환경에 맞는 적절한 솔루션을 선택하는 방법과 같은 좀 더 자세한 사항에 대해서는 이 블로그 시리즈를 참고하시기 바랍니다. (Policy-based countermeasures for Kubernetes Part 1 and Part 2).

PSA와 PaC는 동일한 클러스터내에 동시에 존재할 수 있기 때문에 쿠버네티스 사용자들은 포드 보안과 관련하여 PSA혹은 PaC를  필요에 따라 선택적으로 사용할 수 있습니다. Amazon EKS 모범사례에 언급되어 있는 것처럼 PSP 가 클러스터에서 이용할 수 없을 때까지는 PSA를 이용하여 보안 모범 사례를 구성하거나 PSP와 함께 PaC 솔루션을 이용하는 것이 권장됩니다. PSP에서 PSA로의 이관과 관련한 좀 더 자세한 사항에 대해서는 쿠버네티스의 공식문서를 참고하시기 바랍니다.

참고: 여러분의 클러스터에서 PSP를 사용하고 있는 포드를 식별하려면 아래의 kubectl 명령을 참고하시기 바랍니다.

kubectl get pod -A -o jsonpath='{range .items[?(@.metadata.annotations.kubernetes.io/psp)]}{.metadata.name}{“t”}{.metadata.annotations.kubernetes.io/psp}{“t”}{.metadata.namespace}{“n”}’

적용해보기

Pod Security Standards (PSS) 과 Pod Security Admission (PSA)

쿠버네티스 공식 문서의 PSS 관련 내용에 따르면 PSS 는 “보안 스펙트럼을 광범위하게 다루기 위해 세 가지 다른 정책을 정의합니다. 이러한 정책은 누적되며 매우 허용적인 것부터 매우 제한적인 것까지 다양합니다.”. 라고 정의되어 있습니다.

그리고 정책의 레벨에 대해서는 아래와 같이 정의되어 있습니다.

  • Privileged: 제한적이지 않은 정책으로 가장 광범위한 레벨에 대한 권한을 제공합니다. 이 정책은 알려진 권한 상승을 허용 합니다.
  • Baseline: 알려진 권한 상승을 제한하는 최소화된 제한 정책입니다. 기본 포드 설정을 허용합니다.(최소한으로 지정된)
  • Restricted: 가장 제한적인 정책으로 포드 보안 강화를 위한 모범 사례를 따릅니다.

PSA Admission Controller는 아래 리스트에 나왔 있는 것처럼 3가지 운영 모드를 통해 PSS 정책에 정의되어 있는 통제 항목들을 구현합니다.

  • enforce: 정책을 위반하게 되면 포드는 Reject 처리됩니다.
  • audit: 정책을 위반하게 되면 Audit log 에 이벤트를 기록하기 위한 감사 정보가 추가되어 기록되지만 포드 자체는 허용됩니다.
  • warn: 정책을 위반하게 되면 사용자에게 경고를 표시하지만 포드 자체는 허용됩니다.

내장 Pod Security Admission Enforcement

위에서 언급한 것처럼 Pod Security Feature Gate는 쿠버네티스 1.23 버전부터 베타 기능으로 사용가능하며 Amazon EKS 에서도 기본값으로 활성화되어 있습니다. 쿠버네티스 1.23 업스트림 버전을 위한 기본 PSS 및 PSA 설정이 Amazon EKS에 사용됩니다.

사용되는 설정값들은 아래와 같습니다.:

...
    defaults:
      enforce: "privileged"
      enforce-version: "latest"
      audit: "privileged"
      audit-version: "latest"
      warn: "privileged"
      warn-version: "latest"
    exemptions:
      # Array of authenticated usernames to exempt.
      usernames: []
      # Array of runtime class names to exempt.
      runtimeClasses: []
      # Array of namespaces to exempt.
      namespaces: []
...

클러스터 전체에 적용되는 위의 설정 사항을 살펴보면 아래와 같은 내용을 포함하고 있습니다.

  • 쿠버네티스 API 서버 시작 시 PSA예외처리는 설정되지 않았습니다.
  • 모든 PSA모드에 대해 Privileged PSS 프로파일이 기본값으로 설정되었으며 버전은 최신 버전으로 설정되었습니다.

이 예시에 사용된 설정들은 적용되는 클러스터에 끼치는 영향도가 적으며 어플리케이션에 대해서도 부정적인 영향이 크지 않습니다.

그리고 이후 예시를 통해 알게될 내용이지만, 좀 더 제한적인 설정을 위해 네임스페이스 레이블을 이용하여 정책을 생성할 수 있습니다.

네임스페이스를 위한 Pod Security Admission 레이블

기본 설정사항이 정해지면 여러분은 PSA와 PSS에서 제공하는 제한적인 포드 보안 설정을 네임스페이스에 적용하기 위해서 PSA의 모드와 PSS의 프로파일을 쿠버네티스의 네임스페이스 레벨로 설정해야만 합니다. 여러분은 여러분들이 원하는 포드 보안 설정을 위한 Admission Control Mode를 정의하기 위해 네임스페이스를 설정할 수 있습니다.

또한 쿠버네티스 레이블을 사용하면  사전에 정의된 PSS레벨을 선택하여 주어진 네임스페이스 내에서 어떤 포드를 사용할 지 선택할 수 있습니다. 여러분이 선택한 레이블은 가능한 위반을 탐지하였을 경우 PSA 에서 어떤 액션을 수행할지를 정의합니다.

아래의 코드에서 보이는 것처럼 여러분은 어떠한 모드나 레벨을 설정할 수 있으며 심지어 서로 다른 모드에 대해 서로 다른 레벨을 설정할 수도 있습니다. 그리고 각 모드에서는 정책을 결정할 2개의 레이블이 있습니다.

# The per-mode level label indicates which policy level to apply for the mode.
#
# MODE must be one of `enforce`, `audit`, or `warn`.
# LEVEL must be one of `privileged`, `baseline`, or `restricted`.
pod-security.kubernetes.io/<MODE>: <LEVEL>

# Optional: per-mode version label that can be used to pin the policy to the
# version that shipped with a given Kubernetes minor version (for example v1.24).
#
# MODE must be one of `enforce`, `audit`, or `warn`.
# VERSION must be a valid Kubernetes minor version, or `latest`.
pod-security.kubernetes.io/<MODE>-version: <VERSION>

아래는 테스트를 위해 사용될 수 있는 PSA와 PSS네임스페이스의 설정 예시입니다. 이 예시에는 옵션으로 사용할 수도 있는 PSA mode-version 레이블이 포함되어 있지 않다는 것을 주목하시기 바랍니다. 우리는 기본값으로 설정된 Cluster-Wide 설정과 Latest 설정을 그대로 사용하였습니다.

아래의 예시에서 주석(#)을 제거하면 여러분이 원하는 각 네임스페이스에 대한 PSA모드와 PSS프로파일을 활성화 할 수 있습니다.

apiVersion: v1
kind: Namespace
metadata:
  name: policy-test
  labels:    
    # pod-security.kubernetes.io/enforce: privileged
    # pod-security.kubernetes.io/audit: privileged
    # pod-security.kubernetes.io/warn: privileged
    
    # pod-security.kubernetes.io/enforce: baseline
    # pod-security.kubernetes.io/audit: baseline
    # pod-security.kubernetes.io/warn: baseline
    
    # pod-security.kubernetes.io/enforce: restricted
    # pod-security.kubernetes.io/audit: restricted
    # pod-security.kubernetes.io/warn: restricted

위의 예시에 사용된 것과 같은 PSA와 PSS테스트 예제는 GitHub repository에서 찾을 수 있습니다. 이 포스트에서는 위의 예제를 따르도록 하겠습니다.

Kubernetes Admission Controllers

쿠버네티스에서 Admission Controller는 API 호출이 etcd 에 전달되고 클러스터의 상태를 변경시키기 위해 사용되기 전에 API서버로 전달된 요청을 먼저 처리하는 코드의 조각을 말합니다. Admission Controller는 “Mutating” 이나 “Validating” 타입일 수 있으며 경우에 따라서는 두가지 타입을 모두 포함하고 있을 수 있습니다.

쿠버네티스 API서버가 시작하면 API서버는 클러스터에 설정된 MutatingValidating Admission Controller를 로드합니다. 예를 들어, 아래의 Amazon CloudWatch 로그는 Amazon EKS 1.23 API서버가 시작할 때 Validating Admission Controller가 로드되었음을 나타냅니다.

Loaded 12 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,PodSecurityPolicy,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,ValidatingAdmissionWebhook,ResourceQuota.

위 로그에 나타난 PodSecurity부분의 PSA는 Validating Admission Controller이며 PSA는 유입되는 포드 사양에 대한 요청을 검사하여 지정된 PSS를 준수하는지 여부를 체크합니다. 쿠버네티스 1.23 이전 버전의 경우, PSA는 기존에 운영중인 쿠버네티스 클러스터에 대해 Dynamic Admission Controller를 통해 Admission Webhook의 형태로 추가될 수 있었습니다.

참고: 쿠버네티스의 Dynamic Admission Controller에 대한 간단한 소개에 대해서는 Containers from the Couch에서 제공하는 짧은 비디오를 참고하시기 바랍니다.

아래의 플로우에서 Mutating과 Validating Dynamic Admission Controller는 쿠버네티스 API서버의 요청 플로우와 통합되어 있습니다. 실제로 이전에 보았던 Amazon CloudWatch 로그는 Amazon EKS 1.23 API 서버가 시작될 때 ValidatingAdmissionWebhook승인 컨트롤러가 로드되었음을 나타냅니다.

Webhook은 특정 유형의 API서버 요청에 응답하도록 구성된 서비스를 호출합니다. 예를 들어, 여러분은 Webhook을 사용하여 Dynamic Admission Controller의 설정을 통해 포드내의 컨테이너가 루트가 아닌 사용자로 실행되고 있는지 혹은 컨테이너 이미지가 특정 레지스트리에서 제공되는지 등을 확인할 수 있습니다.

PSA 과 PSS 사용하기

PSA는 API서버 시작 시 로드되는 내장 Admission Controller로서, PSS에 명시된 정책을 적용합니다. 그리고 PSS정책은 포드 보안 프로파일의 집합을 정의합니다. 아래 다이어그램에서는 PSA 및 PSS가 포드 및 네임스페이스와 함께 작동하여 어떻게 포드 보안 프로파일을 정의하고 해당 프로파일을 기반으로 승인 제어를 적용하는지 그 방법을 간략하게 설명합니다.

아래 다음 다이어그램에서 볼 수 있듯이 PSA 적용 모드 및 PSS 정책은 대상 네임스페이스에서 레이블로 정의됩니다.

여러분의 Amazon EKS 클러스터에서 포드 보안 테스트를 위한 설정하기

다음은 Amazon EKS 클러스터에서 PSA 및 PSS를 시작하는 데 도움을 드릴 수 있도록 설계된 안내서입니다. 가이드를 따르려면 AWS 계정, Amazon EKS 1.23 클러스터 및 아래와 같은 도구가 필요합니다.

참고: 클러스터 생성과 관련해서는 Amazon EKS documentation 를 참고하시기 바랍니다.

PSA와 PSS를 이용한 포드 보안 테스트의 동작과 관련한 순서는 아래와 같습니다.

  1. Github의 psa-pss-testing 리포지토리를 복제합니다.
  2. 아직 EKS 클러스터가 없다면 새로운 Amazon EKS 1.23 클러스터를 생성합니다. 클러스터가 생성될 때 PSA 와 PSS에 대한 기본 설정값이 제공될 것입니다. 참고로, 새로운 Amazon EKS 클러스터를 생성하는 가장 간단한 방법은 아래와 같이 eksctl 을 이용하는 방법입니다.
    eksctl create cluster --name <CLUSTER_NAME> --region <AWS_REGION> --version 1.23
  3. 클러스터와 노드 그룹이 생성되었고 Amazon EKS 클러스터에 접근할 수 있도록 kubectl 설정이 변경되었다면 아래의 eksctl 명령을 입력하여 새로운 클러스터에 대한 Amazon CloudWatch 로그 설정을 활성화합니다.
    eksctl utils update-cluster-logging --enable-types=all --region=<AWS_REGION> \
    --cluster=<CLUSTER_NAME>
    
  4. 클러스터에 연결합니다: kubectl version
  5. 복제된 Gibhub 리포지토리에서 원하는 보안 설정을 위한 적절한 PSA와 PSS 레이블과 함께 policy-test 네임스페이스를 설정할 수 있도록 psa-pss-testing/tests/0-ns.yaml 파일을 적절하게 수정합니다.
  6. Amazon EKS 클러스터에 테스트 리소스를 적용하여 테스트가 실행될 수 있도록 psa-pss-testing/tests/test.sh Bash 스크립트를 실행합니다.
  7. 테스트 결과가 PSA와 PSS설정과 매칭되는지 확인합니다.
  8. 테스트를 진행하는 동안 클러스터에 생성된 모든 자원들을 삭제하기 위하여 psa-pss-testing/tests/clean.sh Bash 스크립트를 실행합니다.
  9. Policy-test 네임스페이스에 대한 PSA와 PSS설정을 변경한 후 단계 5에서 8을 반복하여 재테스트를 수행합니다.

PSA 와 PSS 테스트 및 결과

아래에 있는 테스트 시나리오를 수행하기 위해서는 기본적으로 Pod Security Admission(PSA) 및 Pod Security Standards (PSS)의 Privileged프로파일이 활성화된 상태에서 Amazon EKS 1.23 클러스터에 대해 여러 테스트가 실행됩니다. 이 테스트는 다음과 같은 응답을 생성하는 과정들을 통해 다양한 PSA 모드 및 PSS 프로파일에 대해 학습하는 것을 목표로 설계되었습니다.

  • 프로파일 요구사항을 만족하는 포드 허용
  • 프로파일 요구사항을 만족하지 못하는 포드 차단
  • 포드가 PSS 프로파일 요구사항을 만족하지 못하더라도 Deployment 허용
  • 포드가 프로파일 요구사항을 만족하지 못하는 경우 쿠버네티스 API 서버 클라이언트에 “Forbidden” 응답
  • Deployment 리소스에 대해 실패한(Forbidden) 포드 정보 반영
  • PSA Controller 응답과 관련한 쿠버네티스 API 서버로그에 대해 실패한(Forbidden) 포드 정보 반영
  • Deployment 에 포함된 포드 사양이 PSS 프로파일의 요구사항을 만족하지 못하는 경우 쿠버네티스 API 서버 클라이언트에 경고메시지 전달

테스트 설정

다음 리소스는 모든 테스트 시나리오에서 사용됩니다. 원하는 PSA모드 및 PSS프로파일에 대한 레이블을 조정하기 위해 정책 테스트 네임스페이스만 변경됩니다. 네임스페이스 레이블이 조정되면 정책 테스트 네임스페이스에 다른 수준의 포드 보안이 적용되기 때문에 테스트 결과가 달라지게 됩니다.
쿠버네티스 리소스:

  • policy-test 네임스페이스
apiVersion: v1
kind: Namespace
metadata:
  name: policy-test
  labels:    
    # pod-security.kubernetes.io/enforce: privileged
    # pod-security.kubernetes.io/audit: privileged
    # pod-security.kubernetes.io/warn: privileged
    
    # pod-security.kubernetes.io/enforce: baseline
    # pod-security.kubernetes.io/audit: baseline
    # pod-security.kubernetes.io/warn: baseline
    
    # pod-security.kubernetes.io/enforce: restricted
    # pod-security.kubernetes.io/audit: restricted
    # pod-security.kubernetes.io/warn: restricted
  • 적용 예시: 아래의 설정을 적용하면 PSS 프로파일 설정을 준수하는 쿠버네티스 Deployment가 생성되고 삭제됩니다
apiVersion: apps/v1
kind: Deployment
namespace: policy-test
... 
    spec: 
      containers:
      - name: test
        image: public.ecr.aws/r2l1x4g2/go-http-server:v0.1.0-23ffe0a715
        imagePullPolicy: IfNotPresent
        securityContext:  
          allowPrivilegeEscalation: false  
          runAsUser: 1000  
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          capabilities:
            drop: ["ALL"]  
          seccompProfile:
            type: "RuntimeDefault"
        ports:
        - containerPort: 8080
...
  • 적용 예시: 아래의 설정을 적용하면 PSS 프로파일 설정을 위반하는 쿠버네티스 Deployment가 생성됩니다.
  • 정책 위반 사유
    • 포드 수준에 securityContext 요소가 없습니다.
    • 컨테이너 수준에 securityContext 요소가 없습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
  namespace: policy-test
...
    spec: 
      containers:
      - name: test
        image: public.ecr.aws/r2l1x4g2/go-http-server:v0.1.0-23ffe0a715
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
...
  • 적용 예시: 아래의 설정을 적용하면 PSS 프로파일 설정을 위반하는 쿠버네티스 포드가 생성됩니다.
  • 정책 위반 사유
    • 포드 수준에서 securityContext 요소가 없습니다.
    • 컨테이너 수준에서 securityContext 요소가 없습니다.
apiVersion: v1
kind: Pod
metadata:
  name: test
  namespace: policy-test
...
spec:
  containers:
    - name: test
      image: public.ecr.aws/r2l1x4g2/go-http-server:v0.1.0-23ffe0a715
      imagePullPolicy: IfNotPresent
      ports:
      - containerPort: 8080
...
  • 적용 예시: 아래의 설정을 적용하면 PSS 프로파일 설정을 위반하는 쿠버네티스 포드가 생성됩니다.
  • 정책 위반 사유
    • 포드 레벨에서 securityContext 요소가 유효한 runAsUserrunAsNonRoot 와 함께 존재합니다.
    • 컨테이너 수준에서 securityContext 요소가 없습니다.
apiVersion: v1
kind: Pod
metadata:
  name: test2
  namespace: policy-test
...
spec:
  securityContext:  
    runAsUser: 1000  
    runAsNonRoot: true
  containers:
    - name: test
      image: public.ecr.aws/r2l1x4g2/go-http-server:v0.1.0-23ffe0a715
      imagePullPolicy: IfNotPresent
      ports:
      - containerPort: 8080
...
  • 적용 예시: 아래의 설정을 적용하면 PSS 프로파일 설정을 위반하는 쿠버네티스 포드가 생성됩니다.
  • 정책 위반 사유
    • 포드 레벨에서 securityContext 요소가 없습니다.
    • securityContext 요소가 허용되지 않은 allowPrivilegeEscalation, readOnlyRootFilesystemrunAsNonRoot 설정과 함께 컨테이너 레벨에 존재합니다.
apiVersion: v1
kind: Pod
metadata:
  name: test3
  namespace: policy-test
...
spec:
  containers:
    - name: test
      image: public.ecr.aws/r2l1x4g2/go-http-server:v0.1.0-23ffe0a715
      imagePullPolicy: IfNotPresent
      securityContext:  
        allowPrivilegeEscalation: true  
        runAsUser: 1000  
        readOnlyRootFilesystem: false
        runAsNonRoot: false
        capabilities:
          drop: ["ALL"]  
        seccompProfile:
          type: "RuntimeDefault"
      ports:
...
  • 적용 예시: 아래의 설정을 적용하면 PSS 프로파일 설정을 위반하는 쿠버네티스 포드가 생성됩니다.
  • 정책 위반 사유
    • 포드 수준에서 securityContext 요소가 없습니다.
    • securityContext 가 적절한 설정으로 컨테이너 레벨에서 정의되어 있습니다.
    • 포드 사양에 허용되지 않는 hostNetwork, hostPIDhostIPC 설정이 포함되어 있습니다.
apiVersion: v1
kind: Pod
metadata:
  name: test4
  namespace: policy-test
...
spec:
  hostNetwork: true
  hostPID: true
  hostIPC: true
  containers:
    - name: test
      image: public.ecr.aws/r2l1x4g2/go-http-server:v0.1.0-23ffe0a715
      imagePullPolicy: IfNotPresent
      securityContext:  
        allowPrivilegeEscalation: false  
        runAsUser: 1000  
        readOnlyRootFilesystem: true
        runAsNonRoot: true
        capabilities:
          drop: ["ALL"]  
        seccompProfile:
          type: "RuntimeDefault"
      ports:
      - containerPort: 8080
...

테스트 시나리오

앞서 언급한 psa-pss-testing GitHub저장소에는 쿠버네티스 1.23클러스터에서 모든 PSS프로파일을 사용하여 모든 PSA 모드를 테스트하기 위한 여러가지 시나리오가 포함되어 있습니다. 이 포스트에서는 BaselineRestricted PSS 프로파일을 사용하여 세 가지 PSA모드를 모두 테스트하는 두 가지 시나리오(시나리오 3 및 4)를 살펴보도록 하겠습니다.

이 시나리오는 PSS프로파일에서 사용할 수 있는 허용 혹은 차단을 위한 설정의 전체 목록을 포함하지는 않습니다. 다만, 이 시나리오는 PSA가 PSS와 함께 작동하여 원하는 포드 보안을 제공하는 방법을 보여주는 것을 목적으로 합니다.

시나리오 3 – 베이스라인 PSS 프로파일을 위하여 모든 PSA 모드 활성화(네임스페이스 레벨)

  • 네임스페이스 설정
apiVersion: v1
kind: Namespace
metadata:
  name: policy-test
  labels:    
    # pod-security.kubernetes.io/enforce: privileged
    # pod-security.kubernetes.io/audit: privileged
    # pod-security.kubernetes.io/warn: privileged
    
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/audit: baseline
    pod-security.kubernetes.io/warn: baseline
    
    # pod-security.kubernetes.io/enforce: restricted
    # pod-security.kubernetes.io/audit: restricted
    # pod-security.kubernetes.io/warn: restricted
  • 테스트 결과 – 네임스페이스 레벨 PSS 베이스라인 프로파일 적용(4개 포드 허용, 1개 포드 차단)
namespace/policy-test created

>>> 1. Good config...
deployment.apps/test created
deployment.apps "test" deleted

>>> 2. Deployment - Missing container security context element...
deployment.apps/test created

>>> 3. Pod - Missing container security context element...
pod/test created

>>> 4. Pod - Pod security context, but Missing container security context element...
pod/test2 created

>>> 5. Pod - Container security context element present, with incorrect settings...
pod/test3 created

>>> 6. Pod - Container security context element present, with incorrect spec.hostNetwork, spec.hostPID, spec.hostIPC settings...
Error from server (Forbidden): error when creating "policy/psa-pss/tests/6-pod.yaml": pods "test4" is forbidden: violates PodSecurity "baseline:latest": host namespaces (hostNetwork=true, hostPID=true, hostIPC=true), hostPort (container "test" uses hostPort 8080)

kubectl -n policy-test get po
NAME                   READY   STATUS    RESTARTS   AGE
test                   1/1     Running   0          46s
test-59955f994-6tbj7   1/1     Running   0          52s
test2                  1/1     Running   0          42s
test3                  1/1     Running   0          37s

시나리오 4 – 제한된 PSS 프로파일을 위해 모든 PSA 모드 활성화(네임스페이스 레벨)

  • 네임스페이스 설정
apiVersion: v1
kind: Namespace
metadata:
  name: policy-test
  labels:
    # pod-security.kubernetes.io/enforce: privileged
    # pod-security.kubernetes.io/audit: privileged
    # pod-security.kubernetes.io/warn: privileged
    
    # pod-security.kubernetes.io/enforce: baseline
    # pod-security.kubernetes.io/audit: baseline
    # pod-security.kubernetes.io/warn: baseline
    
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
  • 테스트 결과: 네임스페이스 레벨 PSS 제한적인 프로파일 적용(허용된 포드 없음, 5개 포드 차단)
    • 0개의 포드가 허용된 1개의 Deployment 생성
namespace/policy-test created


>>> 1. Good config...
deployment.apps/test created
deployment.apps "test" deleted


>>> 2. Deployment - Missing container security context element...
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "test" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "test" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "test" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "test" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
deployment.apps/test created


>>> 3. Pod - Missing container security context element...
Error from server (Forbidden): error when creating "policy/psa-pss/tests/3-pod.yaml": pods "test" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "test" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "test" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "test" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "test" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")


>>> 4. Pod - Pod security context, but Missing container security context element...
Error from server (Forbidden): error when creating "policy/psa-pss/tests/4-pod.yaml": pods "test2" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "test" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "test" must set securityContext.capabilities.drop=["ALL"]), seccompProfile (pod or container "test" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")


>>> 5. Pod - Container security context element present, with incorrect settings...
Error from server (Forbidden): error when creating "policy/psa-pss/tests/5-pod.yaml": pods "test3" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "test" must set securityContext.allowPrivilegeEscalation=false), runAsNonRoot != true (container "test" must not set securityContext.runAsNonRoot=false)


>>> 6. Pod - Container security context element present, with incorrect spec.hostNetwork, spec.hostPID, spec.hostIPC settings...
Error from server (Forbidden): error when creating "policy/psa-pss/tests/6-pod.yaml": pods "test4" is forbidden: violates PodSecurity "restricted:latest": host namespaces (hostNetwork=true, hostPID=true, hostIPC=true), hostPort (container "test" uses hostPort 8080)

kubectl -n policy-test get po
No resources found in policy-test namespace.

kubectl -n policy-test get deploy test -oyaml
...
status:
  conditions:
...
  - lastTransitionTime: "2022-07-12T23:56:10Z"
    lastUpdateTime: "2022-07-12T23:56:10Z"
    message: 'pods "test-59955f994-wl8hf" is forbidden: violates PodSecurity "restricted:latest":
      allowPrivilegeEscalation != false (container "test" must set securityContext.allowPrivilegeEscalation=false),
      unrestricted capabilities (container "test" must set securityContext.capabilities.drop=["ALL"]),
      runAsNonRoot != true (pod or container "test" must set securityContext.runAsNonRoot=true),
      seccompProfile (pod or container "test" must set securityContext.seccompProfile.type
      to "RuntimeDefault" or "Localhost")'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
...

테스트 가정

Amazon EKS 1.23 클러스터의 기본 PSA및 PSS설정과 PSA및 PSS의 작동 방식을 고려하여 다음과 같은 테스트 가정이 이루어졌습니다.

  • PSA Enforce 모드는 포드에만 영향을 미치며 포드를 생성하는 워크로드 리소스 컨트롤러(Deployment등)에는 영향을 미치지 않는다.
  • PSA컨트롤러에 대해서는 API서버 시작 시 PSA예외처리가 설정되지 않는다.
  • 모든 PSA 모드에 대해Privileged PSS 프로파일은 기본적으로 설정되며 최신 버전으로 설정된다. 이 설정은 네임스페이스 레이블을 통해 변경할 수 있다.

테스트 결과

테스트 설정과 시나리오, 그리고 Amazon EKS가 업스트림 쿠퍼네티스를 사용한다는 사실을 감안할 때 다음과 같은 결과가 도출되었습니다.

  • Amazon EKS 1.23에서 각 PSA모드(Audit, Enforce, Warn)가 예상대로 정상동작 하였습니다.
  • Amazon EKS 1.23에서 PSS프로파일(Privileged, Baseline, and Restricted)은 예상대로 정상동작 하였습니다.

사용자 경험(UX)

PSA모드는 독립적으로 사용될 때 다른 사용자 경험을 초래하는 다른 응답을 갖습니다. 예를 들어, “Enforce” 모드는 각 포드 사양이 적용된 PSS프로파일을 위반하는 경우 포드가 생성되지 않도록 합니다. 하지만 이 모드에서는 Deployment와 같은(포드를 생성하는 포드가 아닌 객체) 쿠버네티스 객체에 적용된 포드 사양이 PSS 프로필을 위반하더라도 해당 객체가 클러스터에 적용되는 것을 차단하지는 않습니다. 이 경우 포드가 생성되지 않는 상태에서 Deployment가 생성됩니다.

이런 시나리오에서는 Deployment 객체가 성공적으로 생성되더라도 포드 생성에는 실패했다는 것을 나타내는 직관적인 표시가 없기 때문에 사용자 경험측면(관리자 입장)에서는 어려움이 예상됩니다. 문제가 되는 포드 사양은 포드를 생성하지 않습니다. 예를 들어, kubectl get deploy <DEPLOYMENT_NAME> -o yaml을 사용하여 배포 리소스를 검사하면 위 테스트에서 볼 수 있듯이 실패한 Pod(s) .status.conditions 요소의 메시지를 확인할 수 있습니다.

AuditWarn PSA 모드에서의 포드 제한은 규칙을 위반하는 포드가 생성되거나 시작되는 것을 차단하지 않습니다. 하지만, 이러한 모드에서는 API 서버 감사 로그 이벤트에 대한 감사 주석과 API 서버 클라이언트(예: kubectl)에 대한 경고가 각각 활성화됩니다. 이는 포드와 포드를 생성하는 객체에 PSS 규칙을 위반하는 포드 사양이 포함되어 있을 때 발생합니다. kubectl Warning 메시지는 다음과 같이 출력에 표시됩니다.

deployment.apps/test created
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != 
false (container "test" must set securityContext.allowPrivilegeEscalation=false), 
unrestricted capabilities (container "test" must set 
securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container 
"test" must set securityContext.runAsNonRoot=true), seccompProfile (pod or 
container "test" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

Deployment 가 생성되는 동안 포드는 생성되지 않았습니다. 이를 통해 우리는 Warn 이나 Audit 모드를 사용하는 것이 좀 더 나은 사용자 경험을 제공하는 모범 사례라는 것을 좀 더 확실하게 알 수 있습니다.

삭제

위에서 eksctl 를 사용하여 Amazon EKS 클러스터를 생성한 경우 eksctl은 두 개의 AWS CloudFormation 스택을 생성합니다. 하나는 클러스터용이고 다른 하나는 노드 그룹용입니다. 아래의 eksctl 명령을 사용하여 원래 eksctl create cluster 명령으로 생성된 클러스터, 노드 그룹 및 기타 모든 AWS 리소스를 삭제합니다. 아래 명령은 로컬 kubectl 구성에서 구성 항목도 함께 제거합니다.

eksctl delete cluster --name <CLUSTER_NAME> --wait

생성된 모든 리소스는 AWS CloudFormation 스택의 일부이므로 스택을 직접 삭제하여 정리할 수도 있습니다. 이 경우 로컬 kubectl 구성에서 클러스터 구성 항목을 명시적으로 제거해야 합니다.

결론

이 포스트에서는 Amazon EKS에서 다양한 포드 보안 표준을 적용하는 방법을 살펴보았고 아래와 같은 몇 가지 사항을 알 수 있었습니다.

  • 쿠버네티스 프로젝트는 PSP를 PSA 및 PSS로 대체하기로 결정했습니다.
  • PSA와 PSS는 함께 작동합니다(즉, PSA는 PSS에 명시된 보안 제어를 구현합니다).
  • PSA와 PSS는 모두 쿠버네티스 1.23의 베타 기능이며 PSP와 동일한 클러스터에서 PSA 및 PSS를 실행할 수 있습니다.
  • 이를 통해 버전 1.25의 쿠버네티스에서 PSP가 제거되기 전에 사전에 준비하고 향후 마이그레이션할 수 있는 방안을 지원합니다.
  • PSA 및 PSS의 기본 구성은 Amazon EKS 1.23의 일부이며 쿠버네티스 네임스페이스는 PSS에서 정의하고 PSA에서 구현하는 포드 보안을 선택하도록 레이블로 구성될 수 있습니다.
  • PSA 와 PSS 의 적절한 정책 구성을 통해PSP를 성공적으로 교체할 수 있습니다.

PSA 와 PSS 를 시도해보세요!

PSP를 대체할 솔루션을 찾고 계셨다면 PSA와 PSS를 살펴보시기를 추천드립니다. 아직 쿠버네티스 1.23을 사용하지 않더라도 앞서 언급한 Dynamic Admission Controller Pod Security Admission Webhook 솔루션을 통해 PSA와 PSS를 사용할 수 있습니다. 이제는 PSA 및 PSS를 시도하고 이 새로운 솔루션이 여러분의 포드 보안 요구 사항에 맞는지 확인할 때입니다.

여러분들은 이 포스트에서 공유한 테스트 접근 방식을 활용하여 PSA 및 PSS가 적합한지 확인하실 수 있습니다. 그리고 PSA 및 PSS가 PSP 대체 가능성이 있는 PaC(Policy-as-Code) 솔루션과 어떻게 비교되는지 궁금하시다면 관련 포드 보안 주제에 대한 EKS 모범 사례 가이드를 확인하시기 바랍니다.

컨테이너 로드맵을 확인하세요!

Amazon 컨테이너 서비스를 개선할 수 있는 방법에 대한 아이디어가 있으시면 컨테이너 로드맵을 이용하여 기존 로드맵 항목을 참고하시기 바라며 이를 통해 우리에게 피드백을 제출하실 수 있습니다.

– Jayaprakash Alawala, Sr Container Specialist Solutions Architect, AWS
– Jimmy Ray,Developer Advocate, AWS

원문: https://aws.amazon.com/blogs/containers/implementing-pod-security-standards-in-amazon-eks/

Eunsu Shin

Eunsu Shin

신은수 Security Specialist Solutions Architect 는 보안 담당 SA로서 다양한 산업군의 고객들이 AWS 환경에서 수행해야하는 규정 준수 및 인증획득(개인정보보호법, 전자금융감독규정, ISMS-P 인증 등)을 위한 기술적인 도움을 제공해드리고 있습니다. 또한, 고객이 보다 안전하게 AWS 클라우드를 구성하고 운영할 수 있도록 다양한 모범 사례 공유, AWS 보안 서비스 교육 및 기술자문 등의 업무를 수행하고 있습니다.