LabHub
배우기 러닝패스 코스

CKS — Kubernetes Security Specialist

Narrowing RBAC and Blocking Tokens

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

클러스터에서 과도한 권한을 가진 역할을 직접 찾아내고, 최소 권한 Role 로 대체한 뒤 그 결과를 API 서버에 물어서 확인합니다. ServiceAccount 토큰이 파드에 자동으로 들어가는 경로도 전부 닫습니다.

왜 중요한가

RBAC 은 "누가 무엇을 할 수 있는가"를 정하지만, 정작 그것을 사람이 읽어서 검증하기는 매우 어렵습니다. ClusterRole 하나에 resources: ["*"] 가 섞여 있으면 그 역할은 앞으로 추가될 CRD 까지 전부 포함하게 되고, 그 사실을 아무도 눈치채지 못합니다. 그래서 하드닝의 실제 작업은 "정책을 잘 쓰는 일"이 아니라 "지금 무엇이 허용돼 있는지 목록으로 뽑는 일"에서 시작합니다.

두 번째 축은 자격증명입니다. 파드는 기본적으로 토큰을 받습니다. 애플리케이션이 API 를 쓰지 않아도 받습니다. 파드 하나가 뚫리면 공격자는 즉시 클러스터 자격증명 하나를 얻는 셈입니다. automountServiceAccountToken: false 는 한 줄이지만 침해 시 확산 범위를 크게 줄입니다.

단계

  1. 네임스페이스 cks-rbac 을 만들고 그 안에 ServiceAccount report-sa 를 만든다.
  2. 클러스터의 모든 ClusterRole 중 rules[].verbs* 가 들어간 것들의 이름만 골라 사전순으로 정렬해 /root/cks-rbac-hardening/wildcard-roles.txt 에 한 줄에 하나씩 저장한다.
  3. ClusterRole cks-pod-reader 를 만든다. apiGroups 는 코어 그룹(빈 문자열) 하나, resources 는 podspods/log, verbs 는 get·list·watch 세 개다. 어느 필드에도 * 가 있으면 안 된다.
  4. 클러스터의 모든 ClusterRole 중 escalate, bind, impersonate 중 하나라도 verbs 에 가진 것들의 이름만 정렬해 /root/cks-rbac-hardening/danger-verb-roles.txt 에 저장한다.
  5. cks-rbac 네임스페이스에 RoleBinding report-sa-pod-readerreport-sa 에 ClusterRole cks-pod-reader 를 바인딩한다. 그다음 아래 두 질문의 답(yes 또는 no)을 순서대로 /root/cks-rbac-hardening/can-i.txt 에 두 줄로 저장한다. 첫 줄은 report-sacks-rbac 에서 pods 를 list 할 수 있는지, 두 줄은 report-sacks-rbac 에서 secrets 를 get 할 수 있는지다.
  6. cks-rbac 에 파드 report(이미지 nginx:1.27-alpine)를 만든다. serviceAccountNamereport-sa, 파드 스펙의 automountServiceAccountTokenfalse 다.
  7. cks-rbac 에 Secret report-sa-token 을 만든다. 타입은 kubernetes.io/service-account-token, 어노테이션 kubernetes.io/service-account.name 값은 report-sa 다. 그리고 파드 token-app(이미지 nginx:1.27-alpine, serviceAccountName report-sa)을 만들되 토큰은 projected 볼륨으로 받는다. 볼륨 이름은 api-token, serviceAccountToken 소스의 audienceapi, expirationSeconds3600, pathtoken 이다.
  8. cks-rbacdefault ServiceAccount 에 automountServiceAccountToken: false 를 설정하고, default SA 가 이 네임스페이스에서 pods 를 list 할 수 없다는 것(no)을 /root/cks-rbac-hardening/default-sa-can-i.txt 에 한 줄로 저장한다.

참고

네임스페이스와 전용 ServiceAccount

네임스페이스 cks-rbac 을 만들고 그 안에 ServiceAccount report-sa 를 만든다.

