Secure CI/CD with GitHub Actions: Deploying to AKS Using ACR and OIDC

Seamless Cloud Deployments: Integrating GitHub Actions with AKS and ACR

I'm currently developing a Geofencing API using a microservices architecture to ensure flexibility and maintainability through loosely coupled services. This architectural choice underscores my dedication to building an application that can easily adapt to changing needs and scale efficiently. However, I recognized the critical need for a robust infrastructure and seamless automation. The challenge wasn't just choosing the right tools; it was integrating them in a way that increased security without sacrificing efficiency.

A common challenge is securely deploying containerized applications to Azure Kubernetes Service without embedding credentials like username and password for the registry login in the GitHub Actions workflow. This blog post walks you through setting up a secure pipeline that uses Azure Container Registry and OpenID Connect for authentication, effectively avoiding the need for hardcoded credentials.

Prerequisites

Before we begin, ensure you have the following:

  • An Azure account with an AKS cluster and an ACR instance configured.
  • A GitHub account with a repository for your project.
  • Azure CLI and kubectl installed on your local machine.

Understanding the Components

  • Azure Kubernetes Service (AKS) provides a managed Kubernetes service that simplifies the deployment, management, and operation of Kubernetes clusters in Azure.
  • Azure Container Registry (ACR) is a managed Docker registry service based on the open source Docker Registry 2.0. ACR allows you to store and manage container images across all types of Azure deployments.
  • OpenID Connect (OIDC) is an authentication protocol based on OAuth 2.0 that allows GitHub Actions to authenticate to Azure using short-lived tokens, eliminating the need to store and manage long-lived credentials.

The Goal

My goal is to avoid checking the "Admin User" checkbox in the ACR's access key settings, not having username/password credentials, and avoiding the following part of the GitHub actions.

      - name: Log in to Azure Container Registry
        uses: azure/docker-login@v1
        with:
          login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }}
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

Let's get started on being more secure by integrating with a managed identity.

Step 1: Configure AKS to Authenticate with ACR Using Managed Identity

The first step is to ensure your AKS cluster can pull container images from your ACR instance using its managed identity, thus eliminating the need for Docker credentials.

Enable Managed Identity on AKS (if not already enabled):

az aks update -n $AKS_NAME -g $RESOURCE_GROUP_NAME --enable-managed-identity

Grant AKS Access to ACR:

az aks update -n $AKS_NAME -g $RESOURCE_GROUP_NAME --attach-acr $ACR_NAME

This command allows the AKS cluster to authenticate to ACR directly using its managed identity.

I already described this more in detail here: https://ricofritzsche.me/acr-integration-with-aks-for-simpler-authentication/

Step 2: Creating an App Registration for GitHub Actions in Azure

App registrations in Azure are essentially creating a unique identity for your applications within the Azure ecosystem. This identity is crucial for enabling secure interactions between your application and Azure's services and resources.

Setting Up Federated Access for GitHub Actions

  • Navigate to Microsoft Entra ID and look for "App registrations".
  • Create a new registration named github-workflow. If you choose a different name, make sure to note it down for future reference.
  • Once your app registration is ready, go to "Certificates & secrets".
  • You'll see a section titled "Federated credentials". Here, click on "+ Add Credential".
  • This step is about linking your GitHub repository with Azure, allowing for a secure and direct integration between your GitHub Actions workflows and Azure services.

GitHub Actions can be authenticated using a token issued by GitHub's OIDC provider, thanks to OIDC federation with Entra ID (former Azure AD).

Step 3: Adding the "ACR Push" Role to Your App Registration

Once you've set up federated access to your GitHub repository with Azure through application registration, the next important step is to ensure that this application identity has the right permissions to interact with your ACR instance.

Within the Azure portal, go to your Azure Container Registry resource and look for the "Access control (IAM)" option in the sidebar. This is where you can manage permissions for your Azure resources.

Click on "Add a role assignment" to open the role assignment wizard. In the role selection step, choose the "ACR Push" role. This role allows the bearer to push new container images to your registry, which is precisely what your GitHub Actions workflow needs to do. When prompted to select the assignee, choose "Azure AD user, group, or service principal," and then search for the name of your app registration. We named it "github-workflow" earlier.

By completing this step, you'll effectively be extending your GitHub Actions workflow with the ability to push updates to your container images directly to ACR.

Step 4: Set Up GitHub Actions Workflow

With OIDC configured, your GitHub Actions workflow can authenticate to Azure and deploy to AKS without hardcoded credentials. This can look as follows.

name: Deploy to Development AKS

on:
  push:
    branches:
      - dev

permissions:
  id-token: write
  contents: read

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    environment: dev
    steps:
      - uses: actions/checkout@v4

      - name: Azure login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Set Git Commit SHA
        run: |
          echo "GIT_COMMIT_SHA=${GITHUB_SHA}" >> $GITHUB_ENV

      - name: Docker build and push ACR
        run: |
          az acr login --name ${{ secrets.REGISTRY_LOGIN_SERVER }}
          docker build -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/geofencing-ui:${{ env.GIT_COMMIT_SHA }} .
          docker push ${{ secrets.REGISTRY_LOGIN_SERVER }}/geofencing-ui:${{ env.GIT_COMMIT_SHA }}

Conclusion

As I conclude this journey of securing my geofencing API deployment, it's clear that integrating OIDC with AKS and ACR has been nothing short of transformative. By using OIDC, I've avoided the all-too-common pitfalls of credential management. There's a certain peace of mind that comes from knowing that I'm not juggling sensitive keys or tokens in my workflow, which significantly reduces the risk of a breach.

What stands out is the simplicity of this setup. The managed identity bridge between AKS and ACR has streamlined my deployments; no more wrestling with Docker login commands or troubleshooting access rights. It's all about keeping things simple and focusing more on what's important - building and deploying, and less on the overhead of managing identities.

Don't miss a thing

If you've found these insights on securing your CI/CD pipelines and deploying with confidence using GitHub Actions, AKS, and ACR valuable, there's plenty more where that came from. Subscribe now for more valuable content like this.

Need Help?

If you're facing the complexities of building secure, efficient cloud deployments on Azure and could use a hand, I'm here to help. Contact me for targeted guidance, support tailored to your needs, and to learn more about the conditions.

Subscribe to Rico Fritzsche

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe