Automate GCP IAM for GKE
Otterize automates GCP IAM roles and policies for your GCP GKE workloads, all in Kubernetes.
In this tutorial, we will:
- Optionally, spin up a GKE cluster.
- Deploy a server pod that uploads files to Google Cloud Storage, and a client pod that submits files to the server app.
- Label the server pod, telling the credentials operator to link its Kubernetes ServiceAccount with a GCP service account created for it, such that GCP workload identity federation can recognize the pod.
- Create a
ClientIntents
resource allowing the server pod to upload to GCS, that tells the intents operator to update the previously-created GCP service account with the relevant permissions. - See that the files have been uploaded successfully.
Prerequisites
Already have Otterize deployed with the IAM integration configured on your cluster? Skip to the tutorial.
1. Create a GCP GKE cluster
Before you start, you'll need an GCP GKE cluster. The cluster should have Workload identity federation and Config Connector installed.
How to set up a GKE cluster using gcloud CLI
Run the following commands to configure your project and create your cluster. Don't have gcloud? Install it now.
Create a project and set the default gcloud configurations
export PROJECT_NAME=otterize-gcp-demo
export REGION=us-central1
gcloud projects create $PROJECT_NAME
gcloud config set project $PROJECT_NAME
gcloud config set compute/region $REGIONEnable the relevant APIS
gcloud services enable container.googleapis.com iamcredentials.googleapis.com cloudresourcemanager.googleapis.com
Ensure that you have at least the following IAM roles: [roles/container.admin, roles/iam.serviceAccountAdmin]
Create a new GKE cluster with workload identity and config connector enabled
gcloud container clusters create otterize-iam-gke-tutorial \
--release-channel regular \
--addons ConfigConnector \
--workload-pool=$PROJECT_NAME.svc.id.goog \
--logging=SYSTEM \
--monitoring=SYSTEM
Don't forget to configure your kubeconfig for your cluster. If using the example cluster above, use this command:
gcloud container clusters get-credentials otterize-iam-gke-tutorial
2. Deploy Otterize for GCP IAM
To deploy Otterize, head over to Otterize Cloud and:
Create a Kubernetes cluster on the Integrations page, and follow the instructions. Make sure to enable enforcement mode for this tutorial. If you already have a Kubernetes cluster connected, skip this step.
Create an GCP IAM integration on the Integrations page.
If you are using the cluster from the previous step, the cluster name is otterize-iam-gke-tutorial
and the region is us-central1
.
Once the GCP integration is configured, you'll be presented with instructions for configuring your Otterize integration with GCP IAM support.
- If you don't have a GCP service account for config connector, make sure to toggle "I don't have Config Connector on my cluster". This will tell terraform to create a GCP service account for config connector and give it the necessary permissions to manage GCP IAM. Choose this option if you deployed your GKE cluster using the instructions in the previous step.
- If you have a GCP service account for config connector, keep the "I have Config Connector deployed with a GCP service account" toggle and provide the service account name. This will tell terraform to use the existing service account and give it the necessary permissions to manage GCP IAM.
After Terraform has configured your cluster, click Next and you'll be presented with the configuration for deploying Otterize. Since you now have the GCP integration enabled, you need to redeploy Otterize with GCP integration enabled flag, providing it the client ID for the managed identity created during the terraform installation.
See how to manually configure Config Connector on your cluster for Otterize
You may also manually configure your clusters config connector to be used with Otterize.
- Configure the GCP service account for Config Connector
- Create a service account for Config Connector
gcloud iam service-accounts create [CONFIG_CONNECTOR_SA_NAME]
- Add the following permissions to the service accountYou can use the following command to add permissions to the service account
roles/iam.roleAdmin
roles/iam.securityAdmin
roles/iam.serviceAccountAdmin
roles/iam.workloadIdentityUsergcloud projects add-iam-policy-binding $PROJECT_NAME \
--member="serviceAccount:[CONFIG_CONNECTOR_SA_NAME]@$PROJECT_NAME.iam.gserviceaccount.com" \
--role="roles/iam.roleAdmin" - Bind the service account to workload identity
gcloud iam service-accounts add-iam-policy-binding \
[CONFIG_CONNECTOR_SA_NAME]@$PROJECT_NAME.iam.gserviceaccount.com \
--member="serviceAccount:$PROJECT_NAME.svc.id.goog[cnrm-system/cnrm-controller-manager]" \
--role="roles/iam.workloadIdentityUser"
- Create a service account for Config Connector
- Apply the following YAML to your kubernetes cluster to finish the config connector configuration.
apiVersion: core.cnrm.cloud.google.com/v1beta1
kind: ConfigConnector
metadata:
name: configconnector.core.cnrm.cloud.google.com
spec:
mode: cluster
googleServiceAccount: "[CONFIG_CONNECTOR_SA_NAME]@$PROJECT_NAME.iam.gserviceaccount.com"
Tutorial
Create a GCS bucket for the server to use
First, we need to pick a bucket name. Because GCS buckets are globally unique, we will save the bucket name in an environment variable for use later.
export BUCKET_NAME=otterize-tutorial-bucket-`date +%s`
echo $BUCKET_NAME
gcloud config set project $PROJECT_NAME
gsutil mb -c standard -l us-central1 gs://$BUCKET_NAME
Deploy the sample server and client
kubectl create namespace otterize-tutorial-gcp-iam
kubectl apply -n otterize-tutorial-gcp-iam -f https://docs.otterize.com/code-examples/gcp-iam-gke/client-and-server.yaml
kubectl patch deployment -n otterize-tutorial-gcp-iam server --type='json' -p="[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/env\", \"value\": [{\"name\": \"BUCKET_NAME\", \"value\": \"$BUCKET_NAME\"}]}]"
Expand to see the deployment YAML
apiVersion: v1
kind: Namespace
metadata:
name: otterize-tutorial-gcp-iam
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: server
namespace: otterize-tutorial-gcp-iam
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
namespace: otterize-tutorial-gcp-iam
spec:
replicas: 1
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
containers:
- name: client
imagePullPolicy: Always
image: 'public.ecr.aws/e3b4k2v5/gcp-tutorial:client'
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: server
namespace: otterize-tutorial-gcp-iam
spec:
replicas: 1
selector:
matchLabels:
app: server
template:
metadata:
labels:
app: server
spec:
serviceAccountName: server
containers:
- name: server
imagePullPolicy: Always
image: 'public.ecr.aws/e3b4k2v5/gcp-tutorial:server'
ports:
- containerPort: 80
env:
- name: BUCKET_NAME
value: "otterize-demo-bucket"
---
apiVersion: v1
kind: Service
metadata:
name: server
namespace: otterize-tutorial-gcp-iam
spec:
type: ClusterIP
selector:
app: server
ports:
- name: http
port: 80
targetPort: 80
View logs for the server - access denied
The server logs will show that it fails to upload files to the GCS bucket.
kubectl logs -f -n otterize-tutorial-gcp-iam deploy/server
Error 403: <service_account> does not have storage.objects.create access to the Google Cloud Storage object.
Permission 'storage.objects.create' denied on resource (or it may not exist).
Label the server pod to create a GCP service account
Label the server Pod
so that the Otterize credentials operator creates a GCP service account and binds to the pods Kubernetes ServiceAccount.
metadata:
labels:
credentials-operator.otterize.com/create-gcp-sa: "true"
To do this, we won't be labeling the Pod
directly, but instead patching the template
attribute of the Deployment
we created earlier so that it updates the Pod
.
kubectl patch deployment -n otterize-tutorial-gcp-iam server -p '{"spec": {"template":{"metadata":{"labels":{"credentials-operator.otterize.com/create-gcp-sa":"true"}}}} }'
A GCP service account was created and bound to the server's Kubernetes ServiceAccount
Let's inspect the created service account:
gcloud iam service-accounts list --filter="otr-"
The Kubernetes ServiceAccount was annotated with the role ARN
The credentials operator automatically annotated the Kubernetes ServiceAccount for the server pod with the newly created GCP service account.
Let's look at the service account:
kubectl get serviceaccount -n otterize-tutorial-gcp-iam server -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
iam.gke.io/gcp-service-account:
otr-demo-cluster-otteri-3f630f@otterize-gcp-demo.iam.gserviceaccount.com
name: server
Apply intents to create the necessary IAM policy
By annotating the pod, we've created a GCP service account. We now need to specify what we need to access, and the intents operator will bind permissions accordingly.
We will specify the following ClientIntents, granting admin permission to the GCS bucket, and it's nested resources.
apiVersion: k8s.otterize.com/v1alpha3
kind: ClientIntents
metadata:
name: server
namespace: otterize-tutorial-gcp-iam
spec:
service:
name: server
calls:
- name: projects/_/buckets/otterize-tutorial-bucket*
type: gcp
gcpPermissions:
- "storage.admin"
To apply these intents, run the following command:
kubectl apply -n otterize-tutorial-gcp-iam -f https://docs.otterize.com/code-examples/gcp-iam-gke/clientintents.yaml
The server can now upload files to GCS!
Let's look at the server logs again to see that no more errors are being reported:
kubectl logs -f -n otterize-tutorial-gcp-iam deploy/server
{
"status":200,
"host":"server",
"method":"POST",
"uri":"/upload"
}
Let's list the contents of the S3 bucket:
gsutil ls gs://$BUCKET_NAME
gs://otterize-tutorial-bucket-1710338230/testfile.0.txt
gs://otterize-tutorial-bucket-1710338230/testfile.1.txt
gs://otterize-tutorial-bucket-1710338230/testfile.2.txt
gs://otterize-tutorial-bucket-1710338230/testfile.3.txt
What's next?
Try out some of the other quick tutorials to learn about how to use ClientIntents to manage network policies, Istio policies, PostgreSQL access, and more. You can use a single ClientIntents resource to specify all the access required for a pod.
Teardown
To remove the deployed examples run:
kubectl delete namespace otterize-tutorial-gcp-iam
To delete the cluster, if you created the one in this tutorial:
gcloud container clusters delete otterize-iam-gke-tutorial
To empty and delete the GCS bucket created for this tutorial:
gsutil -m rm -r gs://$BUCKET_NAME