diff --git a/docs/features/aws-iam/reference.mdx b/docs/features/aws-iam/reference.mdx index fe0517232..f3141ad79 100644 --- a/docs/features/aws-iam/reference.mdx +++ b/docs/features/aws-iam/reference.mdx @@ -33,7 +33,7 @@ spec: | Key | Description | Default | |------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| -| `credentials-operator.otterize.com/create-aws-role` | By setting to **true** the credential operator will create an unique AWS Role for the associated pod | `false` | +| `credentials-operator.otterize.com/create-aws-role` | When set to **true**, the credential operator will create an unique AWS Role for the associated pod | `false` | ### Helm Chart options diff --git a/docs/features/azure-iam/index.mdx b/docs/features/azure-iam/index.mdx index ae336167b..57c38d971 100644 --- a/docs/features/azure-iam/index.mdx +++ b/docs/features/azure-iam/index.mdx @@ -18,12 +18,6 @@ export const tutorials = [ # Azure IAM -:::info -Azure IAM support is currently a part of our early access program. -Sign up to the [Early Access Beta Program](https://otterize.com/EarlyAccessBetaProgram) and we'll be in touch! -::: - - Otterize can create just-in-time Azure IAM workload identities & role assignments for your workloads running on AKS Kubernetes clusters, greatly simplifying the lifecycle of managing Azure IAM identities and roles. ### Tutorials diff --git a/docs/features/azure-iam/reference.mdx b/docs/features/azure-iam/reference.mdx index 02b0a2e3e..99686d439 100644 --- a/docs/features/azure-iam/reference.mdx +++ b/docs/features/azure-iam/reference.mdx @@ -27,7 +27,7 @@ spec: | Key | Description | Default | |------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| -| `credentials-operator.otterize.com/create-azure-workload-identity` | By setting to **true** the credential operator will create an Azure workload identity the associated pod | `false` | +| `credentials-operator.otterize.com/create-azure-workload-identity` | When set to **true**, the credential operator will create an Azure workload identity the associated pod | `false` | ### Helm Chart options diff --git a/docs/features/gcp-iam/_category_.json b/docs/features/gcp-iam/_category_.json new file mode 100644 index 000000000..5fceb135d --- /dev/null +++ b/docs/features/gcp-iam/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "GCP IAM", + "position": 2, + "collapsed": true, + "customProps": { + "image": "/img/icons/gcp.png" + } +} diff --git a/docs/features/gcp-iam/index.mdx b/docs/features/gcp-iam/index.mdx new file mode 100644 index 000000000..7fd67e71c --- /dev/null +++ b/docs/features/gcp-iam/index.mdx @@ -0,0 +1,64 @@ +--- +sidebar_position: 1 +title: GCP IAM | Overview +hide_table_of_contents: true +hide_title: true +--- + +import DocsLinkCard from "@site/src/components/LinkCard"; + +export const tutorials = [ + { + title: 'Automate GCP IAM for GKE', + description: 'Create just-in-time GCP IAM roles and policies that are kept in sync with your workloads', + url: '/features/gcp-iam/tutorials/gcp-iam-gke' + }, +]; + + +# GCP IAM + +Otterize can create just-in-time GCP IAM workload identities & role assignments for your workloads running on GKE Kubernetes clusters, greatly simplifying the lifecycle of managing GCP IAM identities and roles. + +### Tutorials + +To learn how to use the Intents Operator and Credentials Operator to manage just-in-time GCP IAM access, check out the tutorial. + + + +### How does Otterize work with GCP IAM? + +1. First, the GKE cluster must have: + - Enabled [workload identity federation for GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) + - [Config Connector Installed](https://cloud.google.com/config-connector/docs/how-to/install-upgrade-uninstall) + - [Otterize installed](/overview/installation). +2. To have a role created for a pod, label the pod with `credentials-operator.otterize.com/create-gcp-sa: "true"` +3. The credentials operator will: + - Annotate the pod ServiceAccount and Namespace with the relevant workload identity and Otterize labels. + - Create a GCP ServiceAccount and bind it to the pod ServiceAccount. +4. At this point, the pod is connected to your GCP project through the newly created GCP ServiceAccount but does not have any permissions to perform any action. We will need to create a ClientIntents YAML for the access the service requires and apply it to our cluster. Below is an example of a ClientIntents file for accessing a GCS bucket. View the [reference](/features/gcp-iam/reference) to learn more about the GCP IAM ClientIntents syntax. +5. Once the intent is applied, the intents operator will create a new GCP IAM policy, which will be attached to the service’s role with the appropriate access. +6. Done! + +```yaml +apiVersion: k8s.otterize.com/v1alpha3 +kind: ClientIntents +metadata: + name: server + namespace: my-namespace +spec: + service: + name: server + calls: + - name: projects/_/buckets/otterize-demo-bucket* + type: gcp + gcpPermissions: + - "storage.admin" +``` + +### Automatically generating ClientIntents for GCP IAM + +Figuring out which access you need for GCP can be a painful, trial and error process, and something you _must_ do if you're tightening production access. + +Otterize is getting ready to release support for using existing traffic to generate least-privilege IAM policies. Keen to try this out as part of early access? Sign up to the [Early Access Beta Program](https://otterize.com/EarlyAccessBetaProgram) and we'll be in touch! + diff --git a/docs/features/gcp-iam/reference.mdx b/docs/features/gcp-iam/reference.mdx new file mode 100644 index 000000000..124228c52 --- /dev/null +++ b/docs/features/gcp-iam/reference.mdx @@ -0,0 +1,49 @@ +--- +sidebar_position: 3 +title: Reference +--- + +### ClientIntents example (YAML) + +```yaml +apiVersion: k8s.otterize.com/v1alpha3 +kind: ClientIntents +metadata: + name: server +spec: + service: + # The name of the pod that will be granted access + name: server + calls: + # The GCP resource name as defined in the linked documentation below + # Wildcards can be used in the end of the resource name to match multiple and nested resources + - name: projects/_/buckets/otterize-demo-bucket* + type: gcp + # one or more GCP Roles that will be provided to the specified resources + gcpPermissions: + - "storage.admin" + # Multiple call definitions can be defined for a single service. + - name: projects/_/buckets/otterize-read-only-bucket* + type: gcp + gcpPermissions: + - "storage.objectViewer" +``` + +### GCP documentation references + - For GCP resource names, refer to the [Resource Name Format](https://cloud.google.com/iam/docs/conditions-resource-attributes#resource-name) documentation. + - For GCP roles and permissions, refer to the [Predefined roles and permissions](https://cloud.google.com/iam/docs/understanding-roles#predefined_roles) documentation. + +### Annotations + +| Key | Description | Default | +|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| `credentials-operator.otterize.com/create-gcp-sa` | When set to **true**, the credential operator will create a unique GCP service account for the K8S ServiceAccount of the associated pod | `false` | + + +### Helm Chart options + +| Key | Description | Default | +|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| `global.gcp.enabled` | Enable or disable GCP integration | `false` | + +View the [Helm chart reference](/reference/configuration/otterize-chart) for all other options \ No newline at end of file diff --git a/docs/features/gcp-iam/tutorials/_category_.json b/docs/features/gcp-iam/tutorials/_category_.json new file mode 100644 index 000000000..bdfe77bf2 --- /dev/null +++ b/docs/features/gcp-iam/tutorials/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Tutorials", + "position": 2, + "collapsed": false +} diff --git a/docs/features/gcp-iam/tutorials/gcp-iam-gke.mdx b/docs/features/gcp-iam/tutorials/gcp-iam-gke.mdx new file mode 100644 index 000000000..ad0d66739 --- /dev/null +++ b/docs/features/gcp-iam/tutorials/gcp-iam-gke.mdx @@ -0,0 +1,276 @@ +--- +sidebar_position: 2 +title: Automate GCP IAM for GKE +image: /img/quick-tutorials/gcp-iam-gke/social.png +--- + + +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.](#tutorial) + +### 1. Create a GCP GKE cluster +Before you start, you'll need an GCP GKE cluster. The cluster should have +[Workload identity federation](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) and +[Config Connector](https://cloud.google.com/config-connector/docs/how-to/install-upgrade-uninstall) 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.](https://cloud.google.com/sdk/docs/install) + + 1. Create a project and Set the default gcloud configurations + ```shell + gcloud projects create [PROJECT_NAME] + gcloud config set project [PROJECT_NAME] + gcloud config set compute/region [us-central1] + ``` + + 2. Enable the relevant APIS + ```shell + gcloud services enable container.googleapis.com iamcredentials.googleapis.com cloudresourcemanager.googleapis.com + ``` + Ensure that you have at least the following [IAM roles](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles): + [roles/container.admin, roles/iam.serviceAccountAdmin] + + 3. Create a new GKE cluster with workload identity and config connector enabled + ```shell + 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: +```bash +gcloud container clusters get-credentials otterize-iam-gke-tutorial +``` + +### 2. Deploy Otterize for GCP IAM +To deploy Otterize, head over to [Otterize Cloud](https://app.otterize.com) and: + +1. Create a Kubernetes cluster on the [Integrations page](https://app.otterize.com/integrations), and follow the instructions. *Make sure to enable enforcement mode for this tutorial.* If you already have a Kubernetes cluster connected, skip this step. + +2. Create an GCP IAM integration on the [Integrations page](https://app.otterize.com/integrations). + +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 dont 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. + - **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. +1. Configure the GCP service account for Config Connector + - Create a service account for Config Connector + ```shell + gcloud iam service-accounts create [CONFIG_CONNECTOR_SA_NAME] + ``` + - Add the following permissions to the service account + ``` + roles/iam.roleAdmin + roles/iam.securityAdmin + roles/iam.serviceAccountAdmin + roles/iam.workloadIdentityUser + ``` + You can use the following command to add permissions to the service account + ```shell + gcloud 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 + ```shell + 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" + ``` +2. Apply the following YAML to your kubernetes cluster to finish the config connector configuration. + ```yaml + 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. + +```bash +export BUCKET_NAME=otterize-tutorial-bucket-`date +%s` +echo $BUCKET_NAME +``` + +```bash +gcloud config set project [PROJECT_NAME] +gsutil mb -c standard -l us-central1 gs://$BUCKET_NAME +``` + +### Deploy the sample server and client + +```shell +kubectl create namespace otterize-tutorial-gcp-iam +kubectl apply -n otterize-tutorial-gcp-iam -f ${ABSOLUTE_URL}/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 + +```yaml +{@include: ../../../../static/code-examples/gcp-iam-gke/client-and-server.yaml} +``` + +
+ +### View logs for the server - access denied +The server logs will show that it fails to upload files to the GCS bucket. + +```bash +kubectl logs -f -n otterize-tutorial-gcp-iam deploy/server +``` + +```bash +Error 403: does not have storage.objects.create access to the Google Cloud Storage object. +# highlight-next-line +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. +```yaml +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`. + +```bash +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: +```bash +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: +```bash +kubectl get serviceaccount -n otterize-tutorial-gcp-iam server -o yaml +``` + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + # highlight-next-line + iam.gke.io/gcp-service-account: + # highlight-next-line + 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. +```yaml +{@include: ../../../../static/code-examples/gcp-iam-gke/clientintents.yaml} +``` + +To apply these intents, run the following command: +```bash +kubectl apply -n otterize-tutorial-gcp-iam -f ${ABSOLUTE_URL}/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: +```bash +kubectl logs -f -n otterize-tutorial-gcp-iam deploy/server +``` + +```json +{ + # highlight-next-line + "status":200, + "host":"server", + "method":"POST", + "uri":"/upload" +} +``` + +Let's list the contents of the S3 bucket: +```bash +gsutil ls gs://$BUCKET_NAME +``` + +```bash +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: + +```bash +kubectl delete namespace otterize-tutorial-gcp-iam +``` + +To delete the cluster, if you created the one in this tutorial: +```bash +gcloud container clusters delete otterize-iam-gke-tutorial +``` + +To empty and delete the GCS bucket created for this tutorial: +```bash +gsutil -m rm -r gs://$BUCKET_NAME +``` diff --git a/static/code-examples/gcp-iam-gke/client-and-server.yaml b/static/code-examples/gcp-iam-gke/client-and-server.yaml new file mode 100644 index 000000000..a0d3f675e --- /dev/null +++ b/static/code-examples/gcp-iam-gke/client-and-server.yaml @@ -0,0 +1,72 @@ +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 \ No newline at end of file diff --git a/static/code-examples/gcp-iam-gke/clientintents.yaml b/static/code-examples/gcp-iam-gke/clientintents.yaml new file mode 100644 index 000000000..f1b38fe51 --- /dev/null +++ b/static/code-examples/gcp-iam-gke/clientintents.yaml @@ -0,0 +1,13 @@ +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" \ No newline at end of file diff --git a/static/img/icons/gcp.png b/static/img/icons/gcp.png new file mode 100644 index 000000000..c2421bbb6 Binary files /dev/null and b/static/img/icons/gcp.png differ diff --git a/static/img/quick-tutorials/gcp-iam-gke/social.png b/static/img/quick-tutorials/gcp-iam-gke/social.png new file mode 100644 index 000000000..5d040635e Binary files /dev/null and b/static/img/quick-tutorials/gcp-iam-gke/social.png differ