두 번째 단계에서는 GSA의 자격 증명을 KSA의 비밀로 설정했습니다. 그런 다음, GKE 클러스터 내부에서 해당 비밀을 읽을 수 있다면, 해당 GCP 서비스 계정으로 스케일 업할 수 있습니다.
GKE Workload Identity
Workload Identity를 사용하면 Kubernetes 서비스 계정을 Google 서비스 계정으로 작동하도록 구성할 수 있습니다. Kubernetes 서비스 계정으로 실행되는 Pod는 Google Cloud API에 액세스할 때 자동으로 Google 서비스 계정으로 인증됩니다.
이 동작을 활성화하려면 GCP에서 Workload Identity를 활성화하고 k8s가 흉내 내기를 원하는 GCP SA를 생성해야 합니다.
# You could update instead of creategcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
K8s에서 GCP 권한을 가진 GCP 서비스 계정을 표현하도록 만듭니다:
# Create SA called "gsa2ksa"gcloudiamservice-accountscreategsa2ksa--project=<project-id># Give "roles/iam.securityReviewer" role to the SAgcloudprojectsadd-iam-policy-binding<project-id> \--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \--role "roles/iam.securityReviewer"
클러스터에 연결하고 사용할 서비스 계정을 생성합니다.
# Get k8s credsgcloudcontainerclustersget-credentials<cluster_name>--region=us-central1# Generate our testing namespacekubectlcreatenamespacetesting# Create the KSAkubectlcreateserviceaccountksa2gcp-ntesting
GSA와 KSA를 바인딩(bind)합니다
# Allow the KSA to access the GSA in GCP IAMgcloudiamservice-accountsadd-iam-policy-bindinggsa2ksa@<project-id.iam.gserviceaccount.com \--role roles/iam.workloadIdentityUser \--member "serviceAccount:<project-id>.svc.id.goog[<namespace>/ksa2gcp]"# Indicate to K8s that the SA is able to impersonate the GSAkubectlannotateserviceaccountksa2gcp \--namespace testing \iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
KSA와 함께 pod를 실행하고 GSA에 대한 액세스를 확인하십시오:
# If using Autopilot remove the nodeSelector stuff!echo"apiVersion: v1kind: Podmetadata:name: workload-identity-testnamespace: <namespace>spec:containers:- image: google/cloud-sdk:slimname: workload-identity-testcommand: ['sleep','infinity']serviceAccountName: ksa2gcpnodeSelector:iam.gke.io/gke-metadata-server-enabled: 'true'"|kubectlapply-f-# Get inside the podkubectlexec-itworkload-identity-test \--namespace testing \-- /bin/bash# Check you can access the GSA from insie the pod withcurl-H"Metadata-Flavor: Google"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/emailgcloudauthlist
K8s 내부의 공격자로서는 SA를 검색해야 합니다. 이때 SA에 iam.gke.io/gcp-service-account 주석이 있는지 확인해야 합니다. 이 주석이 있다면 SA가 GCP에서 무언가에 액세스할 수 있다는 것을 나타냅니다. 또 다른 옵션은 클러스터 내의 각 KSA를 남용하고 액세스 권한이 있는지 확인하는 것입니다.
GCP에서는 항상 바인딩을 열거하고 Kubernetes 내부의 SA에게 어떤 액세스를 부여하는지 파악하는 것이 흥미로울 수 있습니다.
다음은 모든 pod 정의를 반복하면서 해당 주석을 찾는 스크립트입니다:
for ns in`kubectlgetnamespaces-ocustom-columns=NAME:.metadata.name|grep-vNAME`; dofor pod in`kubectlgetpods-n "$ns" -ocustom-columns=NAME:.metadata.name|grep-vNAME`; doecho"Pod: $ns/$pod"kubectlgetpod"$pod"-n"$ns"-oyaml|grep"gcp-service-account"echo""echo""donedone|grep-B1"gcp-service-account"
AWS
Kiam & Kube2IAM (IAM role for Pods)
Pods에 IAM 역할을 부여하는 (오래된) 방법은 Kiam 또는 Kube2IAM서버를 사용하는 것입니다. 기본적으로 클러스터 내에서 특권 IAM 역할을 가진 데몬셋을 실행해야 합니다. 이 데몬셋은 필요한 팟에 IAM 역할에 액세스할 수 있도록 제공할 것입니다.
먼저 네임스페이스 내에서 액세스할 수 있는 역할을 구성해야 하며, 이를 네임스페이스 객체 내의 주석을 사용하여 수행합니다:
공격자로서, 만약 이러한 주석들을 pods나 namespaces에서 찾거나 kiam/kube2iam 서버가 실행 중이라면 (아마도 kube-system에) 이미 pods에서 사용된 모든 역할을 그리고 더 많은 것들을 (AWS 계정에 액세스 권한이 있다면 역할을 나열할 수 있음) 가장할 수 있습니다.
IAM 역할을 사용하여 Pod 생성
지정해야 하는 IAM 역할은 kiam/kube2iam 역할과 동일한 AWS 계정에 있어야 하며 해당 역할에 액세스할 수 있어야 합니다.
IAM 역할과 SA 간에 신뢰 관계를 생성합니다 (또는 역할에 액세스 권한을 부여하는 네임스페이스). 신뢰 관계는 주로 OIDC 공급자 이름, 네임스페이스 이름 및 SA 이름을 확인합니다.
마지막으로, 역할의 ARN을 나타내는 주석이 있는 SA를 생성하고 해당 SA로 실행되는 팟은 역할의 토큰에 액세스할 수 있습니다. 토큰은 파일 안에 쓰여지며 경로는 **AWS_WEB_IDENTITY_TOKEN_FILE**에 지정됩니다 (기본값: /var/run/secrets/eks.amazonaws.com/serviceaccount/token).
# Create a service account with a rolecat>my-service-account.yaml<<EOFapiVersion: v1kind: ServiceAccountmetadata:name: my-service-accountnamespace: defaultannotations:eks.amazonaws.com/role-arn: arn:aws:iam::318142138553:role/EKSOIDCTestingEOFkubectlapply-fmy-service-account.yaml# Add a role to an existent service accountkubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com/role-arn=arn:aws:iam::$account_id:role/my-role
/var/run/secrets/eks.amazonaws.com/serviceaccount/token에서 토큰을 사용하여 aws를 가져오려면 다음을 실행하십시오:
for ns in`kubectlgetnamespaces-ocustom-columns=NAME:.metadata.name|grep-vNAME`; dofor pod in`kubectlgetpods-n "$ns" -ocustom-columns=NAME:.metadata.name|grep-vNAME`; doecho"Pod: $ns/$pod"kubectlgetpod"$pod"-n"$ns"-oyaml|grep"amazonaws.com"echo""echo""donefor sa in`kubectlgetserviceaccounts-n "$ns" -ocustom-columns=NAME:.metadata.name|grep-vNAME`; doecho"SA: $ns/$sa"kubectlgetserviceaccount"$sa"-n"$ns"-oyaml|grep"amazonaws.com"echo""echo""donedone|grep-B1"amazonaws.com"
노드 IAM 역할
이전 섹션은 파드로 IAM 역할을 어떻게 도용하는지에 대한 것이었지만, K8s 클러스터의 노드는 클라우드 내부의 인스턴스가 될 것입니다. 이는 노드가 새로운 IAM 역할을 가질 가능성이 매우 높다는 것을 의미합니다. (일반적으로 K8s 클러스터의 모든 노드가 동일한 IAM 역할을 가지므로 각 노드를 확인해 볼 가치가 없을 수도 있음을 유의하세요).
그러나 노드에서 메타데이터 엔드포인트에 액세스하려면 노드에 있어야 합니다 (ssh 세션?) 또는 적어도 동일한 네트워크에 있어야 합니다: