게이트키퍼를 사용하여 커스텀 포드 수준 보안 정책 적용


이 페이지에서는 게이트키퍼 허용 컨트롤러를 사용하여 포드 수준 보안 제어를 Google Kubernetes Engine(GKE) 클러스터에 적용하는 방법을 설명합니다.

개요

게이트키퍼OPA(Open Policy Agent)를 사용하여 Kubernetes 클러스터에서 포드 생성 및 업데이트 요청을 검증하는 허용 컨트롤러입니다.

게이트키퍼를 사용하면 관리자가 제약조건을 사용하여 정책을 정의할 수 있습니다. 제약조건은 Kubernetes에서 배포 동작을 허용하거나 거부하는 조건의 집합입니다. 그런 다음 ConstraintTemplate을 사용하여 클러스터에 이러한 정책을 시행할 수 있습니다. 이 문서에서는 게이트키퍼를 사용하여 보안 정책을 시행, 테스트, 감사하도록 워크로드의 보안 기능을 제한하는 예시를 제공합니다.

게이트키퍼는 다음 작업도 수행할 수 있습니다.

  • 정책 출시: 점진적이고 범위가 지정된 방식으로 정책을 시행하여 워크로드가 중단되는 위험을 제한합니다.
  • 정책 변경사항 테스트 실행: 정책 영향 및 시행 전에 범위를 테스트하기 위한 메커니즘을 제공합니다.
  • 기존 정책 감사: 새로운 워크로드 및 기존 워크로드에 보안 제어 애플리케이션을 적용합니다(감사 제어).

개념

게이트키퍼에는 관리자에게 클러스터를 제어할 수 있는 강력하고 유연한 수단을 제공하기 위한 두 가지 개념인 제약조건제약조건 템플릿이 있습니다. 두 가지 모두 개방형 정책 에이전트(OPA) 제악조건 프레임워크에서 상속된 개념입니다.

제약조건은 보안 정책을 표현하며 요구사항 및 적용 범위를 정의합니다. 제약조건 템플릿Rego로 작성된 재사용 가능한 문으로, 제약조건에 정의된 요구사항에 따라 Kubernetes 객체의 특정 필드를 평가하는 로직을 적용합니다.

예를 들어 특정 네임스페이스의 포드에 적용할 수 있는 허용 가능한 seccomp 프로필을 선언하는 제약조건과 이러한 값을 추출하고 시행을 처리하는 논리를 제공하는 유사한 제약조건 템플릿이 있을 수 있습니다.

게이트키퍼 저장소의 다음 제약조건 템플릿은 포드 사양에서 securityContext.privileged의 존재를 확인합니다.

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8spspprivilegedcontainer
spec:
  crd:
    spec:
      names:
        kind: K8sPSPPrivilegedContainer
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspprivileged

        violation[{"msg": msg, "details": {}}] {
            c := input_containers[_]
            c.securityContext.privileged
            msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
        }
        input_containers[c] {
            c := input.review.object.spec.containers[_]
        }
        input_containers[c] {
            c := input.review.object.spec.initContainers[_]
        }

위의 제약조건 템플릿을 확장하기 위해 다음 제약조건은 dryrun 모드에서 이 제약조건 템플릿의 특정 시행에 대한 범위(kinds)를 정의합니다.

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: psp-privileged-container
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]

게이트키퍼를 사용하면 특정 요구사항에 맞게 고유한 제약조건과 제약조건 템플릿을 만들 수 있습니다. 또한 빠른 채택과 보안 적용을 사용 설정하도록 정의된 게이트키퍼 저장소에서 제약조건 표준 설정과 제약조건 템플릿을 사용할 수 있습니다. 각 제약조건에는 예시 포드 구성도 함께 제공됩니다.

Google Cloud는 오픈소스 게이트키퍼의 공식적으로 지원되는 관리형 버전인 정책 컨트롤러를 제공합니다. Google은 오픈소스 게이트키퍼 프로젝트를 공식적으로 지원하지 않습니다.

시작하기 전에

