보안 게시판

Kubernetes Engine 관련 보안 게시판은 수시로 업데이트될 수 있습니다. Google Kubernetes Engine에 해당되는 모든 보안 게시판은 여기에서 확인할 수 있습니다.

취약점은 영향을 받는 당사자가 대처 방안을 마련할 수 있을 때까지 엠바고에 따라 비밀로 유지되는 경우가 많습니다. 이 경우 GKE의 출시 노트에서 이러한 취약점은 엠바고가 해제될 때까지 '보안 업데이트'로 지칭됩니다. 엠바고가 해제되면 패치가 해결한 취약점을 반영하여 출시 노트가 업데이트됩니다.

GKE 보안 게시판 구독 구독

2018년 12월 3일

설명 심각도 참고

Kubernetes에서 최근 발견한 신규 보안 취약점 CVE-2018-1002105는 권한이 비교적 낮은 사용자가 kubelet의 API에 대한 승인을 우회하여 클러스터 내의 어떤 노드에서든 모든 포드에 대해 임의의 작업을 실행할 수 있다는 문제를 안고 있습니다. 자세한 내용은 Kubernetes 정보 공개를 참조하세요. 모든 Google Kubernetes Engine(GKE) 마스터가 이 취약점의 영향을 받았으며, Google은 이미 최신 패치 버전으로 클러스터를 업그레이드했습니다. 필요한 별도의 조치는 없습니다.

어떻게 해야 하나요?

별도의 조치를 취할 필요가 없습니다. GKE 마스터가 이미 업그레이드되었습니다.

이 패치는 GKE 1.9.7-gke.11, 1.10.6-gke.11, 1.10.7-gke.11, 1.10.9-gke.5, 1.11.2-gke.18 및 그 이상의 출시 버전에서 제공됩니다.

이 패치로 어떤 취약점이 해결되나요?

이 패치로 다음의 취약점이 완화됩니다.

취약점 CVE-2018-1002105는 권한이 비교적 낮은 사용자가 kubelet의 API에 대한 승인을 우회할 수 있다는 문제를 안고 있습니다. 그에 따라 사용자에게는 kubelet API 임의 호출 수행과 에스컬레이션을 위한 요청(업그레이드 가능) 권한이 주어집니다. 이는 Kubernetes에서 심각한 취약점으로 분류됩니다. 미승인 에스컬레이션 경로가 차단된 GKE 구현을 좀 더 상세히 고려해 보면 이 문제는 높은 등급의 취약점으로 분류됩니다.

높음 CVE-2018-1002105

2018년 11월 13일

설명

2018년 11월 16일 업데이트 내용: 영향을 받았을 가능성이 있는 모든 토큰의 취소와 회전이 완료되었습니다. 추가 조치는 필요하지 않습니다.

Google은 최근 Calico Container Network Interface(CNI) 플러그인이 특정 구성에서 민감한 정보를 로깅할 수 있다는 문제를 발견했습니다. 이 문제는 Tigera Technical Advisory TTA-2018-001에서 추적됩니다.

  • 디버그 수준 로깅으로 실행 시 Calico CNI 플러그인은 Kubernetes API 클라이언트 구성을 로그에 씁니다.
  • 또한 CNI 네트워크 구성에서 'k8s_auth_token' 필드가 설정된 경우 Calico CNI는 정보 수준에서 Kubernetes API 토큰을 로그에 씁니다.
  • 또한 디버그 수준 로깅으로 실행 시 서비스 계정 토큰이 Calico가 읽는 Calico 구성 파일에서든 Calico에서 사용하는 환경 변수로서든 명시적으로 설정된 경우, Calico 구성요소(calico/node, felix, CNI)는 이 정보를 로그 파일에 씁니다.

이러한 토큰에는 다음과 같은 권한이 있습니다.


