Skip to content

Description

Securely acccess AWS services from GKE through Workload Identity without using long-living AWS credentials (AKSK), we leverage gtoken to automatically inject AWS credentials into pods.

Prerequisite

Deployment

# 0. Configure environment variables for following steps
export PROJECT_ID=<Project ID>
export CLUSTER_NAME=<The name of GKE cluster>
export GSA_NAME=<The name of Service Account>
export GSA_ID=$(gcloud iam service-accounts describe --format json ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com  | jq -r '.uniqueId')
export KSA_NAME=<The name of Service Account in Kubernetes>
export KSA_NAMESPACE=<The name of Namespace where the Service Account is located>
export AWS_ROLE_NAME=<The name of AWS role>
export AWS_POLICY_NAME=<The name of policy that was granted to AWS role>


# 1. Create the Service Account and grant necessary permissions
gcloud iam service-accounts create ${GSA_NAME} \
    --description="Service account for Workload Identity and test accessing AWS resource." \
    --display-name=${GSA_NAME}

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/container.admin" \
    --role="roles/resourcemanager.projectIamAdmin"


# 2. Create the AWS role for demo purpose (Customize your role if need to)
# Add trusted principal for Google account
cat > gcp-trust-policy.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "accounts.google.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "accounts.google.com:sub": "${GSA_ID}"
        }
      }
    }
  ]
}
EOF
# Create the AWS role 
aws iam create-role --role-name ${AWS_ROLE_NAME} --assume-role-policy-document file://gcp-trust-policy.json
# Attache the policy to the AWS role
aws iam attach-role-policy --role-name ${AWS_ROLE_NAME} --policy-arn arn:aws:iam::aws:policy/${AWS_POLICY_NAME}
# Retrieve ARN from the AWS role
export AWS_ROLE_ARN=$(aws iam get-role --role-name ${AWS_ROLE_NAME} --query Role.Arn --output text)


# 3. Create the namespace and related services account in GKE
# Create the namespace
kubectl create namespace ${KSA_NAMESPACE}
# Create the service account
kubectl create serviceaccount --namespace ${KSA_NAMESPACE} ${KSA_NAME}

# Binding Service Account with Workload Identity
gcloud iam service-accounts add-iam-policy-binding ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:${PROJECT_ID}.svc.id.goog[${KSA_NAMESPACE}/${KSA_NAME}]"

kubectl annotate serviceaccount --namespace ${KSA_NAMESPACE} ${KSA_NAME} \
  iam.gke.io/gcp-service-account=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

kubectl annotate serviceaccount --namespace ${KSA_NAMESPACE} ${KSA_NAME} \
  amazonaws.com/role-arn=${AWS_ROLE_ARN}


# 4. Run demo to verify services accessing
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: ${KSA_NAMESPACE}
spec:
  serviceAccountName: ${KSA_NAME}
  containers:
  - name: test-pod
    image: amazon/aws-cli
    command: ["tail", "-f", "/dev/null"]
EOF

# Login into Pod Shell
kubectl exec -it pods/test-pod -- sh
# In Pod shell: check AWS assumed role
aws sts get-caller-identity


References