시작하기 전에 다음 태스크를 수행했는지 확인합니다.

  • Google Kubernetes Engine API를 사용 설정합니다.
  • Google Kubernetes Engine API 사용 설정
  • 이 태스크에 Google Cloud CLI를 사용하려면 gcloud CLI를 설치한 후 초기화합니다. 이전에 gcloud CLI를 설치한 경우 gcloud components update를 실행하여 최신 버전을 가져옵니다.

정책 컨트롤러로를 사용하여 클러스터에서 게이트키퍼 사용 설정

정책 컨트롤러게이트키퍼 오픈소스 프로젝트에 빌드된 정책 엔진입니다. 코드형 정책, 멀티 클러스터 지원, Cloud Logging과의 통합, Google Cloud 콘솔에서 정책 상태 확인 기능을 포함하여 규모에 맞게 정책을 시행하는 데 도움이 되는 추가 기능이 포함되어 있으므로 정책 컨트롤러를 사용하는 것이 좋습니다. 정책 컨트롤러는 Google Kubernetes Engine(GKE) Enterprise 버전 라이선스에서 사용할 수 있지만 대신 클러스터에 Gatekeeper를 설치할 수 있습니다.

클러스터에서 정책 컨트롤러를 사용 설정하려면 정책 컨트롤러 설치 가이드를 따르세요.

제약조건 및 제약조건 템플릿 사용 설정

게이트키퍼 및 제약조건 템플릿은 기존 워크로드나 새 워크로드에 부정적인 영향을 미치지 않고 설치 및 사용 설정할 수 있습니다. 따라서 적용 가능한 모든 포드 보안 제약조건 템플릿을 클러스터에 적용하는 것이 좋습니다.

또한 게이트키퍼 제약조건을 구현하여 네임스페이스 및 포드와 같은 특정 객체에 대한 제어를 적용할 수 있습니다.

제약조건 일치 문에서 포드를 정의하여 production 네임스페이스에 있는 포드로 범위를 제한하는 아래 예시를 관찰합니다.

...
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces:
      - "production"

ConstraintConstraintTemplate 객체에 사용 가능한 옵션에 대한 자세한 내용은 Gatekeeper 사용 방법을 참조하세요.

정책 테스트

기존 클러스터에 새 정책을 도입하면 기존 워크로드 제한과 같은 부정적인 동작이 발생할 수 있습니다. 포드 보안에 게이트키퍼를 사용하는 이점 중 하나는 정책을 실제로 변경하지 않고도 테스트 실행 모드를 사용하여 정책의 효과와 영향을 테스트할 수 있다는 점입니다. 이렇게 하면 시행하지 않고도 실행 중인 클러스터에서 정책 구성을 테스트할 수 있습니다. 정책 위반은 간섭 없이 로깅 및 식별됩니다.