bgpconfigurations.crd.projectcalico.org     [create get list update watch]
bgppeers.crd.projectcalico.org              [create get list update watch]
clusterinformations.crd.projectcalico.org   [create get list update watch]
felixconfigurations.crd.projectcalico.org   [create get list update watch]
globalbgpconfigs.crd.projectcalico.org      [create get list update watch]
globalfelixconfigs.crd.projectcalico.org    [create get list update watch]
globalnetworkpolicies.crd.projectcalico.org [create get list update watch]
globalnetworksets.crd.projectcalico.org     [create get list update watch]
hostendpoints.crd.projectcalico.org         [create get list update watch]
ippools.crd.projectcalico.org               [create get list update watch]
networkpolicies.crd.projectcalico.org       [create get list update watch]
nodes                                       [get list update watch]
pods                                        [get list watch patch]
namespaces                                  [get list watch]
networkpolicies.extensions                  [get list watch]
endpoints                                   [get]
services                                    [get]
pods/status                                 [update]
networkpolicies.networking.k8s.io           [watch list]
      

클러스터 네트워크 정책과 Stackdriver Logging이 사용 설정된 Google Kubernetes Engine 클러스터가 Calico 서비스 계정 토큰을 Stackdriver에 로깅했습니다. 네트워크 정책이 사용 설정되지 않은 클러스터는 영향을 받지 않습니다.

Google은 수정 배포를 통해 Calico CNI 플러그인을 이전하여 경고 수준에서만 로깅하고 새로운 서비스 계정을 사용할 수 있도록 했습니다. 패치된 calico 코드는 후속 출시 버전에서 배포됩니다.

영향을 받았을 가능성이 있는 모든 토큰은 다음 주 중에 단계적으로 취소될 예정입니다. 취소가 완료되면 이 게시판이 업데이트됩니다. 추가 조치는 필요하지 않습니다. (이 회전은 2018년 11월 16일에 완료됨)

이러한 토큰은 다음의 명령어를 실행해 즉시 회전할 수 있으며, 서비스 계정의 신규 보안 비밀이 몇 초 이내에 자동으로 재생성됩니다.


kubectl get sa --namespace kube-system calico -o template --template '&#123&#123(index .secrets 0).name&#125&#125' | xargs kubectl delete secret --namespace kube-system
      

감지

GKE가 API 서버에 모든 액세스를 로깅합니다. Calico 토큰이 Google Cloud의 예상 IP 범위 밖에서 사용되었는지 확인하려면 다음의 Stackdriver 쿼리를 실행하세요. 이 쿼리는 GCP 네트워크 밖에서 수행된 호출에 대한 레코드만 반환합니다. 이는 사용자의 환경별로 맞춤설정할 필요가 있습니다.


resource.type="k8s_cluster"
protoPayload.authenticationInfo.principalEmail="system:serviceaccount:kube-system:calico"
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "8.34.208.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "8.35.192.0/21")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "8.35.200.0/23")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "108.59.80.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "108.170.192.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "108.170.208.0/21")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "108.170.216.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "108.170.220.0/23")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "108.170.222.0/24")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.224.0.0/13")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "162.216.148.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "162.222.176.0/21")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "173.255.112.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "192.158.28.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "199.192.112.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "199.223.232.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "199.223.236.0/23")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "23.236.48.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "23.251.128.0/19")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.204.0.0/14")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.208.0.0/13")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "107.167.160.0/19")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "107.178.192.0/18")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "146.148.2.0/23")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "146.148.4.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "146.148.8.0/21")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "146.148.16.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "146.148.32.0/19")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "146.148.64.0/18")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.203.0.0/17")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.203.128.0/18")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.203.192.0/19")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.203.240.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "130.211.8.0/21")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "130.211.16.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "130.211.32.0/19")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "130.211.64.0/18")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "130.211.128.0/17")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "104.154.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "104.196.0.0/14")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "208.68.108.0/23")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.184.0.0/14")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.188.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.202.0.0/16")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.190.0.0/17")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.190.128.0/18")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.190.192.0/19")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.235.224.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.192.0.0/14")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.196.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.198.0.0/16")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.199.0.0/17")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.199.128.0/18")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.200.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "2600:1900::/35")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.190.224.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.232.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.234.0.0/16")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.235.0.0/17")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.235.192.0/20")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.236.0.0/14")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.240.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.203.232.0/21")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "130.211.4.0/22")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.220.0.0/14")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.242.0.0/15")
NOT ip_in_net(protoPayload.requestMetadata.callerIp, "35.244.0.0/14")
      

