IL-0052 JW Player · 2019-03-19 (게시일)kubernetesawssecurity
JW Player 데브옵스 팀은 개발 클러스터와 스테이징 클러스터에서 암호화폐 채굴기를 발견했다. 프로덕션 클러스터는 영향을 받지 않았고, 고객 데이터에 접근하거나 유출된 일도 없었으며, 서비스도 멈추지 않았다.
처음 신호는 Datadog 의 높은 load average 경보였다. 팀은 정상 서비스의 부하로 보고 경보를 뮤트했고, 3일 차에야 노드에 들어가 gcc 라는 이름으로 위장한 채굴기를 찾았다. 채굴기는 마스터를 포함한 모든 노드에서 CPU 코어 하나를 100% 쓰고 있었다. 프로세스의 부모는 Weave Scope 였다. XMRig 를 검색해 찾은 소스로 이 프로그램이 Monero 채굴만 한다는 것을 확인했다.
공격 경로를 찾으려고 같은 배포 방식으로 격리된 새 클러스터를 띄워 보니, Weave Scope 로드 밸런서의 보안 그룹이 인터넷 전체에 열려 있었다. 로드 밸런서 URL 을 아는 사람은 누구나 인증 없이 Weave Scope 대시보드에 들어갈 수 있었다. Weave Scope 에는 실행 중인 컨테이너에 셸을 여는 기능이 있다. 배포에는 로드 밸런서를 내부용으로 만드는 annotation 이 빠져 있었다. weave-scope 컨테이너는 --privileged 로 돌았다. 호스트 루트 파일 시스템의 파일이 컨테이너에 마운트돼 있었고, 컨테이너는 root 사용자로 실행됐다. 이 조합 때문에 컨테이너 밖으로 빠져나가 호스트에서 임의 코드를 실행할 수 있었다. 팀은 scope-agent 컨테이너에서 같은 방식으로 호스트에 root 권한 스크립트를 띄워 공격을 재현했다. scope-agent 는 지표를 모으려고 모든 노드에서 돌기 때문에 채굴기도 모든 노드에서 돌 수 있었다.
AWS CloudTrail 로그를 보니 쿠버네티스가 로드 밸런서를 만들 때 0.0.0.0/0 에서 80 포트를 허용하는 보안 그룹을 연결했다. 팀은 AWS 콘솔에서 이 보안 그룹을 손으로 고쳤지만, 쿠버네티스가 얼마 뒤 그 수정을 되돌렸다. 쿠버네티스 기여자는 GitHub 이슈에서 ELB 는 쿠버네티스가 관리하는 리소스이므로 손으로 고치면 안 된다고 설명했다.
피해 조사에서 호스트에 외부 로그인은 없었고, 침입자는 쿠버네티스 API 에 접근하지 않았다. 로드 밸런서 URL 은 공유된 적이 없었고, 자동화된 크롤러가 대시보드를 찾아냈다. RBAC 가 Weave Scope 의 권한을 weave 네임스페이스로 묶어 두었고, 노드에는 민감한 정보가 없었다. 피해는 개발과 스테이징의 모든 노드에서 CPU 코어 하나가 100% 쓰인 것이 전부였다.
Datadog 이 스테이징 환경의 정규화한 load average 가 높다고 경보를 보냈다. 팀은 원인을 정상 서비스 하나의 부하 증가로 판단했고, 정상 동작으로 여겼다.
다음 날 같은 경보가 개발 환경에서도 울렸다. 그 서비스는 개발과 스테이징 양쪽에서 스케일 업과 스케일 다운을 계속 되풀이하며 경보를 거듭 냈다. 앞선 경보를 이미 살펴본 데다 같은 경보가 쏟아졌으므로, 팀은 부하가 안정될 때까지 이 경보들을 뮤트했다.
3일 차가 되자 두 클러스터에서 나흘 동안 이어진 부하 증가를 더는 정상으로 볼 수 없었다. 엔지니어가 노드 하나에 SSH 로 들어가 top 을 보니 gcc 프로세스가 CPU 를 100% 쓰고 있었다. 이 프로세스는 개발과 스테이징 클러스터의 모든 머신에서 돌고 있었다.
엔지니어는 처음에 사내 배포 도구로 잘못된 컨테이너가 떠서 gcc 를 돌린다고 의심했다. 마스터 노드도 영향을 받았으므로 이 가설은 맞지 않았다. 인프라를 코드로 관리하는 저장소에도 gcc 를 실행하는 설정이나 배포는 없었다. 프로세스를 따라가 보니 부모가 Weave Scope 컨테이너였고, gcc 라는 이름의 파일은 호스트의 루트 디렉터리에서 실행되는 암호화폐 채굴기였다. Weave Scope 로 보니 이 프로세스는 마이닝 풀로 나가는 연결을 맺고 있었다.
Datadog 이 정규화한 load average 경보를 0일 차 21:06 EST 에 스테이징에서, 1일 차 16:53 EST 에 개발 환경에서 보냈다. 팀은 정상 서비스의 부하로 보고 경보를 뮤트했다. 3일 차 17:40 EST 에 부하 증가가 나흘째 이어지자 엔지니어가 노드에 SSH 로 들어가 top 으로 CPU 를 100% 쓰는 gcc 프로세스를 찾았다.
load 기반 모니터링은 클러스터의 이상을 가려내는 좋은 지표가 아니었다. 정상 서비스가 낸 경보 소음이 침입 신호를 가렸다. 쿠버네티스가 관리하는 리소스를 손으로 고치면 쿠버네티스가 그 변경을 되돌린다. 로드 밸런서를 내부용으로 정의하거나 보안 그룹 규칙을 쿠버네티스 YAML 에 넣었어야 했다. 인증 없는 내부 대시보드, --privileged 와 root 로 도는 컨테이너, 호스트 파일 시스템 마운트가 겹치면 열린 로드 밸런서 하나로 모든 노드에서 코드가 실행될 수 있다. 컨테이너를 root 로 돌리는 것처럼 예전 인프라에서 물려받은 결정은 다시 검토해야 한다.
같은 원인 · 공통 클라우드 로드 밸런서
같은 원인 · 공통 클라우드 로드 밸런서
같은 원인 · 공통 클라우드 로드 밸런서
같은 원인 · 공통 클라우드 로드 밸런서
같은 원인 · 공통 클라우드 로드 밸런서