보안 이슈 완화


이 문서에서는 Google Kubernetes Engine(GKE) 클러스터와 컨테이너에서 발생할 수 있는 보안 이슈에 대한 일반적인 완화 및 대응 방법을 설명합니다.

이 문서는 GKE 보안 이슈에 대응할 때 안내를 원하는 보안 전문가를 위해 작성되었습니다. Google Cloud 콘텐츠에서 참조하는 일반적인 역할과 예시 태스크에 대한 자세한 내용은 일반 GKE Enterprise 사용자 역할 및 태스크를 참조하세요.

클러스터 보안 강화의 권장사항을 따르면 GKE 워크로드의 보안을 향상시킬 수 있습니다. 하지만 보안 이슈는 워크로드를 보호하는 조치가 마련된 경우에도 발생할 수 있습니다.

이슈 감지

잠재적인 이슈를 감지하려면 워크로드의 로그를 수집 및 모니터링하는 프로세스를 설정하는 것이 좋습니다. 그런 다음 로그에서 감지된 비정상 이벤트를 기준으로 알림을 설정합니다. 알림은 비정상적인 상황이 감지되면 보안팀에게 이를 알립니다. 그러면 보안팀이 잠재적 이슈를 검토할 수 있습니다.

로그에서 알림 생성

특정 측정항목 또는 작업을 기준으로 알림을 맞춤설정할 수 있습니다. 예를 들어 GKE 노드의 CPU 사용량이 많다는 알림은 노드가 암호화폐 채굴로 인해 손상되었음을 나타내는 것일 수 있습니다.

알림은 로그 및 측정항목을 집계할 때 생성되어야 합니다. 예를 들어 Cloud Logging에서 GKE 감사 로깅로그 기반 알림을 함께 사용할 수 있습니다.

보안 관련 쿼리에 대한 자세한 내용은 감사 로깅 문서를 참고하세요.

보안 이슈에 대응

이슈에 대한 알림을 받으면 조치를 취합니다. 가능한 경우 취약점을 수정합니다. 취약점의 근본 원인을 알 수 없거나 수정할 준비가 되지 않았으면 완화 조치를 취합니다.

취할 수 있는 완화 조치는 이슈의 심각도와 문제를 파악하고 있는 정도에 따라 달라집니다.

이 가이드에서는 GKE에서 실행되는 워크로드에서 발생한 이슈를 감지한 후 취할 수 있는 조치를 설명합니다. 심각도가 높아지는 순서대로 다음 조치를 취할 수 있습니다.

다음 섹션에서 이러한 완화 방법을 설명합니다.

시작하기 전에

이 주제에서 사용되는 방법에서는 다음 정보를 사용합니다.

  • 손상된 것으로 판단되는 pod의 이름 또는 POD_NAME
  • 컨테이너 또는 pod를 실행하는 호스트 VM의 이름 또는 NODE_NAME

또한 조치를 취하기 전에 공격자가 부정적인 반응을 보일 수도 있음에 유의하세요. 공격자가 데이터를 삭제하거나 워크로드를 폐기할 수 있습니다. 위험도가 너무 높으면 추가 조사를 수행하기 전에 워크로드를 삭제하는 등 보다 과감한 완화 조치를 고려합니다.

VM 디스크의 스냅샷 만들기

VM 디스크의 스냅샷을 만들면 워크로드를 재배포 또는 삭제한 후 포렌식 조사를 수행할 수 있습니다. 디스크가 실행 중인 인스턴스에 연결되어 있는 동안에 스냅샷을 만들 수 있습니다.

  1. 영구 디스크 스냅샷을 만들려면 먼저 VM에 연결된 디스크를 찾습니다. 다음 명령어를 실행하고 source 필드를 확인합니다.

    gcloud compute instances describe NODE_NAME --zone COMPUTE_ZONE \
        --format="flattened([disks])"
    
  2. disks[NUMBER].source가 포함된 줄을 찾습니다. 출력은 다음과 비슷합니다.

    disks[0].source: https://www.googleapis.com/compute/v1/projects/PROJECT_NAME/zones/COMPUTE_ZONE/disks/DISK_NAME
    

    디스크 이름은 마지막 슬래시 뒤의 소스 이름 부분입니다. 예를 들어 디스크 이름은 gke-cluster-pool-1-abcdeffff-zgt8입니다.

  3. 스냅샷을 완료하려면 다음 명령어를 실행합니다.

    gcloud compute disks snapshot DISK_NAME
    

자세한 내용은 Compute Engine 문서에서 영구 디스크 스냅샷 만들기를 참조하세요.

워크로드가 실행되는 동안 VM 검사