2018년 8월 14일

설명 심각도 참고

Intel은 다음과 같은 CVE를 공개했습니다.

이러한 CVE를 통칭하여 'L1 터미널 오류(L1TF)'라고 부릅니다.

이러한 L1TF 취약점은 프로세서 수준 데이터 구조의 구성을 공격하는 식의 예측 실행 악용 문제를 안고 있습니다. 'L1'은 레벨 1 데이터 캐시(L1D), 즉 메모리 액세스 가속화에 사용되는 소형 온코어(on-core) 리소스를 의미합니다.

이러한 취약점과 Compute Engine의 완화 조치에 대한 자세한 내용은 Google Cloud 블로그 글을 참조하세요.

Google Kubernetes Engine에 미치는 영향

Kubernetes Engine을 실행하고 고객 클러스터와 노드를 서로 격리하는 인프라는 알려진 공격으로부터 안전합니다.

Google의 컨테이너 최적화 OS 이미지를 사용하고 자동 업그레이드를 사용 설정한 Kubernetes Engine 노드 풀은 2018년 8월 20일로 시작하는 주부터 COS 이미지의 패치 버전이 제공될 때마다 자동으로 업데이트됩니다.

자동 업그레이드를 사용 설정하지 않은 Kubernetes Engine 노드 풀은 COS 이미지의 패치 버전이 제공될 때마다 수동으로 업그레이드해야 합니다.

높음

2018년 8월 6일, 최종 업데이트 날짜: 2018년 9월 5일

설명 심각도 참고

2018년 9월 5일 업데이트 내용

최근 공개된 CVE-2018-5391CVE-2018-5390과 같은 커널 수준의 네트워킹 취약점으로서, 취약 시스템에 대한 서비스 거부(DoS) 공격 효과가 증가한다는 문제를 안고 있습니다. 주요한 차이는 CVE-2018-5391은 IP 연결 시 악용될 수 있다는 점입니다. 이 두 취약점을 모두 고려할 수 있도록 게시판이 업데이트되었습니다.

설명

CVE-2018-5390('SegmentSmack')은 TCP 연결 시 취약 시스템에 대한 서비스 거부(DoS) 공격 효과가 증가하는 커널 수준의 네트워킹 취약점입니다.

CVE-2018-5391('FragmentSmack')은 IP 연결 시 취약 시스템에 대한 서비스 거부(DoS) 공격 효과가 증가하는 커널 수준의 네트워킹 취약점입니다.

Google Kubernetes Engine에 미치는 영향

2018년 8월 11일부터는 모든 Kubernetes Engine 마스터와 자동 업그레이드가 구성된 모든 Kubernetes Engine 클러스터가 두 취약점으로부터 안전해집니다. 자동 업그레이드가 구성되지 않았고 마지막 수동 업그레이드 시점이 2018년 8월 11일 이전인 Kubernetes Engine 노드 풀은 두 취약점에 영향을 받을 수 있습니다.

패치 버전

이 취약점의 심각도가 높으므로 패치가 제공되는 즉시 노드를 수동으로 업그레이드하는 것이 좋습니다.

높음

2018년 5월 30일

설명 심각도 참고

Git에서 최근 발견된 취약점은 권한 없는 사용자가 gitRepo 볼륨이 포함된 포드를 생성할 수 있을 경우 Kubernetes 내의 권한 에스컬레이션이 가능할 수도 있다는 문제를 안고 있습니다. 이 CVE는 CVE-2018-11235 태그로 식별됩니다.