다음 단계에서는 개발자, 운영자 또는 관리자가 제약조건 템플릿과 제약조건을 적용하여 효율성 또는 잠재적 영향을 알아내는 방법을 보여줍니다.

  1. 게이트키퍼 구성을 적용하여 감사 및 테스트 실행 기능을 위한 데이터 복제

    kubectl create -f- <<EOF
    apiVersion: config.gatekeeper.sh/v1alpha1
    kind: Config
    metadata:
      name: config
      namespace: "gatekeeper-system"
    spec:
      sync:
        syncOnly:
          - group: ""
            version: "v1"
            kind: "Namespace"
          - group: ""
            version: "v1"
            kind: "Pod"
    EOF
    
  2. 적용된 제약조건이 없는 상태에서 승격된 권한으로 워크로드를 실행합니다.

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    
  3. 위에서 언급한 k8spspprivilegedcontainer 제약조건 템플릿을 로드합니다.

    kubectl create -f- <<EOF
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8spspprivilegedcontainer
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPPrivilegedContainer
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspprivileged
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                c.securityContext.privileged
                msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
    EOF
    
  4. 이제 이 제약조건 템플릿을 확장하는 새 제약조건을 만들어 보겠습니다. 이번에는 enforcementActiondryrun으로 설정합니다.

    kubectl create -f- <<EOF
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
      name: psp-privileged-container
    spec:
      enforcementAction: dryrun
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    EOF
    
  5. 게이트키퍼가 실행 중인 객체 데이터를 동기화하고 수동으로 위반 여부를 확인함으로써 제약조건의 status를 확인하여 위반이 발생했는지 확인할 수 있습니다.

    kubectl get k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container -o yaml
    
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
    ...
     name: psp-privileged-container
    ...
    spec:
     enforcementAction: dryrun
     match:
       kinds:
       - apiGroups:
         - ""
         kinds:
         - Pod
    status:
     auditTimestamp: "2019-12-15T22:19:54Z"
     byPod:
     - enforced: true
       id: gatekeeper-controller-manager-0
     violations:
     - enforcementAction: dryrun
       kind: Pod
       message: 'Privileged container is not allowed: nginx, securityContext: {"privileged":
         true}'
       name: nginx
       namespace: default
    
  6. 권한이 있는 다른 포드를 실행하여 정책이 배포와 충돌하지 않는지 확인하겠습니다.

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: privpod
      labels:
        app: privpod
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    

    이 새 포드가 성공적으로 배포됩니다.

  7. 이 섹션에서 만든 리소스를 삭제하려면 다음 명령어를 실행합니다.

    kubectl delete k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container
    kubectl delete constrainttemplate k8spspprivilegedcontainer
    kubectl delete pod/nginx
    kubectl delete pod/privpod
    

정책 시행

이제 기존 또는 새 워크로드에 영향을 주지 않고 정책의 유효성과 영향을 확인할 수 있으므로 본격 시행으로 정책을 구현해 봅시다.

다음 단계에서는 위 정책 유효성을 검사하는 데 사용된 예시를 바탕으로 개발자, 운영자 또는 관리자가 제약조건 템플릿과 제약조건을 적용하여 정책을 시행하는 방법을 보여줍니다.

  1. 앞에서 언급한 k8spspprivilegedcontainer 제약조건 템플릿을 로드합니다.

    kubectl create -f- <<EOF
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8spspprivilegedcontainer
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPPrivilegedContainer
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspprivileged
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                c.securityContext.privileged
                msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
    EOF
    
  2. 이제 이 제약조건 템플릿을 확장하는 새 제약조건을 만들어 보겠습니다. 이번에는 enforcementAction 키를 설정하지 않습니다. 기본적으로 enforcementAction 키는 deny로 설정됩니다.

    kubectl create -f- <<EOF
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
      name: psp-privileged-container
    spec:
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    EOF
    
  3. 승격된 권한을 선언하는 컨테이너를 배포하려고 시도합니다.

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    

    다음과 같은 오류 메시지가 표시됩니다.

    Error from server ([denied by psp-privileged-container] Privileged container is not allowed:
    nginx, securityContext: {"privileged": true}): error when creating "STDIN": admission webhook "validation.gatekeeper.sh" denied the request: [denied by psp-privileged-container]
    Privileged container is not allowed: nginx, securityContext: {"privileged": true}
    
  4. 삭제하려면 다음 명령어를 실행합니다.

    kubectl delete k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container
    kubectl delete constrainttemplate k8spspprivilegedcontainer
    

게이트키퍼 대안

게이트키퍼를 사용하면 커스텀 포드 수준 보안 정책을 선언하고 적용할 수 있습니다. Kubernetes에서 기본 제공되는 PodSecurity 허용 컨트롤러를 사용하여 사전 정의된 포드 수준 보안 정책을 적용할 수도 있습니다. 이러한 사전 정의된 정책은 포드 보안 표준에서 정의된 수준과 일치합니다.

다음 단계

게이트키퍼는 선언적 정책을 사용하여 GKE 클러스터에서 보안을 시행하고 유효성을 검사하는 강력한 방법을 제공합니다. 게이트키퍼 사용은 보안 수준을 뛰어넘으며 관리 및 운영의 다른 측면에서 게이트키퍼를 사용할 수 있습니다.