FluxCD Azure DevOps OIDC Authentication
In today’s fast-evolving DevOps landscape, automation and security are key drivers of success. Traditional authentication methods for Azure DevOps repositories — such as Personal Access Tokens (PATs) and SSH keys — require manual management and frequent rotation, posing both operational and security challenges. However, with the introduction of OIDC authentication in Flux v2.4, these pain points are now a thing of the past.
OIDC (OpenID Connect) is a secure, scalable, and token-based authentication mechanism that leverages Kubernetes Workload Identity to seamlessly integrate Azure Kubernetes Service (AKS) with Azure DevOps. By eliminating the need for static credentials, this approach enables secure, ephemeral authentication directly from workloads within your AKS cluster.
In this article, I’ll guide you step-by-step through the process of setting up Flux source-controller and image-automation-controller to authenticate against Azure DevOps repositories using OIDC tokens. By the end, you’ll have a robust and automated solution that aligns with modern security best practices and simplifies your CI/CD workflows.
- Preparing AKS for OIDC Authentication
- Setting Up Permissions in Azure DevOps
- Configuring Flux for OIDC Authentication
Step 1: Preparing AKS for OIDC Authentication
Before configuring Flux to use OIDC tokens for authentication with Azure DevOps, it’s essential to set up your AKS cluster to support this mechanism. This involves creating a user-assigned identity, enabling managed identity on your AKS cluster, and configuring OpenID Connect (OIDC) integration. Let’s break it down step by step.
1.1 Enable Managed Identity on the AKS Cluster
If you are managing an existing AKS cluster, you can enable both managed identity and OIDC issuer using the Azure CLI with a single command:
az aks update \
--resource-group "${RESOURCE_GROUP}" \
--name "${CLUSTER_NAME}" \
--enable-oidc-issuer \
--enable-workload-identity
However, in my case, since I manage AKS clusters using Terraform, I enabled OIDC and managed identity at the root module level. This setup allowed me to dynamically toggle these features based on configuration variables, delegating the actual implementation to the platform module. Below is an example of how I structured my Terraform code:
workload_identity_enabled = var.workload_identity_enabled ? true : false
oidc_issuer_enabled = var.workload_identity_enabled ? true : null
By setting workload_identity_enabled
in the root module, I ensure that both Workload Identity and the OIDC issuer are enabled if required. This approach maintains consistency across environments while keeping the infrastructure code modular and reusable.
1.2 Using a User-Assigned Identity
To keep this article focused on the main topic, I won’t discuss the differences between user-assigned and system-assigned managed identities. For this setup, I’ve chosen to use a user-assigned identity.
Since I manage all my infrastructure using Terraform, here’s the code snippet I used to create and assign the user-managed identity to my AKS cluster:
resource "azurerm_user_assigned_identity" "this" {
location = data.azurerm_resource_group.this.location
name = format("flux-%s-%s-%s", var.service_type, var.region, var.env)
resource_group_name = data.azurerm_resource_group.this.name
}
1.3 Creating a Federated Identity for Flux
To enable Flux to authenticate with Azure DevOps using OIDC, you need to create a federated identity credential. This maps the user-assigned identity to the service account and namespace where Flux is deployed in your AKS cluster.
Here’s the Terraform code I used to create the federated identity credential:
variables.tf
variable "flux_service_accounts" {
type = list(string)
description = "flux service accounts that should be mapped to federated accounts in the user assigned managed identity"
}
terraform.tfvars
flux_service_accounts = ["source-controller", "image-automation-controller"]
main.tf
resource "azurerm_federated_identity_credential" "this" {
for_each = toset(var.flux_service_accounts)
name = format("flux-%s-%s-%s", each.value, var.region, var._env)
resource_group_name = data.azurerm_resource_group.this.name
audience = ["api://AzureADTokenExchange"]
issuer = module.aks.oidc_issuer_url -- you need to adjust this
parent_id = azurerm_user_assigned_identity.this.id
subject = "system:serviceaccount:flux-system:${each.value}"
depends_on = [azurerm_user_assigned_identity.this]
}
Step 2: Configuring Azure DevOps for OIDC Authentication
Now that the AKS cluster and federated identity are set up, the next step is to configure Azure DevOps to allow your user-assigned identity to access your repository. This involves granting the identity appropriate permissions for reading and writing to the repository.
2.1 Centralizing Permissions with an Azure DevOps Team
To simplify permission management across multiple Kubernetes clusters, I created a dedicated team in Azure DevOps named “AKS Managed Identities Team”. This team acts as a central point for managing access permissions for all managed identities used by my AKS clusters.
Adding Managed Identities to the Team
Once the team is created, you can add all your managed identities to it.
2.2 Assigning Permissions to the Team
After creating the “AKS Managed Identities Team” and adding all your managed identities, the next step is to grant the team the necessary permissions on your Azure DevOps repository. This ensures that Flux components can interact with the repository securely.
Why These Permissions?
For my setup, I assigned Read and Contribute permissions because:
- Read enables the source-controller to fetch configurations from the repository.
- Contribute is necessary for the automation-controller to update the repository with new manifests reflecting changes in image repositories or image policies.
Step 3: Upgrading Flux to Version 2.4 for OIDC Support
To enable OIDC authentication with Azure DevOps, it’s essential to use FluxCD v2.4 or later, as earlier versions do not support this feature. If you’re already running Flux, the first step is to upgrade it to the latest version.
3.1 Upgrading Flux to v2.4
You can upgrade Flux by running the following command:
flux install \
--components-extra="image-reflector-controller,image-automation-controller" \
--export > ./clusters/<my-cluster>/flux-system/gotk-components.yaml
After completing the Flux upgrade and verifying the setup, the next step is to push your changes to the source repository. This action will trigger the kustomization-controller, which will reconcile the repository state with your Kubernetes cluster. As a result, your custom resource definitions (CRDs) and other configurations will be updated automatically based on the latest changes in the repository.
3.2 Updating the kustomization.yaml
File
To ensure that Flux can properly integrate with Azure DevOps using OIDC, you need to modify the kustomization.yaml
file. This file was initially created during the Flux bootstrap process when Flux was first installed.
Add the following lines to the kustomization.yaml
file:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- patch: |-
apiVersion: v1
kind: ServiceAccount
metadata:
name: source-controller
namespace: flux-system
annotations:
azure.workload.identity/client-id: "managed identity client id"
labels:
azure.workload.identity/use: "true"
- patch: |-
apiVersion: v1
kind: ServiceAccount
metadata:
name: image-automation-controller
namespace: flux-system
annotations:
azure.workload.identity/client-id: "managed identity client id"
labels:
azure.workload.identity/use: "true"
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: source-controller
namespace: flux-system
labels:
azure.workload.identity/use: "true"
spec:
template:
metadata:
labels:
azure.workload.identity/use: "true"
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: image-automation-controller
namespace: flux-system
labels:
azure.workload.identity/use: "true"
spec:
template:
metadata:
labels:
azure.workload.identity/use: "true"
In addition to modifying the kustomization.yaml
file, you need to update the gotk-sync.yaml
file to enable the Azure provider for authentication. By default, the provider is set to generic, which is used for SSH authentication. To use OIDC authentication with Azure DevOps, you need to change this to azure and remove the secretRef
configuration.
Note: Below step should be also included to above patching step, Doing this, you don’t risk undoing the changes if you would run bootstrap again (Thanks for Stefan Prodan for the hint)
https://www.linkedin.com/feed/update/urn:li:activity:7266420730781745153?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7266420730781745153%2C7266428580442611712%29&dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287266428580442611712%2Curn%3Ali%3Aactivity%3A7266420730781745153%29
Update your gotk-sync.yaml
file with the following changes:
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m
url: https://dev.azure.com/<your-organization>/<your-project>/_git/<your-repo>
provider: azure
ref:
branch: main
After updating both the kustomization.yaml
and gotk-sync.yaml
files, the final step in this phase is to commit and push these changes to your source repository. This action will trigger the kustomization-controller, which will detect the updated configurations and start reconciling them with your Kubernetes cluster.
3.3 Validating the Setup with Useful Commands
After pushing the changes, it’s important to validate that the new OIDC setup is working correctly. Below are some commands I used to confirm and troubleshoot the configuration:
# flux reconcile source git flux-system
# flux reconcile kustomization flux-system
# flux get source git
# flux get kustomization
# flux get image update|repoisotry|policy
Conclusion
By following these steps, you’ve successfully configured Flux to use OIDC authentication with Azure DevOps. This modern approach eliminates the need for managing and rotating secret keys, significantly enhancing the security and maintainability of your CI/CD pipeline.
With OIDC, authentication is now seamlessly integrated with your AKS cluster and Azure DevOps repository, allowing you to focus on delivering value without worrying about credential management.
Reminder: Always test your setup in a development platform first to ensure that everything works as expected. Once validated, you can confidently roll out the configuration to your production environments.
This setup not only simplifies authentication but also aligns with best practices for cloud-native security and automation. I hope this guide helps you achieve a more robust and efficient integration. Happy deploying!