영향을 받는지 여부는 어떻게 확인하나요?

다음 항목이 모두 해당된다면 이 취약점의 영향을 받는 것입니다.

  • 신뢰할 수 없는 사용자가 포드를 생성하거나 포드 생성을 트리거할 수 있습니다.
  • 신뢰할 수 없는 사용자가 생성한 포드에 호스트 루트 액세스를 방지하는 제한이 있습니다(예: PodSecurityPolicy를 통해 설정한 경우).
  • 신뢰할 수 없는 사용자가 생성한 포드가 gitRepo 볼륨 유형을 사용할 수 있습니다.

이에 해당되는 모든 Kubernetes Engine 노드는 취약합니다.

어떻게 해야 하나요?

gitRepo 볼륨 유형의 사용을 금지하세요. PodSecurityPolicy를 통해 gitRepo 볼륨을 사용 금지하려면 PodSecurityPolicy의 volumes 허용 목록에서 gitRepo를 제외하면 됩니다.

이에 상응하는 gitRepo 볼륨 동작은 initContainer에서 git 저장소를 EmptyDir 볼륨으로 복제하여 구현할 수 있습니다.


apiVersion: v1
kind: Pod
metadata:
  name: git-repo-example
spec:
  initContainers:
    # This container clones the desired git repo to the EmptyDir volume.
    - name: git-clone
      image: alpine/git # Any image with git will do
      args:
        - clone
        - --single-branch
        - --
        - https://github.com/kubernetes/kubernetes # Your repo
        - /repo # Put it in the volume
      securityContext:
        runAsUser: 1 # Any non-root user will do. Match to the workload.
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
      volumeMounts:
        - name: git-repo
          mountPath: /repo
  containers:
    ...
  volumes:
    - name: git-repo
      emptyDir: {}

어떤 패치로 이 취약점을 해결할 수 있나요?

향후 예정된 Kubernetes Engine 출시 버전에 패치가 포함될 예정입니다. 추후에 게시판을 다시 방문하여 자세한 내용을 확인하세요.

보통

2018년 5월 21일

설명 심각도 참고

Linux 커널에서 최근 발견된 몇몇 취약점은 권한 없는 프로세스의 (커널 장애를 통한) 서비스 거부 또는 권한 에스컬레이션이 가능할 수도 있다는 문제를 안고 있습니다. 이러한 CVE는 CVE-2018-1000199, CVE-2018-8897, CVE-2018-1087 태그로 식별됩니다. 해당 취약점은 모든 Kubernetes Engine 노드에 영향을 미치므로 아래의 세부정보를 참고하여 최신 패치 버전으로 업그레이드하는 것이 좋습니다.

어떻게 해야 하나요?

업그레이드하려면 우선 마스터를 최신 버전으로 업그레이드해야 합니다. 이 패치는 Kubernetes Engine 1.8.12-gke.1, Kubernetes Engine 1.9.7-gke.1, Kubernetes Engine 1.10.2-gke.1에서 제공됩니다. 이러한 출시 버전에는 컨테이너 최적화 OS와 Ubuntu 이미지의 패치가 포함됩니다.

그 전에 새 클러스터를 만드는 경우 여기에 사용할 패치 버전을 지정해야 합니다. 노드 자동 업그레이드를 사용 설정하고 수동으로는 업그레이드하지 않는 고객은 노드가 향후 몇 주 내에 패치 버전으로 업그레이드됩니다.

이 패치로 어떤 취약점이 해결되나요?

이 패치로 다음의 취약점이 완화됩니다.

CVE-2018-1000199: 이 취약점은 Linux 커널에 영향을 미치며, 권한이 없는 사용자나 프로세스가 시스템 커널에 장애를 일으켜 DoS 공격이나 권한 에스컬레이션이 발생할 수 있습니다. 이 문제는 CVSS 7.8점에 해당하는 높은 등급의 취약점으로 분류됩니다.

