Azure Workload Identity Preview on AKS with Spring Boot

  • A maximum of 200 pod identities are allowed for a cluster.
  • A maximum of 200 pod identity exceptions are allowed for a cluster.
  • Pod-managed identities are available on Linux node pools only.

NOTE: Azure Workload Identity installation won’t be covered. You can find the details on the Github page. https://azure.github.io/azure-workload-identity/docs/installation.html. We will focus on technical details of Workload Identity, and Spring Boot implementation.

Azure Workload Identity(Preview)

Azure Workload Identity Github page is at https://azure.github.io/azure-workload-identity/docs/introduction.html. I have copied & pasted the advantages over v1 from the link below.

  • Removes the scale and performance issues that existed for identity assignment
  • Supports Kubernetes clusters hosted in any cloud or on-premises
  • Supports both Linux and Windows workloads
  • Removes the need for Custom Resource Definitions and pods that intercept Instance Metadata Service (IMDS) traffic
  • Avoids the complication and error-prone installation steps such as cluster role assignment from the previous iteration.

Authentication Flow

- name: azure-identity-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: api://AzureADTokenExchange
expirationSeconds: 3600
path: azure-identity-token
{
"aud": [
"api://AzureADTokenExchange"
],
"exp": 1646808223,
"iat": 1646804623,
"iss": "https://oidc.prod-aks.azure.com/28562482-e0a6-4012-ae58-89522724eed9/",
"kubernetes.io": {
"namespace": "default",
"pod": {
"name": "workloadidentity-blob",
"uid": "79e461db-64be-4653-a9f7-dd45ced5ed4f"
},
"serviceaccount": {
"name": "workload-identity-sa",
"uid": "2c455bf3-c2d4-458e-9c41-94332f2e60ef"
}
},
"nbf": 1646804623,
"sub": "system:serviceaccount:default:workload-identity-sa"
}
Federated Credentials on Azure AD
Name:                workload-identity-sa
Namespace: default
Labels: azure.workload.identity/use=true
Annotations: azure.workload.identity/client-id: 4ac69285-cca0-450e-b29a-450c1e17615d
azure.workload.identity/tenant-id: 72f988bf-86f1-41af-91ab-2d7cd011db47

Image pull secrets: <none>
Mountable secrets: workload-identity-sa-token-mwpc8
Tokens: workload-identity-sa-token-mwpc8
Events: <none>
apiVersion: v1
kind: Pod
metadata:
name: workloadidentity-blob
labels:
app: workloadidentity-blob
spec:
serviceAccountName: workload-identity-sa
containers:
- name: workloadidentity-blob
image: eggboy/workloadidentity-blob:0.0.1
imagePullPolicy: Always
env:
- name: BLOB_ACCOUNT_NAME
value: ""
- name: BLOB_CONTAINER_NAME
value: ""
restartPolicy: Always
AZURE_CLIENT_ID:             4ac69285-cca0-450e-b29a-450c1e17615d
AZURE_TENANT_ID: 72f988bf-86f1-41af-91ab-2d7cd011db47
AZURE_FEDERATED_TOKEN_FILE: /var/run/secrets/azure/tokens/azure-identity-token
AZURE_AUTHORITY_HOST: https://login.microsoftonline.com/
<dependency>
<
groupId>com.microsoft.azure</groupId>
<
artifactId>msal4j</artifactId>
<
version>1.11.2</version>
</
dependency>
<
dependency>
<
groupId>com.azure</groupId>
<
artifactId>azure-identity</artifactId>
<
version>1.4.5</version>
</
dependency>
public class FederatedCredential implements TokenCredential {

@Value("${AZURE_FEDERATED_TOKEN_FILE}")
private String AZURE_FEDERATED_TOKEN_FILE;

@Value("${AZURE_AUTHORITY_HOST}")
private String AZURE_AUTHORITY_HOST;

@Value("${AZURE_TENANT_ID}")
private String AZURE_TENANT_ID;

@Value("${AZURE_CLIENT_ID}")
private String AZURE_CLIENT_ID;

@Override
public Mono<AccessToken> getToken(TokenRequestContext tokenRequestContext) {

String clientAssertion = null;
try {
clientAssertion = Files.readString(Paths.get(AZURE_FEDERATED_TOKEN_FILE));
}
catch (IOException e) {
log
.error("Error getting AZURE_FEDERATED_TOKEN_FILE", e);
}

IClientCredential credential = ClientCredentialFactory.createFromClientAssertion(clientAssertion);

StringBuilder authority = new StringBuilder();
authority.append(AZURE_AUTHORITY_HOST);
authority.append(AZURE_TENANT_ID);

try {
ConfidentialClientApplication app = ConfidentialClientApplication
.builder(AZURE_CLIENT_ID, credential).authority(authority.toString()).build();

Set<String> scopes = tokenRequestContext.getScopes().stream().collect(Collectors.toSet());

ClientCredentialParameters parameters = ClientCredentialParameters.builder(scopes).build();
IAuthenticationResult result = app.acquireToken(parameters).join();

return Mono.just(
new AccessToken(result.accessToken(), result.expiresOnDate().toInstant().atOffset(ZoneOffset.UTC)));
}
catch (Exception e) {
log
.error("Error creating client application.", e);
}

return Mono.empty();
}

}
@Bean
public BlobContainerClient blobContainerClient() {

String accountName = env.getProperty("BLOB_ACCOUNT_NAME");
String containerName = env.getProperty("BLOB_CONTAINER_NAME");

String endpoint = String.format(Locale.ROOT, "https://%s.blob.core.windows.net", accountName);

BlobServiceClient storageClient = new BlobServiceClientBuilder().endpoint(endpoint)
.credential(federatedCredential).buildClient();

return storageClient.getBlobContainerClient(containerName);
}
2022-03-09 08:33:42.116  INFO 1 --- [     parallel-1] c.a.c.implementation.AccessTokenCache    : Acquired a new access token.

Conclusion

Workload Identity is the next generation of Pod Identity based on OIDC and Kubernetes native implementation. It is still in early-stage with rough edges, and most importantly it’s missing Managed Identity support at the moment, but the fundamental design of the new implementation looks fantastic and future-proof. I will write another article once Managed Identity support is available. Stay tuned!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jay Lee

Jay Lee

Cloud Native Enthusiast. Java, Golang, Spring, Kubernetes.