또한 공격자가 작업을 수행하기 전에 어떠한 액세스 권한을 가질 수 있는지 생각해 봅니다. 컨테이너가 손상되었고 공격자가 이를 알 수 있는 우려가 있는 경우 컨테이너에 연결하여 검사할 수 있습니다. 검사는 더 큰 영향을 주는 조치를 취하기 전에 신속한 조사를 수행할 때 유용합니다. 또한 검사는 워크로드에 가장 영향을 적게 주는 방식이지만 이슈를 중단시키지는 못합니다.

또는 권한이 있는 사용자 인증 정보를 사용하여 머신에 로그인을 방지하려면 라이브 포렌식(예: GRR 신속 대응), 온노드 에이전트 또는 네트워크 필터링을 설정하여 워크로드를 분석하면 됩니다.

라이브 VM을 검사하기 전에 액세스 줄이기

손상된 컨테이너를 호스팅하는 VM에 대한 네트워크 액세스를 차단, 드레이닝, 제한하여 손상된 컨테이너를 클러스터의 나머지 부분에서 부분적으로 격리할 수 있습니다. VM에 대한 액세스를 제한하면 위험을 줄일 수 있지만 공격자가 중요 취약점을 악용하여 사용자 환경에서 측면 이동하는 것을 막을 수는 없습니다.

노드 차단 및 다른 워크로드를 이 노드에서 드레이닝

노드를 차단 및 드레이닝하면 손상된 컨테이너와 함께 있던 워크로드가 클러스터 내 다른 VM으로 이동합니다. 차단과 드레이닝은 같은 노드에 있는 다른 워크로드에 영향을 미치는 공격자의 능력을 감소시킵니다. 하지만 공격자가 컨테이너 이미지 콘텐츠를 검사하는 등 워크로드의 영구 상태를 검사하는 것을 막을 수 있는 것은 아닙니다.

  1. kubectl을 사용하여 노드를 차단하고 다른 pod가 예약되어 있지 않은지 확인합니다.

    kubectl cordon NODE_NAME
    

    노드를 차단한 후 다른 pod의 노드를 드레이닝합니다.

  2. 격리하려는 pod에 라벨을 지정합니다.

    kubectl label pods POD_NAME quarantine=true
    

    여기서 POD_NAME은 격리할 pod의 이름으로 바꿉니다.

  3. quarantine으로 라벨이 지정되지 않은 pod의 노드를 드레이닝합니다.

    kubectl drain NODE_NAME --pod-selector='!quarantine'
    

노드에 대한 네트워크 액세스 제한

내부 트래픽과 외부 트래픽 모두 호스트 VM에 액세스하지 않도록 차단하는 것이 좋습니다. 그런 다음 네트워크 또는 VPC의 특정 VM의 인바운드 연결이 격리된 VM에 연결하도록 허용합니다.

첫 번째 단계는 VM을 소유한 관리형 인스턴스 그룹에서 VM을 중단하는 것입니다. VM을 중단하면 조사가 완료되기 전에 노드가 비정상 상태로 표시되고 자동 복구(다시 생성)되지 않도록 할 수 있습니다.

VM을 중단하려면 다음 명령어를 실행합니다.

gcloud compute instance-groups managed abandon-instances INSTANCE_GROUP_NAME \
    --instances=NODE_NAME

VM에 방화벽 설정

영향을 받는 컨테이너와 같은 네트워크에 있는 다른 워크로드 사이에 방화벽을 만들면 추가 분석을 수행하는 동안 공격자가 환경의 다른 부분으로 이동하는 것을 방지할 수 있습니다. 이미 다른 컨테이너의 VM을 드레이닝했으므로 이 조치는 격리된 컨테이너에만 영향을 미칩니다.

다음 안내를 따라 VM에 방화벽을 설정하면 다음 연결을 차단할 수 있습니다.

  • 이그레스 규칙을 사용하여 클러스터의 다른 VM에 대한 아웃바운드 연결 차단
  • 인그레스 규칙을 사용하여 손상된 VM에 대한 인바운드 연결 차단