CVE-2018-8897: 이 취약점은 Linux 커널에 영향을 미치며, 권한이 없는 사용자나 프로세스가 시스템 커널에 장애를 일으켜 DoS 공격이 발생할 수 있습니다. 이 문제는 CVSS 6.5점에 해당하는 보통 등급의 취약점으로 분류됩니다.

CVE-2018-1087: 이 취약점은 Linux 커널의 KVM 하이퍼바이저에 영향을 미치며, 그에 따라 권한이 없는 프로세스가 게스트 커널에 장애를 일으킬 수 있게 되고 이 프로세스에 권한이 주어질 가능성도 있습니다. 이 취약점은 Kubernetes Engine 실행 인프라에서 관련 패치를 실시해 Kubernetes Engine에 영향을 미치지 않습니다. 이 문제는 CVSS 8.0점에 해당하는 높은 등급의 취약점으로 분류됩니다.

높음

2018년 3월 12일

설명 심각도 참고

Kubernetes 프로젝트에서 최근 공개된 신규 보안 취약점 CVE-2017-1002101CVE-2017-1002102는 컨테이너의 컨테이너 외부 파일 액세스가 가능하다는 문제를 안고 있습니다. 해당 취약점은 모든 Kubernetes Engine 노드에 영향을 미치므로 아래의 세부정보를 참고하여 가급적 신속히 최신 패치 버전으로 업그레이드하는 것이 좋습니다.

어떻게 해야 하나요?

이 취약점의 심각도가 높으므로 노드의 자동 업그레이드 사용 설정 여부와 관계없이 패치가 제공되는 즉시 노드를 수동으로 업그레이드하는 것이 좋습니다. 패치는 3월 16일까지 모든 고객에게 제공될 예정이지만 사용자의 클러스터가 위치한 영역(zone)을 기준으로 출시 일정에 따라 더 일찍 제공될 수도 있습니다.

업그레이드하려면 우선 마스터를 최신 버전으로 업그레이드해야 합니다. 이 패치는 Kubernetes 1.9.4-gke.1, Kubernetes 1.8.9-gke.1, Kubernetes 1.7.14-gke.1에서 제공됩니다. 새 클러스터는 3월 30일까지 기본적으로 패치 버전을 사용합니다. 그 전에 새 클러스터를 만드는 경우에는 여기에 사용할 패치 버전을 지정해야 합니다.

노드 자동 업그레이드를 사용 설정하고 수동으로는 업그레이드하지 않는 Kubernetes Engine 고객은 노드가 4월 23일까지 패치 버전으로 업그레이드됩니다. 그러나 취약점의 특성을 고려할 때 패치가 제공되는 즉시 노드를 수동으로 업그레이드하는 것이 좋습니다.

이 패치로 어떤 취약점이 해결되나요?

이 패치로 다음의 취약점이 완화됩니다.

취약점 CVE-2017-1002101은 subpath 볼륨 마운트를 사용하는 컨테이너의 볼륨 외부 파일 액세스가 가능하다는 문제를 안고 있습니다. 즉, PodSecurityPolicy로 컨테이너의 hostpath 볼륨 액세스를 차단하는 경우 포드 업데이트나 생성이 가능한 공격자가 다른 볼륨 유형으로 임의의 hostpath를 마운트할 수 있게 됩니다.

취약점 CVE-2017-1002102는 특정 볼륨 유형(보안 비밀, 구성 맵, 예상 볼륨 또는 하향식 API 볼륨 등)을 사용하는 컨테이너의 볼륨 외부 파일 삭제가 가능하다는 문제를 안고 있습니다. 즉, 이러한 볼륨 유형 중 하나를 사용하는 컨테이너가 손상되거나 신뢰할 수 없는 사용자의 포드 생성이 허용되는 경우 공격자가 이러한 컨테이너로 호스트상의 임의 파일을 삭제할 수 있게 됩니다.

해결책에 대한 자세한 내용은 Kubernetes 블로그 글을 참조하세요.

높음
이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

Kubernetes Engine