Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Figure out how Workload Identity works #72

Open
zmoog opened this issue Jan 16, 2024 · 9 comments
Open

Figure out how Workload Identity works #72

zmoog opened this issue Jan 16, 2024 · 9 comments
Assignees
Labels

Comments

@zmoog
Copy link
Owner

zmoog commented Jan 16, 2024

I have an AKS cluster on Azure. I want to write a Go application that lists the VM on a subscription without using credentials.

Azure offers an authentication mechanism called workload identity that seems a good fit.

Let's see how it works.

@zmoog zmoog self-assigned this Jan 16, 2024
@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

After some reading and a few failed attempts, the document Deploy and configure workload identity on an Azure Kubernetes Service (AKS) cluster seems the best resource I was able to get.

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Export environment variables

For this walkthrough, I'm using an existing cluster named mbranca-sdh-4160:

export RESOURCE_GROUP="mbranca-sdh-4160"
export CLUSTER_NAME="mbranca-sdh-4160"
export LOCATION="eastus2"
export SERVICE_ACCOUNT_NAMESPACE="default"
export SERVICE_ACCOUNT_NAME="workload-identity-sa"
export SUBSCRIPTION="$(az account show --query id --output tsv)"
export USER_ASSIGNED_IDENTITY_NAME="myIdentity"
export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity"

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Update an existing AKS cluster

Since I already have a cluster, I'll set it up enabling OIDC and workload identity:

az aks update \
	-g "${RESOURCE_GROUP}" \
	-n "${CLUSTER_NAME}" \
	--enable-oidc-issuer \
	--enable-workload-identity

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Retrieve the OIDC Issuer URL

Now that OIDC is enabled, we can collect the OIDC Issuer URL and store it as environment variable:

export AKS_OIDC_ISSUER="$(az aks show --resource-group "${RESOURCE_GROUP}" --name "${CLUSTER_NAME}" --query "oidcIssuerProfile.issuerUrl" -otsv)"

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Create a managed identity

From the source document:

Use the Azure CLI az account set command to set a specific subscription to be the current active subscription. Then use the az identity create command to create a managed identity.

# set the default subscription
az account set --subscription "${SUBSCRIPTION}"

az identity create --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --subscription "${SUBSCRIPTION}"

# (optional) if you need to delete one 
az identity delete --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --subscription "${SUBSCRIPTION}"

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Create Kubernetes service account

Create a Kubernetes service account and annotate it with the client ID of the managed identity created in the previous step. Use the az aks get-credentials command and replace the values for the cluster name and the resource group name.

az aks get-credentials -n "${CLUSTER_NAME}" -g "${RESOURCE_GROUP}"

Copy and paste the following multi-line input in the Azure CLI.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
  name: "${SERVICE_ACCOUNT_NAME}"
  namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
EOF

Use the az identity federated-credential create command to create the federated identity credential between the managed identity, the service account issuer, and the subject.

az identity federated-credential create \
  --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \
  --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \
  --resource-group "${RESOURCE_GROUP}" \
  --issuer "${AKS_OIDC_ISSUER}" \
  --subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}" \
  --audience api://AzureADTokenExchange

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Build your application

For this test, I created a trivial "application" that count the VMs available on a subscription.

Here's the source code:

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"

	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
	"k8s.io/klog/v2"
)

func main() {
	subscriptionId := os.Getenv("SUBSCRIPTION_ID")

	credential, err := azidentity.NewDefaultAzureCredential(nil)
	if err != nil {
		klog.Fatal(err)
	}

	client, err := armcompute.NewVirtualMachinesClient(subscriptionId, credential, nil)
	if err != nil {
		klog.Fatal(err)
	}

	pager := client.NewListAllPager(nil)

	for pager.More() {
		page, err := pager.NextPage(context.Background())
		if err != nil {
			klog.Fatal(err)
		}

		fmt.Printf("Found %d VMs\n", len(page.Value))
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello, Kubernetes!")
	})

	err = http.ListenAndServe(":8080", nil)
	if err != nil {
		klog.Fatal(err)
	}
}

Here is the Dockerfile to build an image:

FROM golang:1.21 as builder

WORKDIR /app
COPY . .

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o myapp

FROM alpine:3.14

WORKDIR /app
COPY --from=builder /app/myapp /app/

EXPOSE 8080
CMD ["/app/myapp"]

And finally, the K8s deployment file:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      namespace: default
      labels:
        azure.workload.identity/use: "true"
        app: myapp
    spec:
      serviceAccountName: workload-identity-sa
      containers:
        - name: myapp
          image: zmoog/myapp:latest
          env:
          - name: SUBSCRIPTION_ID
            value: "<put your subscription ID here>"

I build and pushed the image to Docker Hub:

docker build -t zmoog/myapp:latest .  

docker push zmoog/myapp:latest  

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Permit myIdentity to list the VMs

The workload identity infrastructure is ready, but we must give myIdentity permission to read the VM information.

I'm using Azure Portal for this step, but I want to come back later and replace this section with a CLI command.

On Azure Portal:

  1. Visit Subscriptions > [select your subscription] > Access Control (IAM)
  2. Click on Add > Add role assignment
  3. In the Role tab, select Reader
  4. In the Members tab, click Select member
  5. Type "myIdentity" in the search box
  6. Select "myIdentity" and click on the Select button to confirm
  7. Click on Review + assign to complete the operation

CleanShot 2024-01-17 at 00 12 53@2x

Now, the identity "myIdentity" is authorized to perform the action the app needs.

@zmoog
Copy link
Owner Author

zmoog commented Jan 16, 2024

Run your application

As a final step, I will deploy and run the app on k8s.

$ k apply -f deployment.yaml
deployment.apps/myapp-deployment created

$ k get pods                                                                                                                                                                          
NAME                                READY   STATUS    RESTARTS   AGE
myapp-deployment-785ff98547-tmxn9   1/1     Running   0          14s

$ k logs myapp-deployment-785ff98547-tmxn9                                                                                                                                            
Found 7 VMs

@zmoog zmoog added the research label Jan 16, 2024
@zmoog zmoog added this to Notes Jan 16, 2024
@zmoog zmoog moved this to In Review in Notes Jan 16, 2024
@zmoog zmoog changed the title Figure out how workload identity work Figure out how Workload Identity works Jan 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: In Review
Development

No branches or pull requests

1 participant