워크로드를 전용 노드 풀에서 격리

이 페이지에서는 Google Distributed Cloud (GDC) 에어 갭에서 전용 노드 풀에 컨테이너 워크로드를 격리하여 포드를 더 세부적으로 제어하는 방법을 보여줍니다. 워크로드 격리는 다음과 같은 이점을 제공합니다.

  • Kubernetes 클러스터에서 권한 에스컬레이션 공격의 위험이 감소했습니다.
  • 추가 리소스가 필요한 포드를 더 많이 제어할 수 있습니다.

이러한 경우 더 많은 제어와 최적화를 위해 컨테이너 워크로드를 격리하는 것이 좋습니다.

워크로드를 격리해야 하는 이유는 무엇인가요?

전용 노드 풀에서 워크로드를 격리하지 않아도 되지만 잠재적인 문제를 방지하기 위해 취할 수 있는 신중한 조치입니다. 하지만 전용 노드 풀을 관리하려면 더 많은 감독이 필요하며 불필요한 경우가 많습니다.

Kubernetes 클러스터는 권한이 있는 GDC 관리 워크로드를 사용하여 측정항목 수집과 같은 특정 클러스터 기능과 기능을 사용 설정합니다. 이러한 워크로드에는 클러스터에서 올바르게 실행될 수 있는 특수한 권한이 부여됩니다.

노드에 배포하는 워크로드는 악의적인 항목에 의해 손상될 가능성이 있습니다. 권한이 있는 GDC 관리 워크로드와 함께 이러한 워크로드를 실행하면 손상된 컨테이너를 침입한 공격자가 노드에서 권한이 있는 워크로드의 사용자 인증 정보를 사용하여 클러스터의 권한을 에스컬레이션할 수 있습니다.

전용 노드 풀은 추가 메모리 또는 추가 로컬 디스크 공간 등 다른 것보다 더 많은 리소스가 필요한 포드를 예약해야 할 경우에도 유용합니다.

다음 메커니즘을 사용하여 전용 노드 풀에 워크로드를 예약할 수 있습니다.

노드 taint는 Kubernetes 클러스터에 해당 노드에서 해당 톨러레이션(예: GDC 관리 워크로드) 없이 워크로드를 예약하지 않도록 알립니다. 자체 워크로드의 노드 어피니티는 전용 노드에서 포드를 예약하도록 클러스터에 지시합니다.

노드 격리 제한사항

  • 공격자는 여전히 손상된 노드에서 서비스 거부 (DoS) 공격을 시작할 수 있습니다.

  • 손상된 노드는 클러스터의 모든 포드 및 네임스페이스를 포함하여 여러 리소스를 계속 읽을 수 있습니다.

  • 손상된 노드는 해당 노드에서 실행되는 모든 포드에서 사용되는 보안 비밀과 사용자 인증 정보에 액세스할 수 있습니다.

  • 별도의 노드 풀을 사용하여 워크로드를 격리하면 비용 효율성, 자동 확장, 리소스 사용률에 영향을 미칠 수 있습니다.

  • 손상된 노드는 계속해서 이그레스 네트워크 정책을 우회할 수 있습니다.

  • 일부 GDC 관리 워크로드는 클러스터의 모든 노드에서 실행되어야 하며 모든 taint를 허용하도록 구성됩니다.

  • 권한이 승격되고 taint를 허용할 수 있는 DaemonSet 리소스를 배포하는 경우, 해당 포드는 손상된 노드에서 권한 에스컬레이션을 위한 경로가 될 수 있습니다.

노드 격리 작동 방식

워크로드의 노드 격리를 구현하려면 다음을 수행해야 합니다.

  1. 워크로드의 노드 풀을 taint하고 라벨을 지정합니다.

  2. 해당 톨러레이션(toleration) 및 노드 어피니티 규칙으로 워크로드를 업데이트합니다.

이 가이드는 클러스터에서 노드 풀 한 개로 시작한다고 가정합니다. 노드 taint 외에 노드 어피니티를 반드시 사용할 필요는 없지만 예약을 보다 효율적으로 제어할 수 있으므로 이를 사용하는 것이 좋습니다.

시작하기 전에

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

  • 전용 노드 풀에 사용할 노드 taint와 노드 라벨의 특정 이름을 선택하세요. 예를 들면 workloadType=untrusted입니다.

  • 필요한 경우 조직 IAM 관리자에게 네임스페이스에 바인딩되지 않은 사용자 클러스터 개발자 역할 (user-cluster-developer)을 부여해 달라고 요청하세요.

