From 0d10327780f85a70c5cb4d206e3d82c1847fe136 Mon Sep 17 00:00:00 2001 From: Amit Lichtenberg Date: Sun, 10 Mar 2024 12:21:36 +0200 Subject: [PATCH] Azure IAM tutorial - WIP --- docs/features/azure-iam/_category_.json | 8 + docs/features/azure-iam/index.mdx | 63 ++++ docs/features/azure-iam/reference.mdx | 47 +++ .../azure-iam/tutorials/_category_.json | 5 + .../azure-iam/tutorials/azure-iam-aks.mdx | 269 ++++++++++++++++++ .../code-examples/azure-iam-aks/client.yaml | 6 +- .../azure-iam-aks/clientintents.yaml | 5 +- 7 files changed, 398 insertions(+), 5 deletions(-) create mode 100644 docs/features/azure-iam/_category_.json create mode 100644 docs/features/azure-iam/index.mdx create mode 100644 docs/features/azure-iam/reference.mdx create mode 100644 docs/features/azure-iam/tutorials/_category_.json create mode 100644 docs/features/azure-iam/tutorials/azure-iam-aks.mdx diff --git a/docs/features/azure-iam/_category_.json b/docs/features/azure-iam/_category_.json new file mode 100644 index 000000000..031fb9c98 --- /dev/null +++ b/docs/features/azure-iam/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Azure IAM", + "position": 2, + "collapsed": true, + "customProps": { + "image": "/img/icons/aws.png" + } +} diff --git a/docs/features/azure-iam/index.mdx b/docs/features/azure-iam/index.mdx new file mode 100644 index 000000000..0f57c210f --- /dev/null +++ b/docs/features/azure-iam/index.mdx @@ -0,0 +1,63 @@ +--- +sidebar_position: 1 +title: Azure IAM | Overview +hide_table_of_contents: true +hide_title: true +--- + +import DocsLinkCard from "@site/src/components/LinkCard"; + +export const tutorials = [ + { + title: 'Automate Azure IAM for AKS', + description: 'Create just-in-time Azure managed identities & role assignments that are kept in sync with your workloads', + url: '/features/azure-iam/tutorials/azure-iam-eks' // TODO + }, +]; + + +# Azure IAM + +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 IAM identities and roles. + +### Tutorials + +To learn how to use the Intents Operator and Credentials Operator to manage just-in-time Azure IAM access, check out the tutorial. + + + +### How does Otterize work with Azure IAM? + +1. First, the EKS cluster must have [Otterize installed](/overview/installation), as well as the [Otterize Azure integration](TODO) configured +2. To have a managed identity created for a pod, label the pod with `credentials-operator.otterize.com/create-azure-workload-identity: "true"` +3. The credentials operator will create an Azure managed identity and federated identity credential bound to the pod's ServiceAccount. The ServiceAccount will be annotated automatically. +4. At this point, the pod is able to assume the identity, but it does not have the permissions to perform any actions. +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 an Azure Storage Blobs bucket. +View the [reference](/features/azure-iam/reference) to learn more about the Azure IAM ClientIntents syntax. +5. Once the intent is applied, the intents operator will create a new role assignment, which will be attached to the workload identity with the appropriate access. +6. Done! + +```yaml +apiVersion: k8s.otterize.com/v1alpha3 +kind: ClientIntents +metadata: + name: client + namespace: otterize-tutorial-azure-iam +spec: + service: + name: client + calls: + # replace 00000000-0000-0000-0000-000000000000 with your Azure subscription ID + - name: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/otterizetutorialazureiam/blobServices/default/containers/test" + type: azure + azureRoles: + - "Storage Blob Data Contributor" +``` + +### Automatically generating ClientIntents for Azure IAM + +Figuring out which access you need for Azure 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 Azure 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/azure-iam/reference.mdx b/docs/features/azure-iam/reference.mdx new file mode 100644 index 000000000..fe0517232 --- /dev/null +++ b/docs/features/azure-iam/reference.mdx @@ -0,0 +1,47 @@ +--- +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 AWS ARN or ARN wildcard that references the resource(s) for the authorization + - name: arn:aws:s3:::example-bucket-*/* + type: aws + # one or more AWS Actions or Action wildcards that will be provided to the specified resources + awsActions: + - "s3:PutObject" + - "s3:GetObject" + # Multiple call definitions can be defined for a single service. + - name: arn:aws:s3:::read-only-bucket-*/* + type: aws + awsActions: + - "s3:GetObject" +``` + +### Annotations + +| 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` | + + +### Helm Chart options + +| Key | Description | Default | +|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| `global.aws.enabled` | Enable or disable AWS integration | `false` | +| `global.aws.eksClusterNameOverride` | EKS cluster name (overrides auto-detection) | `(none)` | +| `aws.roleARN` | ARN of the AWS role the operator will use to access AWS. By defeault, Otterize will create a unique role for each service an annotate the service with the role's ARN. | `(none)` | + +View the [Helm chart reference](/reference/configuration/otterize-chart) for all other options \ No newline at end of file diff --git a/docs/features/azure-iam/tutorials/_category_.json b/docs/features/azure-iam/tutorials/_category_.json new file mode 100644 index 000000000..bdfe77bf2 --- /dev/null +++ b/docs/features/azure-iam/tutorials/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Tutorials", + "position": 2, + "collapsed": false +} diff --git a/docs/features/azure-iam/tutorials/azure-iam-aks.mdx b/docs/features/azure-iam/tutorials/azure-iam-aks.mdx new file mode 100644 index 000000000..f6070595d --- /dev/null +++ b/docs/features/azure-iam/tutorials/azure-iam-aks.mdx @@ -0,0 +1,269 @@ +--- +sidebar_position: 2 +title: Automate Azure IAM for AKS +image: /img/quick-tutorials/azure-iam-aks/social.png +--- + + +Otterize automates Azure IAM identities and role assignments for your Azure AKS workloads, all in Kubernetes. + +In this tutorial, we will: + +- Optionally, spin up an AKS cluster, install the Otterize Kubernetes operator on it, and configure it to manage Azure IAM. +- Deploy a client pod that lists files in an Azure Blog Storage container. +- Label the client pod, telling the credentials operator to link its Kubernetes ServiceAccount with an Azure workload identity created for it. +- Create a `ClientIntents` resource allowing the client pod to access Azure Blob Storage, that tells the intents operator to create a role assignment and associate it with the previously created workload identity. +- See that the client is now able to list files in the Azure Blob Storage container. + +## Prerequisites +Already have Otterize deployed with the Azure IAM integration configured on your cluster? [Skip to the tutorial.](#tutorial) + + +### 1. Install the Azure CLI +Follow the installation instructions for the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli). + +### 2. Create an Azure AKS cluster +Before you start, you'll need an Azure AKS cluster, with OIDC issuer & workload identity enabled. + +
+How to set up an Azure AKS cluster using the Azure CLI + + +Export the following environment variables: +```bash +export LOCATION="eastus" +export RESOURCE_GROUP="otterizeAzureIAMTutorialResourceGroup" +export AKS_CLUSTER_NAME="otterizeAzureIAMTutorialAKSCluster" +``` + +Create a resource group: +```bash +az group create --name $RESOURCE_GROUP --location $LOCATION +``` + +Create an AKS cluster, with OIDC issuer and workload identity enabled: +```bash +az aks create -g $RESOURCE_GROUP -n $AKS_CLUSTER_NAME --node-count 1 --enable-oidc-issuer --enable-workload-identity --generate-ssh-keys +``` + +
+ +Alternatively, update an existing AKS cluster to enable OIDC issuer and workload identity: + +
+How to update an existing AKS cluster using the Azure CLI + +Export the following environment variables: +```bash +export RESOURCE_GROUP="" +export AKS_CLUSTER_NAME="" +``` + +Update the AKS cluster to enable OIDC issuer and workload identity: +```bash +az aks update -g $RESOURCE_GROUP -n $AKS_CLUSTER_NAME --enable-oidc-issuer --enable-workload-identity +``` + +
+ + + +Don't forget to configure your kubeconfig for your cluster. If using the example cluster above, use this command: +```bash +az aks get-credentials -n otterizeAzureIAMTutorialAKSCluster -g otterizeAzureIAMTutorialResourceGroup +``` + +### 2. Deploy Otterize for Azure 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 Azure IAM integration on the [Integrations page](https://app.otterize.com/integrations). + - Input your Azure tenant & subscription IDs. These are available in the Azure portal, or by running the following command: + ```bash + az account list --output table + ``` + - If you are using the cluster from the previous step, the resource group name is `otterizeAzureIAMTutorialResourceGroup` and the cluster name is `otterizeAzureIAMTutorialAKSCluster`. + +Once the Azure integration is configured, you'll be presented with instructions for configuring your Otterize integration with Azure IAM support. +This creates a managed identity and federated identity credential for the Otterize Kubernetes operator, and assigns it the resource group owner role on the resource group containing your AKS cluster, so that it is able to manage identitiies and role assignments for your AKS workloads. +This setup is required once per-cluster. + +After terraform has configured your cluster, click Next and you'll be presented with the configuration for deploying Otterize. +Since you now have the Azure integration enabled, you need to redeploy Otterize with Azure integration enabled flag, providing it the client ID for the managed identity created during the terraform installation. + +## Tutorial + +### Create an Azure Blob Storage account & container +Create a general-purpose storage account using the Azure CLI: +```bash +export STORAGE_ACCOUNT_NAME=otterizeazureiamtutorial +az storage account create --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --location $LOCATION --sku Standard_ZRS --encryption-services blob +``` + +Create a container in the storage account: +```bash +export STORAGE_CONTAINER_NAME=otterizeazureiamtutorialcontainer +az storage container create \ + --account-name $STORAGE_ACCOUNT_NAME \ + --name $STORAGE_CONTAINER_NAME +``` + +Upload a blob to the storage container: +```bash +echo "Hello, Azure integration" > testfile.txt +az storage blob upload \ + --account-name $STORAGE_ACCOUNT_NAME \ + --container-name $STORAGE_CONTAINER_NAME \ + --file testfile.txt \ + --name testfile.txt +``` + +### Deploy the sample client + +```bash +kubectl create namespace otterize-tutorial-azure-iam +kubectl apply -n otterize-tutorial-azure-iam -f ${ABSOLUTE_URL}/code-examples/azure-iam-aks/client.yaml +kubectl patch deployment -n otterize-tutorial-azure-iam server --type='json' -p="[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/env\", \"value\": [{\"name\": \"AZURE_STORAGE_ACCOUNT\", \"value\": \"$STORAGE_ACCOUNT_NAME\"}]}]" +kubectl patch deployment -n otterize-tutorial-azure-iam server --type='json' -p="[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/env\", \"value\": [{\"name\": \"AZURE_STORAGE_CONTAINER\", \"value\": \"$STORAGE_CONTAINER_NAME\"}]}]" +``` + +
+Expand to see the deployment YAML + +```yaml +{@include: ../../../../static/code-examples/azure-iam-aks/client.yaml} +``` + +
+ + +### View logs for the client - access denied +The client logs will show that it fails to access the Azure Blob Storage container. + +```bash +kubectl logs -f -n otterize-tutorial-azure-iam deploy/client +``` + +```bash +TODO +``` + +### Label the server pod to create an Azure workload identity role +Label the client pod so that the Otterize credentials operator creates an Azure workload identity for it and binds its Kubernetes ServiceAccount to the newly created identity. +```yaml +metadata: + labels: + credentials-operator.otterize.com/create-azure-workload-identity: "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-azure-iam client -p '{"spec": {"template":{"metadata":{"labels":{"credentials-operator.otterize.com/create-azure-workload-identity":"true"}}}} }' +``` + +#### An Azure workload identity was created for the server pod +Let's inspect the created managed identity +```bash +az identity list --query "[?starts_with(name,'ottr-uai-')]" --output table +``` + +In the output, you should see that a managed identity was created for the client workload, with the name starting with `ottr-uai-otterize-tutorial-azure-iam-client-...`: +```bash +Name Location TenantId PrincipalId ClientId ResourceGroup +--------------------------------------------------------------- ---------- ------------------------------------ ------------------------------------ ------------------------------------ --------------- +ottr-uai-otterize-tutorial-azure-iam-client-myAKSCluster-7d747a eastus f8b92b88-e477-41ad-a5af-079de8dc8210 1aa514ff-01cd-4856-8c76-e4d671aab79e d82c9ea7-9178-4e4a-bffa-23488c589d5e myResourceGroup +```` + +You could also inspect the federated identity credential created for the client workload: +```bash +export WORKLOAD_IDENTITY_NAME=$(az identity list --query "[?starts_with(name,'ottr-uai-otterize-tutorial-azure-iam-client-myAKSCluster')].name" -o tsv ) +az identity federated-credential list --identity-name $WORKLOAD_IDENTITY_NAME --resource-group $RESOURCE_GROUP --output table +``` + +In the output, you should see that a federated identity credential was created for the client workload: +```bash +Issuer Name ResourceGroup Subject +---------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------- --------------- -------------------------------------------------------- +https://eastus.oic.prod-aks.azure.com/f8b92b88-e477-41ad-a5af-079de8dc8210/bd429a59-100e-4ed8-88d4-29643c922e05/ ottr-fic-otterize-tutorial-azure-iam-client-myAKSCluster-e54654 myResourceGroup system:serviceaccount:otterize-tutorial-azure-iam:client +``` + +[//]: # (TODO: rename to the IDs generated in the tutorial ) + +#### The Kubernetes ServiceAccount was annotated with the workload identity ID +The credentials operator automatically annotated the Kubernetes ServiceAccount for the server pod with the newly created workload identity + +Let's look at the service account: +```bash +kubectl get serviceaccount -n otterize-tutorial-azure-iam client -o yaml +``` + +[//]: # (TODO: update me) +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + # highlight-next-line + eks.amazonaws.com/role-arn: + # highlight-next-line + arn:aws:iam::353146681200:role/otr-otterize-tutorial-azure-iam.server@otterize-iam-eks-tutoria-ef91a7 + name: server + namespace: otterize-tutorial-azure-iam +``` + + +### Apply intents to create the necessary IAM role assignments + +By annotating the pod, we've created a workload identity. +We now need to specify what we need to access, and the intents operator will create an Azure IAM role assignment accordingly. + +We will specify the following ClientIntents, granting the `Storage Blob Data Contributor` permission to the `otterizeazureiamtutorialcontainer` container in the `otterizeazureiamtutorial` storage account. +```yaml +{@include: ../../../../static/code-examples/azure-iam-aks/clientintents.yaml} +``` + +To apply these intents, run the following command: +```bash +kubectl apply -n otterize-tutorial-azure-iam -f ${ABSOLUTE_URL}/code-examples/azure-iam-aks/clientintents.yaml +``` + +### The client can now list files in the Azure Blob Storage container! + +Let's look at the client logs again to see that no more errors are being reported: +```bash +kubectl logs -f -n otterize-tutorial-azure-iam deploy/client +``` + +[//]: # (# TODO: output) +```json +{ + # highlight-next-line + "status":200, + "host":"server", + "method":"POST", + "uri":"/upload" +} +``` + +### 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-azure-iam +``` + +To delete the Azure blob storage account & container: +```bash +az storage account delete --name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP +``` + +To delete the cluster, if you created the one in this tutorial: +```bash +az aks delete --name $AKS_CLUSTER_NAME --resource-group $RESOURCE_GROUP +``` diff --git a/static/code-examples/azure-iam-aks/client.yaml b/static/code-examples/azure-iam-aks/client.yaml index 6635773b0..0b6c70d10 100644 --- a/static/code-examples/azure-iam-aks/client.yaml +++ b/static/code-examples/azure-iam-aks/client.yaml @@ -23,7 +23,7 @@ spec: labels: app: client azure.workload.identity/use: "true" - credentials-operator.otterize.com/create-azure-role-assignment: "true" + credentials-operator.otterize.com/create-azure-workload-identity: "true" spec: serviceAccountName: client containers: @@ -32,7 +32,7 @@ spec: command: [ "/bin/sh", "-c", "--" ] env: - name: AZURE_STORAGE_ACCOUNT - value: otterizetutorialazureiam + value: otterizeazureiamtutorial - name: AZURE_STORAGE_CONTAINER - value: test + value: otterizeazureiamtutorialcontainer args: [ "while true; do az login -o table --federated-token \"$(cat $AZURE_FEDERATED_TOKEN_FILE)\" --service-principal -u $AZURE_CLIENT_ID -t $AZURE_TENANT_ID; az storage blob list --container $AZURE_STORAGE_CONTAINER --account-name $AZURE_STORAGE_ACCOUNT --auth-mode login -o table; sleep 1; echo 'Client - The time is:' $(date); sleep 2; done" ] \ No newline at end of file diff --git a/static/code-examples/azure-iam-aks/clientintents.yaml b/static/code-examples/azure-iam-aks/clientintents.yaml index ccbbd28af..3c06f14ec 100644 --- a/static/code-examples/azure-iam-aks/clientintents.yaml +++ b/static/code-examples/azure-iam-aks/clientintents.yaml @@ -7,7 +7,8 @@ spec: service: name: client calls: - - name: "/subscriptions/ef54c90c-5351-4c8f-a126-16a6d789104f/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/otterizetutorialazureiam/blobServices/default/containers/test" + # replace 00000000-0000-0000-0000-000000000000 with your Azure subscription ID + - name: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/otterizeazureiamtutorial/blobServices/default/containers/otterizeazureiamtutorialcontainer" type: azure - azureRoles: # pre-existing (preset or custom) role by name + azureRoles: - "Storage Blob Data Contributor"