다른 인스턴스에서 VM에 방화벽을 설정하려면 격리하려는 pod를 호스팅하는 노드에서 다음 단계를 수행합니다.

  1. 새 방화벽 규칙을 적용할 수 있도록 인스턴스에 태그를 지정합니다.

    gcloud compute instances add-tags NODE_NAME \
        --zone COMPUTE_ZONE \
        --tags quarantine
    
  2. quarantine 태그가 있는 인스턴스의 모든 이그레스 TCP 트래픽을 거부하는 방화벽 규칙을 만듭니다.

    gcloud compute firewall-rules create quarantine-egress-deny \
        --network NETWORK_NAME \
        --action deny \
        --direction egress \
        --rules tcp \
        --destination-ranges 0.0.0.0/0 \
        --priority 0 \
        --target-tags quarantine
    
  3. quarantine 태그가 있는 인스턴스에 대한 모든 인그레스 TCP 트래픽을 거부하는 방화벽 규칙을 만듭니다. 이 인그레스 규칙의 priority1로 설정합니다. 그러면 지정된 VM에서 SSH를 허용하는 다른 규칙으로 이를 재정의할 수 있습니다.

    gcloud compute firewall-rules create quarantine-ingress-deny \
        --network NETWORK_NAME \
        --action deny \
        --direction ingress \
        --rules tcp \
        --source-ranges 0.0.0.0/0 \
        --priority 1 \
        --target-tags quarantine
    

VM의 외부 IP 주소 삭제

VM의 외부 IP 주소를 삭제하면 VPC 외부의 기존 네트워크 연결이 끊어집니다.

VM의 외부 주소를 삭제하려면 다음 단계를 수행합니다.

  1. 외부 IP를 VM과 연결하는 액세스 구성을 찾아 삭제합니다. 먼저 VM을 설명하여 액세스 구성을 찾습니다.

    gcloud compute instances describe NODE_NAME \
        --zone COMPUTE_ZONE --format="flattened([networkInterfaces])"
    

    namenatIP가 포함된 줄을 찾습니다. 다음과 같이 표시됩니다.

    networkInterfaces[0].accessConfigs[0].name:              ACCESS_CONFIG_NAME
    networkInterfaces[0].accessConfigs[0].natIP:             EXTERNAL_IP_ADDRESS
    
  2. 삭제할 외부 IP와 일치하는 natIP 값을 찾습니다. 액세스 구성 이름을 기록합니다

  3. 외부 IP를 삭제하려면 다음 명령어를 실행합니다.

    gcloud compute instances delete-access-config NODE_NAME \
        --access-config-name "ACCESS_CONFIG_NAME"
    

중간 VM을 통해 호스트 VM에 SSH를 통해 연결

호스트 VM의 외부 IP를 삭제한 후에는 VPC 외부에서 SSH를 통해 연결할 수 없습니다. 같은 네트워크에 있는 다른 VM에서 액세스합니다. 이 섹션의 나머지 부분에서는 이를 중간 VM이라고 합니다.

기본 요건

  • 호스트 VM의 서브네트워크에 대한 액세스 권한이 있는 중간 VM. 아직 없으면 이러한 목적으로 VM을 만듭니다.
  • 중간 VM의 내부 IP 주소
  • 중간 VM의 SSH 공개 키. 자세한 내용은 SSH 키 관리를 참조하세요.

호스트 VM에 연결

  1. 중간 VM의 공개 키를 호스트 VM에 추가합니다. 자세한 내용은 Compute Engine 문서의 SSH 키 추가 및 삭제를 참조하세요.
  2. 중간 VM에 태그를 추가합니다.

    gcloud compute instances add-tags INTERMEDIATE_NODE_NAME \
      --zone COMPUTE_ZONE \
      --tags intermediate
    
  3. 앞에서 추가한 거부 규칙을 재정의하는 인그레스 허용 규칙을 추가합니다. 규칙을 추가하려면 다음 명령어를 실행합니다.

    gcloud compute firewall-rules create quarantine-ingress-allow \
        --network NETWORK_NAME \
        --action allow \
        --direction ingress \
        --rules tcp:22 \
        --source-tags intermediate \
        --priority 0 \
        --target-tags quarantine
    

    이 규칙을 사용하면 네트워크에서 intermediate 태그가 있는 VM에서 포트 22(SSH)로 들어오는 트래픽이 허용됩니다. 이 규칙은 거부 규칙의 priority0으로 재정의합니다.

  4. 내부 IP를 사용하여 격리된 VM에 연결합니다.

    ssh -i KEY_PATH USER@QUARANTINED_VM_INTERNAL_IP
    

    다음을 바꿉니다.

    • KEY_PATH: SSH 비공개 키의 경로입니다.
    • USER: Google Cloud 계정의 이메일 주소입니다.
    • QUARANTINED_VM_INTERNAL_IP: 내부 IP 주소입니다.

컨테이너 재배포

컨테이너를 다시 배포하면 컨테이너의 새 복사본이 시작되고 손상된 컨테이너가 삭제됩니다.

컨테이너를 호스팅하는 Pod를 삭제하여 컨테이너를 다시 배포합니다. Pod가 상위 Kubernetes 생성자(예: 배포 또는 DaemonSet)에서 관리되는 경우 Pod를 삭제하면 새 Pod가 예약됩니다. 이 Pod는 새 컨테이너를 실행합니다.

