KCSA — Kubernetes Security Associate
A Cluster-Wide RBAC Audit
한국어 원문으로 표시합니다.
목표
클러스터 전체의 RBAC 을 실제로 감사합니다. 와일드카드 권한과 위험 동사를 가진 ClusterRole 을 찾아내고,
서비스어카운트의 실효 권한을 kubectl auth can-i 로 측정하고, 최소 권한 Role 을 새로 만들어
그 경계가 실제로 작동하는지 증명한 뒤, 근거가 담긴 감사 리포트를 작성합니다.
왜 중요한가
RBAC 감사가 어려운 이유는 매니페스트를 읽는 것만으로는 답이 안 나오기 때문입니다.
한 주체에 여러 바인딩이 붙고, 그룹을 통해 상속되고, 기본 제공 ClusterRole 이 aggregation 으로
합쳐지기도 합니다. 머리로 계산하지 말고 apiserver 에게 물어보세요.
kubectl auth can-i --list --as= 가 그 도구입니다.
와일드카드를 따로 세는 이유가 있습니다. resources: ["*"] 는 지금 있는 리소스뿐 아니라
미래에 CRD 로 추가될 리소스까지 허용합니다. "지금은 안전하다"는 반론이 통하지 않는 이유입니다.
escalate·bind·impersonate 는 특별히 위험한 메타 권한입니다. 이 동사들은 "권한을 주는 권한"
이라, 하나만 있으면 스스로를 관리자로 만들 수 있습니다. RBAC 감사에서 가장 먼저 찾아야 할 대상입니다.
마지막으로 6단계. 최소 권한이란 "필요한 것을 줬다"가 아니라 "필요하지 않은 것이 안 된다" 입니다. 읽기가 되는지만 확인하고 끝내면 반쪽입니다. 삭제가 안 되는지, 다른 네임스페이스에서 안 되는지까지 확인해야 경계를 증명한 것입니다.
단계
- 디렉터리
/root/kcsa-audit와 네임스페이스kcsa-audit를 만들고, 그 네임스페이스에 ServiceAccount 두 개auditor와probe를 만듭니다. 둘 다 이 단계에서는 아무 권한도 붙이지 않습니다. - 클러스터의 모든 ClusterRole 중 규칙 하나 안에서
verbs와resources가 모두*인 것을 찾아 이름만 한 줄에 하나씩/root/kcsa-audit/wildcard-clusterroles.txt에 저장합니다. kubectl auth can-i --list를--as=system:serviceaccount:kcsa-audit:probe와-n kcsa-audit로 실행하고, 그 출력 전체를 가공 없이 그대로/root/kcsa-audit/probe-can-i.txt에 저장합니다.probe는 이 실습이 끝날 때까지 아무 롤도 바인딩하지 않는 비교 기준이므로 나중에라도 권한을 붙이면 안 됩니다.- 클러스터의 모든 ClusterRole 중
verbs에escalate,bind,impersonate중 하나라도 문자 그대로 포함한 것을 찾아 이름만 한 줄에 하나씩/root/kcsa-audit/dangerous-verbs.txt에 저장합니다. system:serviceaccount:kcsa-audit:default로 세 가지를 물어보고 결과를/root/kcsa-audit/default-sa.txt에키=값세 줄로 저장합니다 —create-pods=(네임스페이스kcsa-audit에서 파드 create),get-secrets=(네임스페이스kcsa-audit에서 secrets get),list-nodes=(클러스터 범위 nodes list). 값은yes/no를 그대로 적습니다.- 네임스페이스
kcsa-audit에 Roleconfigmap-reader를 만듭니다 — 규칙은 정확히 하나이며 apiGroups 는 코어 그룹 하나, resources 는configmaps하나, verbs 는get,list,watch셋뿐입니다. 그리고 RoleBindingauditor-configmap-reader로 SAauditor에 바인딩합니다. 만든 뒤auth can-i로 (a)kcsa-audit에서 configmaps list 가 되는지, (b) 같은 곳에서 delete 는 안 되는지, (c)default네임스페이스에서는 list 도 안 되는지 확인합니다. /root/kcsa-audit/rbac-audit-report.md를 작성합니다. 다음 네 줄이 섹션 제목으로 정확히 들어가야 합니다 —## 와일드카드 권한,## 위험 동사,## 기본 서비스어카운트,## 최소 권한 적용. 본문에 다음 다섯 단어가 언급되어야 합니다 —cluster-admin,escalate,impersonate,configmap-reader,auditor. 그리고 2단계에서 센 와일드카드 ClusterRole 개수를wildcard-count=<숫자>형태의 줄로 한 줄 넣습니다.
참고
kubectl get clusterroles -o json | jq -r '.items[] | ... | .metadata.name'형태로 뽑으면 편합니다.select와any를 조합해 규칙 배열을 훑으세요.- 규칙에
verbs나resources가 아예 없는 경우(비리소스 URL 규칙 등)가 있으니// []같은 기본값 처리를 넣어야 안전합니다. - 코어 API 그룹은 이름이 빈 문자열(
"")입니다. - 흔한 실수 1: 2단계에서
verbs만*인 것까지 넣는 것. 두 조건을 같은 규칙 안에서 동시에 만족해야 합니다. - 흔한 실수 2: 3단계에서 출력을 grep 이나 정렬로 가공해 저장하는 것. 채점은 같은 명령을 다시 실행해 대조하므로 그대로 저장해야 합니다.
- 흔한 실수 3: 6단계에서 롤바인딩 대상을
probe로 잡는 것.probe는 기준선이라 권한이 붙으면 3단계가 다시 실패합니다. 롤은auditor에 붙입니다.
감사 작업 공간 준비
디렉터리 /root/kcsa-audit 와 네임스페이스 kcsa-audit 를 만들고, 그 네임스페이스에 ServiceAccount 두 개 auditor 와 probe 를 만듭니다. 둘 다 이 단계에서는 아무 권한도 붙이지 않습니다.
산출물 디렉터리, 네임스페이스, 그리고 서비스어카운트 두 개를 만듭니다. 하나는 나중에 최소 권한 롤을 받을 대상이고, 다른 하나는 끝까지 아무 권한도 받지 않는 비교 기준입니다. 기준이 없으면 '권한이 늘었다'를 말할 수 없습니다.
와일드카드 권한 ClusterRole 찾기
클러스터의 모든 ClusterRole 중 규칙 하나 안에서 verbs 와 resources 가 모두 * 인 것을 찾아 이름만 한 줄에 하나씩 /root/kcsa-audit/wildcard-clusterroles.txt 에 저장합니다.
규칙 하나 안에서 verbs 와 resources 가 모두 * 인 경우를 찾습니다. 규칙은 배열이고 ClusterRole 하나에 여러 개 있을 수 있으니, 어느 하나라도 조건을 만족하면 그 역할을 목록에 넣습니다. jq 의 select 와 any 를 조합하면 한 줄로 됩니다.
권한 없는 서비스어카운트의 기준선 뜨기
kubectl auth can-i --list 를 --as=system:serviceaccount:kcsa-audit:probe 와 -n kcsa-audit 로 실행하고, 그 출력 전체를 가공 없이 그대로 /root/kcsa-audit/probe-can-i.txt 에 저장합니다. probe 는 이 실습이 끝날 때까지 아무 롤도 바인딩하지 않는 비교 기준이므로 나중에라도 권한을 붙이면 안 됩니다.
auth can-i 에는 개별 질문 말고 전체 목록을 뽑는 옵션이 있습니다. 대상은 --as 로 지정하고, 네임스페이스 범위 권한도 보려면 -n 을 함께 줘야 합니다. 출력을 grep 이나 정렬로 가공하지 말고 그대로 저장하세요. 여기 나오는 몇 줄이 '아무 권한도 없는 계정'의 기준선이며, 이 계정에는 끝까지 아무 롤도 붙이면 안 됩니다.
위험 동사 보유 ClusterRole 탐지
클러스터의 모든 ClusterRole 중 verbs 에 escalate, bind, impersonate 중 하나라도 문자 그대로 포함한 것을 찾아 이름만 한 줄에 하나씩 /root/kcsa-audit/dangerous-verbs.txt 에 저장합니다.
세 동사가 각각 무엇을 가능하게 하는지 떠올려 보면 왜 함께 묶어 보는지 이해됩니다. 여기서는 동사가 문자 그대로 적힌 것만 셉니다 — 와일드카드는 이미 앞 단계에서 따로 집계했습니다.
기본 서비스어카운트가 무엇을 할 수 있나
system:serviceaccount:kcsa-audit:default 로 세 가지를 물어보고 결과를 /root/kcsa-audit/default-sa.txt 에 키=값 세 줄로 저장합니다 — create-pods=(네임스페이스 kcsa-audit 에서 파드 create), get-secrets=(네임스페이스 kcsa-audit 에서 secrets get), list-nodes=(클러스터 범위 nodes list). 값은 yes/no 를 그대로 적습니다.
모든 네임스페이스에는 default 서비스어카운트가 자동으로 있고, 파드가 SA 를 지정하지 않으면 이것을 씁니다. 이 계정이 무엇을 할 수 있는지 직접 물어보고 답을 그대로 옮기세요. 클러스터 범위 리소스를 물을 때는 네임스페이스 옵션이 필요 없습니다.
최소 권한 Role 만들고 경계 증명하기
네임스페이스 kcsa-audit 에 Role configmap-reader 를 만듭니다 — 규칙은 정확히 하나이며 apiGroups 는 코어 그룹 하나, resources 는 configmaps 하나, verbs 는 get,list,watch 셋뿐입니다. 그리고 RoleBinding auditor-configmap-reader 로 SA auditor 에 바인딩합니다. 만든 뒤 auth can-i 로 (a) kcsa-audit 에서 configmaps list 가 되는지, (b) 같은 곳에서 delete 는 안 되는지, (c) default 네임스페이스에서는 list 도 안 되는지 확인합니다.
규칙 하나에 필요한 것만 담습니다. 코어 API 그룹은 이름이 빈 문자열입니다. 만든 뒤에는 원하는 것이 되는지뿐 아니라 원하지 않는 것이 안 되는지도 확인해야 최소 권한이라고 말할 수 있습니다 — 특히 다른 네임스페이스에서 안 되는지.
감사 리포트로 묶기
/root/kcsa-audit/rbac-audit-report.md 를 작성합니다. 다음 네 줄이 섹션 제목으로 정확히 들어가야 합니다 — ## 와일드카드 권한, ## 위험 동사, ## 기본 서비스어카운트, ## 최소 권한 적용. 본문에 다음 다섯 단어가 언급되어야 합니다 — cluster-admin, escalate, impersonate, configmap-reader, auditor. 그리고 2단계에서 센 와일드카드 ClusterRole 개수를 wildcard-count=<숫자> 형태의 줄로 한 줄 넣습니다.
앞 단계들의 산출물을 근거로 하나의 문서를 만듭니다. 섹션 제목은 지시된 형태 그대로여야 하고, 2단계에서 센 개수를 숫자로 적는 줄이 하나 필요합니다. 그 숫자는 채점 시점에 다시 계산해 대조합니다.