새 노드 풀 taint 및 라벨 지정

새 노드 풀에 taint 또는 라벨을 적용하면 나중에 추가된 노드를 포함한 모든 노드에 지정된 taint 및 라벨이 자동으로 적용됩니다.

새 노드 풀에 taint와 라벨을 추가하려면 다음 단계를 완료하세요.

  1. 노드 풀을 만드는 경우 Cluster 커스텀 리소스의 nodePools 섹션을 직접 수정합니다.

    nodePools:
      ...
      - machineTypeName: n2-standard-2-gdc
        name: nodepool-1
        nodeCount: 3
        taints: TAINT_KEY=TAINT_VALUE:TAINT_EFFECT
        labels: LABEL_KEY=LABEL_VALUE
    

    다음을 바꿉니다.

    • TAINT_KEY=TAINT_VALUE: TAINT_EFFECT 예약과 연결된 키-값 쌍입니다. 예를 들면 workloadType=untrusted입니다.
    • TAINT_EFFECT: 다음 효과 값 중 하나입니다.
      • NoSchedule: 이 taint를 허용하지 않는 포드는 노드에서 예약되지 않습니다. 기존 포드가 노드에서 제거되지 않습니다.
      • PreferNoSchedule: Kubernetes는 노드에 이 taint를 허용하지 않는 포드를 예약하지 않습니다.
      • NoExecute: 포드가 이미 노드에서 실행 중이면 포드가 노드에서 제거되고 아직 노드에서 실행되지 않은 경우 노드에 예약되지 않습니다.
    • LABEL_KEY=LABEL_VALUE: 워크로드 매니페스트에 지정한 선택기에 해당하는 노드 라벨의 키-값 쌍입니다.
  2. Cluster 리소스를 적용하여 새 노드 풀을 만듭니다.

    kubectl apply -f cluster.yaml \
        --kubeconfig MANAGEMENT_API_SERVER
    

    MANAGEMENT_API_SERVER를 Kubernetes 클러스터가 호스팅되는 영역 API 서버의 kubeconfig 경로로 바꿉니다. 타겟 영역의 API 서버에 대한 kubeconfig 파일을 아직 생성하지 않은 경우 로그인에서 자세한 내용을 확인하세요.

기존 노드 풀 taint 및 라벨 지정

기존 노드 풀에 테인트 또는 라벨을 적용하려면 각 기존 노드에 변경사항을 적용해야 합니다. 노드 풀 구성을 동적으로 업데이트할 수 없습니다.

기존 노드 풀에 taint와 라벨을 추가하려면 다음 단계를 완료하세요.

  1. 전용 노드 풀의 노드를 나열합니다.

    kubectl get node --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG \
        -l baremetal.cluster.gke.io/node-pool=NODE_POOL_NAME
    

    다음 변수를 바꿉니다.

    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes 클러스터의 kubeconfig 경로입니다.
    • NODE_POOL_NAME: 전용 노드 풀의 이름입니다.

    출력에서 노드 풀에 있는 모든 노드의 각 노드 ID를 기록합니다.

  2. 노드 풀의 각 노드에 대해 테인트를 적용합니다.

    kubectl taint nodes NODE_ID \
        TAINT_KEY=TAINT_VALUE:TAINT_EFFECT \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    다음 변수를 바꿉니다.

    • NODE_ID: 전용 노드 풀의 워커 노드 ID
    • TAINT_KEY=TAINT_VALUE: TAINT_EFFECT 예약과 연결된 키-값 쌍입니다. 예를 들면 workloadType=untrusted입니다.
    • TAINT_EFFECT: 다음 효과 값 중 하나입니다.
      • NoSchedule: 이 taint를 허용하지 않는 포드는 노드에서 예약되지 않습니다. 기존 포드가 노드에서 제거되지 않습니다.
      • PreferNoSchedule: Kubernetes는 노드에 이 taint를 허용하지 않는 포드를 예약하지 않습니다.
      • NoExecute: 포드가 이미 노드에서 실행 중이면 포드가 노드에서 제거되고 아직 노드에서 실행되지 않은 경우 노드에 예약되지 않습니다.
    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes 클러스터의 kubeconfig 경로입니다.
  3. 노드 풀의 각 노드에 컨테이너 워크로드에서 정의할 선택기에 해당하는 라벨을 적용합니다.

    kubectl label NODE_ID \
        LABEL_KEY:LABEL_VALUE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    다음 변수를 바꿉니다.

    • NODE_ID: 전용 노드 풀의 워커 노드 ID
    • LABEL_KEY:LABEL_VALUE: 노드 라벨의 키-값 쌍입니다. 이는 워크로드 매니페스트에 지정한 선택기에 해당합니다.
    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes 클러스터의 kubeconfig 경로입니다.