재배포는 다음과 같은 경우에 적합합니다.

  • 이미 취약점의 원인을 알고 있는 경우
  • 공격자가 컨테이너를 다시 손상시키는 데 상당한 노력이나 시간이 소요될 것으로 판단되는 경우
  • 컨테이너가 빠르게 손상될 수 있고 오프라인으로 전환하지 않으려고 해서 영향을 제한하기 위해 샌드박스에 배치하려는 경우

워크로드를 다시 배포할 때 다른 손상 가능성이 높으면 GKE Sandbox와 같은 샌드박스 환경에 워크로드를 배치합니다. 샌드박스를 사용하면 공격자가 컨테이너를 다시 훼손하더라도 호스트 노드 커널에 대한 액세스가 제한됩니다.

Kubernetes에서 컨테이너를 다시 배포하려면 컨테이너가 포함된 포드를 삭제합니다.

kubectl delete pods POD_NAME --grace-period=10

삭제된 포드의 컨테이너가 계속 실행되는 경우에는 워크로드를 삭제하면 됩니다.

샌드박스 내에 컨테이너를 다시 배포하려면 GKE Sandbox를 사용하여 워크로드 격리 강화의 안내를 따르세요.

워크로드 삭제

배포 또는 DaemonSet과 같은 워크로드를 삭제하면 모든 구성원 Pod가 삭제됩니다. Pod 내부의 모든 컨테이너가 실행 중지됩니다. 워크로드 삭제는 다음과 같은 경우에 적합합니다.

  • 진행 중인 공격을 중단시키려는 경우
  • 워크로드를 오프라인으로 전환하려는 경우
  • 애플리케이션 업타임 또는 포렌식 분석보다 공격을 즉시 중단시키는 것이 더 중요한 경우

워크로드를 삭제하려면 kubectl delete CONTROLLER_TYPE을 사용합니다. 예를 들어 배포를 삭제하려면 다음 명령어를 실행합니다.

kubectl delete deployments DEPLOYMENT

워크로드를 삭제해도 연결된 모든 pod 또는 컨테이너가 삭제되지 않는 경우에는 컨테이너 런타임 CLI 도구(일반적으로 docker)를 사용하여 컨테이너를 수동으로 삭제할 수 있습니다. 노드가 containerd로 실행되고 있으면 crictl을 사용합니다.

Docker

Docker 컨테이너 런타임을 사용하여 컨테이너를 중지하려면 docker stop 또는 docker kill을 사용하면 됩니다.

docker stop은 루트 프로세스에 SIGTERM 신호를 보내 컨테이너를 중지하고 프로세스가 종료될 때까지 기본적으로 10초 동안 대기합니다. 프로세스가 이 기간 내에 종료되지 않으면 SIGKILL 신호를 보냅니다. --time 옵션으로 이 유예 기간을 지정할 수 있습니다.

docker stop --time TIME_IN_SECONDS CONTAINER

docker kill을 사용하면 컨테이너를 가장 빠르게 중지할 수 있습니다. SIGKILL 신호를 즉시 보내기 때문입니다.

docker kill CONTAINER

또한 docker rm -f 명령어 하나로 컨테이너를 중지하고 삭제할 수 있습니다.

docker rm -f CONTAINER

containerd

GKE에서 containerd 런타임을 사용하는 경우 crictl로 컨테이너를 중지하거나 삭제할 수 있습니다.

containerd에서 컨테이너를 중지하려면 다음 명령어를 실행합니다.

crictl stop CONTAINER

containerd에서 컨테이너를 삭제하려면 다음 명령어를 실행합니다.

crictl rm -f CONTAINER

호스트 VM 삭제

컨테이너를 삭제할 수 없는 경우에는 영향을 받는 컨테이너를 호스팅하는 가상 머신을 삭제하면 됩니다.

Pod가 계속 표시되는 경우 다음 명령어를 사용하여 호스트 VM의 이름을 확인할 수 있습니다.

kubectl get pods --all-namespaces \
  -o=custom-columns=POD_NAME:.metadata.name,INSTANCE_NAME:.spec.nodeName \
  --field-selector=metadata.name=POD_NAME

호스트 VM을 삭제하려면 다음 gcloud 명령어를 실행합니다.

gcloud compute instance-groups managed delete-instances INSTANCE_GROUP_NAME \
    --instances=NODE_NAME

관리형 인스턴스 그룹에서 인스턴스를 중단하면 그룹 크기가 VM 한 개만큼 줄어듭니다. 다음 명령어를 사용하면 수동으로 인스턴스 한 개를 다시 그룹에 추가할 수 있습니다.

gcloud compute instance-groups managed resize INSTANCE_GROUP_NAME \
    --size=SIZE

다음 단계