kubectl create serviceaccount 로 만듭니다. 네임스페이스를 먼저 만들지 않으면 SA 생성이 실패합니다.

와일드카드 ClusterRole 찾아내기

클러스터의 모든 ClusterRole 중 rules[].verbs* 가 들어간 것들의 이름만 골라 사전순으로 정렬해 /root/cks-rbac-hardening/wildcard-roles.txt 에 한 줄에 하나씩 저장한다.

kubectl get clusterrole -o json 을 jq 로 훑어 rules 중 verbs 에 * 가 들어간 것을 고릅니다. 결과는 이름만, 한 줄에 하나씩 저장합니다.

좁힌 ClusterRole 작성

ClusterRole cks-pod-reader 를 만든다. apiGroups 는 코어 그룹(빈 문자열) 하나, resources 는 podspods/log, verbs 는 get·list·watch 세 개다. 어느 필드에도 * 가 있으면 안 된다.

kubectl create clusterrole --resource= --verb= 로 초안을 만들 수 있습니다. 와일드카드는 apiGroups·resources·verbs 어디에도 쓰지 않습니다.

위험 동사를 가진 역할 골라내기

클러스터의 모든 ClusterRole 중 escalate, bind, impersonate 중 하나라도 verbs 에 가진 것들의 이름만 정렬해 /root/cks-rbac-hardening/danger-verb-roles.txt 에 저장한다.

escalate, bind, impersonate 세 동사 중 하나라도 가진 ClusterRole 이 대상입니다. 02 단계와 같은 방식으로 이름만 정렬해 저장합니다.

auth can-i 로 결과 확인

cks-rbac 네임스페이스에 RoleBinding report-sa-pod-readerreport-sa 에 ClusterRole cks-pod-reader 를 바인딩한다. 그다음 아래 두 질문의 답(yes 또는 no)을 순서대로 /root/cks-rbac-hardening/can-i.txt 에 두 줄로 저장한다. 첫 줄은 report-sacks-rbac 에서 pods 를 list 할 수 있는지, 두 줄은 report-sacks-rbac 에서 secrets 를 get 할 수 있는지다.

--as=system:serviceaccount:<네임스페이스>:<이름> 형식으로 주체를 가장합니다. 출력은 yes 또는 no 한 줄뿐이니 그대로 파일에 모으면 됩니다.

파드에서 토큰 자동 마운트 끄기

cks-rbac 에 파드 report(이미지 nginx:1.27-alpine)를 만든다. serviceAccountNamereport-sa, 파드 스펙의 automountServiceAccountTokenfalse 다.

automountServiceAccountToken 은 파드 스펙 최상위(spec 바로 아래) 필드입니다. 컨테이너 안이 아닙니다.

수동 토큰 Secret 과 projected 토큰

cks-rbac 에 Secret report-sa-token 을 만든다. 타입은 kubernetes.io/service-account-token, 어노테이션 kubernetes.io/service-account.name 값은 report-sa 다. 그리고 파드 token-app(이미지 nginx:1.27-alpine, serviceAccountName report-sa)을 만들되 토큰은 projected 볼륨으로 받는다. 볼륨 이름은 api-token, serviceAccountToken 소스의 audienceapi, expirationSeconds3600, pathtoken 이다.

수동 토큰 Secret 은 타입이 kubernetes.io/service-account-token 이고 kubernetes.io/service-account.name 어노테이션으로 주인을 지정합니다. 반대편은 파드의 projected 볼륨에 serviceAccountToken 소스를 두고 audience 와 만료 시간을 붙입니다.

default ServiceAccount 무력화

cks-rbacdefault ServiceAccount 에 automountServiceAccountToken: false 를 설정하고, default SA 가 이 네임스페이스에서 pods 를 list 할 수 없다는 것(no)을 /root/cks-rbac-hardening/default-sa-can-i.txt 에 한 줄로 저장한다.

kubectl patch serviceaccount default -n <네임스페이스> -p '...' 로 한 줄에 끝납니다. 그리고 default SA 가 정말 아무 권한이 없는지 can-i 로 확인하세요.