톨러레이션(toleration) 및 노드 어피니티 규칙 추가

전용 노드 풀을 taint한 후에는 추가한 taint에 대한 톨러레이션(toleration)이 없으면 워크로드가 이를 예약할 수 없습니다. 워크로드 사양에 톨러레이션(toleration)을 추가하여 해당 포드가 taint된 노드 풀에서 예약할 수 있도록 합니다.

전용 노드 풀에 라벨을 지정한 경우, 노드 어피니티 규칙을 추가하여 해당 노드 풀에서만 워크로드를 예약하도록 GDC에 지시할 수도 있습니다.

전용 노드 풀에서 실행되도록 컨테이너 워크로드를 구성하려면 다음 단계를 완료하세요.

  1. 컨테이너 워크로드의 .spec.template.spec 섹션에 다음 섹션을 추가합니다.

    kind: Deployment
    apiVersion: apps/v1
        ...
        spec:
        ...
          template:
            spec:
              tolerations:
              - key: TAINT_KEY
                operator: Equal
                value: TAINT_VALUE
                effect: TAINT_EFFECT
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: LABEL_KEY
                        operator: In
                        values:
                        - "LABEL_VALUE"
              ...
    

    다음을 바꿉니다.

    • TAINT_KEY: 전용 노드 풀에 적용한 taint 키
    • TAINT_VALUE: 전용 노드 풀에 적용한 taint 값입니다.
    • TAINT_EFFECT: 다음 효과 값 중 하나입니다.
      • NoSchedule: 이 taint를 허용하지 않는 포드는 노드에서 예약되지 않습니다. 기존 포드가 노드에서 제거되지 않습니다.
      • PreferNoSchedule: Kubernetes는 노드에 이 taint를 허용하지 않는 포드를 예약하지 않습니다.
      • NoExecute: 포드가 이미 노드에서 실행 중이면 포드가 노드에서 제거되고 아직 노드에서 실행되지 않은 경우 노드에 예약되지 않습니다.
    • LABEL_KEY: 전용 노드 풀에 적용한 노드 라벨 키입니다.
    • LABEL_VALUE: 전용 노드 풀에 적용한 노드 라벨 값입니다.

    예를 들어 다음 Deployment 리소스는 workloadType=untrusted:NoExecute taint에 대한 톨러레이션(toleration)과 workloadType=untrusted 노드 라벨에 대한 노드 어피니티 규칙을 추가합니다.

    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: my-app
      namespace: default
      labels:
        app: my-app
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: my-app
      template:
        metadata:
          labels:
            app: my-app
        spec:
          tolerations:
          - key: workloadType
            operator: Equal
            value: untrusted
            effect: NoExecute
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: workloadType
                    operator: In
                    values:
                    - "untrusted"
          containers:
          - name: my-app
            image: harbor-1.org-1.zone1.google.gdc.test/harborproject/my-app
            ports:
            - containerPort: 80
          imagePullSecrets:
          - name: SECRET
    
  2. 배포를 업데이트합니다.

    kubectl apply -f deployment.yaml -n NAMESPACE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    다음 변수를 바꿉니다.

    • NAMESPACE: 컨테이너 워크로드의 프로젝트 네임스페이스입니다.
    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes 클러스터의 kubeconfig 경로입니다.

GDC가 영향을 받는 포드를 다시 만듭니다. 노드 어피니티 규칙은 생성된 전용 노드 풀로 포드를 강제 적용합니다. 톨러레이션(toleration)을 통해 이러한 포드만 노드에 배치될 수 있습니다.

분리 작동 확인

예약이 올바르게 작동하는지 확인하려면 다음 명령어를 실행하고 워크로드가 전용 노드 풀에 있는지 확인합니다.

kubectl get pods -o=wide -n NAMESPACE \
    --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG

추천 및 권장사항

노드 격리를 설정한 후에는 다음을 수행하는 것이 좋습니다.

  • 새 노드 풀을 만들 때 노드 풀에 자체 taint를 추가하여 대부분의 GDC 관리 워크로드가 이러한 노드에서 실행되지 않도록 합니다.
  • 타사 도구를 설치할 때와 같이 새 워크로드를 클러스터에 배포할 때마다 포드에 필요한 권한을 감사합니다. 가능하면 공유 노드에 승격된 권한을 사용하는 워크로드를 배포하지 마세요.