Click here to Skip to main content
15,868,016 members
Articles / Hosted Services / Azure
Article

Azure AD Managed Identities: Java Apps on Azure Kubernetes Service

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
8 Feb 2022CPOL7 min read 3.7K   1
How Managed Identities enable a containerized Spring Boot web app running on Azure Kubernetes Service
This is Part 2 of a 3-part series that explores how to give our enterprise Java + Spring applications identities of their own via Azure AD managed identities. In this article, you will learn how to establish managed identities for a Spring Boot application deployed on Azure Kubernetes Service.

This article is a sponsored article. Articles such as these are intended to provide you with information on products and services that we consider useful and of value to developers

This second article continues our three-part series exploring how Azure AD managed identities help applications connect to services. Using managed identities eliminates the need to manage credentials for every application-connected service and helps protect these services from credential-related exploits. In the previous article, we explored how to use managed identities with Azure App Service. However, they work with a wide variety of services.

Here, we’ll guide you through establishing managed identities for a Spring Boot application deployed on Azure Kubernetes Service. The Spring Boot service will connect to an Azure Cosmos DB database, read data, and display it on a web page. We'll create an application-specific identity to access the required database.

Prerequisites

To follow this tutorial, you should know Java, and you'll need to set up a few things first:

  • Access to Azure Cloud Platform. It’s free to sign up for 12 months of access to popular services and offers a $200 credit.
  • Azure CLI installed on your local machine.
  • The Rkord application. This demo Spring Boot application for making user notes is developed using Spring MVC, the Thymeleaf framework, and a database. Previously, we used this application with Azure App Service in the first article of this series. Here, we’ll refactor and integrate the same application with Cosmos DB.

You can find the complete code for this article’s version of the Rkord application on GitHub.

Creating a Spring Boot Application

First, refactor the Rkord application to integrate Cosmos DB instead of SQL Server. Perform cleanup by removing Spring Data JPA and SQL Server dependencies and the associated properties. The code will raise compilation errors for JPA annotations and the JPA repository.

Next, add the Spring Boot Cosmos DB starter dependency. This action will also import the required Cosmos DB libraries.

XML
<dependency>
 <groupId>com.azure.spring</groupId>
 <artifactId>azure-spring-boot-starter-cosmos</artifactId>
 <version>3.11.0</version>
</dependency>

Updating the Notes Entity

Next, update the Notes entity with the Container annotation. Also, update the ID property to the String data type instead of the int data type. Additionally, add the Id and GeneratedValue annotations like this:

Java
@Container(containerName = "notescollection")
public class Notes {
   @Id
   @GeneratedValue
   private String id;
   private String message;

  // Removed for Brevity
}

Setting up the Notes Repository

The project adopts the Spring Data programming model to remove the boilerplate code for accessing Cosmos DB collections. So, you only need to create an entity-specific interface by extending the ReactiveCosmosRepository interface like this:

Java
public interface NotesRepository extends ReactiveCosmosRepository<Notes,String> {

}

Using the Azure Cosmos DB Service

The above changes would compile the application code, but the tests would still fail. The application cannot start due to the missing Cosmos DB details. You need to connect to an instance of Cosmos DB to build and deploy the application successfully.

So, in the Azure portal, create an instance using the Azure Cosmos DB service. Provide the server name, location, and associated resource group.

Also, provide the provisioned capacity for the instance. The serverless model is suitable for development and testing needs. Alternatively, select the provisioned throughput based on the application’s needs.

Finally, click the Create button on the Review screen to generate an instance based on the specified configuration.

Image 1

After generating the instance, note the access keys. Next, update application.properties with the details (location, key, and database name) of the instance generated above.

Azure-CLI
# Specify the DNS URI of your Azure Cosmos DB.
azure.cosmos.uri=https://rkord.documents.azure.com:443/
azure.cosmos.key=uXNFTFcAUwzAq4o7ds1l9hnhKnNnIVNrJ6fqWtvLWYhrIKKgHkRtCwdJ2PjwJIkbA216DzM8E2wyYcugGAUYOw==
azure.cosmos.database=rkord 

The test should pass, providing a successful build. You can now deploy the application on your local machine.

Setting Up Azure Kubernetes Service

Now we want to deploy the application on Azure Kubernetes Service (AKS). So, create a cluster using the Azure Kubernetes Service management console.

The console opens a dialog with various configuration options. Azure provides multiple configuration presets for diverse availability needs. You can select the dev/ Test configuration to create a cluster with minimal configuration.

Provide a valid cluster name, review the configuration, and click Create.

Image 2

After generating the cluster, you must update the kubeconfig file with its access details. You can use the CLI like this:

Azure-CLI
az aks get-credentials --resource-group rkord-notes-rg --name rkord-dev-aks

After updating the configuration, you can work with the cluster using the kubectl command:

kubectl get deployments --all-namespaces=true

Deploying the Application

We can only deploy Kubernetes applications as Pods and Services. Each Pod is an application container downloaded from a container registry. We can integrate AKS with any container registry, including Docker, Harbor, and Quay.

AKS also provides a deployment console for integrating projects with the Azure Container Registry. The integration requires a project-specific Dockerfile, so add the following file to the  project:

SQL
FROM azul/zulu-openjdk:11
COPY target/rkord.jar rkord.jar
EXPOSE 8080
CMD ["java", "-jar","/rkord.jar"]

Note that the Dockerfile refers to the project artifact as rkord.jar. You need to specify the name using the finalName XML tag in the POM file’s build section:

XML
<finalName>rkord</finalName>

Next, integrate Azure Container Registry using the Deployment console. The process will generate template deployment and service files in the manifest folder of the project directory. It will then build the project and deploy services to the cluster. After successful deployment, it will provide the application access URL from the Services and ingresses console. Access the applications using the specified IP and port.

Image 3

Enabling Managed Identity

In the previous section, we successfully deployed the Rkord application. However, the application code contains the access keys to the Cosmos DB, putting our database at risk of unauthorized access. We should enable managed identity for the environment and remove the credentials from the code.

Let's start by configuring managed identity for our Kubernetes cluster:

Azure-CLI
az aks update --resource-group rkord-notes-rg --name rkord-dev-aks --enable-managed-identity

The managed identity service provisions the following credentials:

  • Kubernetes service credential, used to access the API server
  • Kubelet service credential, used by Nodes to access other services

The application Pod will use the Kubelet service credential to access Cosmos DB. So, determine the kubelet service principal using this command:

Azure-CLI
az aks show --resource-group rkord-notes-rg --name rkord-dev-aks 
--query "identityProfile.kubeletidentity"

Enabling Cosmos RBAC

Next, we must allow Cosmos DB access for the above service principal. Cosmos DB employs role-based access control (RBAC). It has some built-in roles, but you can also customize roles for your application’s needs.

First, determine the built-in roles:

Azure-CLI
az cosmosdb SQL role definition list --account-name rkord --resource-group rkord-notes-rg

The contributor role provides full access to the database. Note the role definition ID. Then, enable the grant for the Kubelet service principal using this command:

Azure-CLI
az cosmosdb sql role assignment create --account-name rkord --resource-group rkord-notes-rg 
--scope "/" --principal-id 00b01194-00a6-4dca-9ac0-0ba8a6a84a31 
--role-definition-id /subscriptions/9b55b4b6-5a78-420f-a669-fdb43873b0ce/resourceGroups/
rkord-notes-rg/providers/Microsoft.DocumentDB/databaseAccounts/rkord/sqlRoleDefinitions/
00000000-0000-0000-0000-000000000002

Integrating Managed Identity

The Azure Identity SDK provides the required abstraction to load the identity. So, add the library to the set of project dependencies:

XML
<dependency>
 <groupId>com.azure</groupId>
 <artifactId>azure-identity</artifactId>
 <version>1.3.7</version>
</dependency>

The Cosmos DB Spring Boot starter doesn’t provide a way to configure the managed identity. So, we must refactor the code to load the managed identity explicitly. On the other hand, the test uses access keys to connect to the database. So, the application must support different ways to connect to Cosmos DB based on different environments.

Spring profiles provide the necessary support to achieve this diverse connection ability. Start by disabling the automatic Cosmos DB bean creation and create various environment-specific configurations:

Java
@SpringBootApplication(exclude = CosmosAutoConfiguration.class)
public class RkordApplication {

   public static void main(String[] args) {
       SpringApplication.run(RkordApplication.class, args);
   }
}

Now, create the following configuration class to load the managed identity (using ManagedIdentityCredential). The configuration then passes these managed identity credentials to the Cosmos DB client. The configuration class requires properties for the Cosmos DB location and database name, so add these properties to the application.properties file in the src/main/resources directory.

Java
@Profile("prod")
@Configuration
public class CosmosDBConfig extends AbstractCosmosConfiguration {
   @Value("${cosmos.dbname}")
   private  String dbname;
   @Value("${cosmos.loc}")
   private String url;

   protected String getDatabaseName() {
       return dbname;
   }

   @Bean
   public ManagedIdentityCredential dbCredential() {
        ManagedIdentityCredential managedIdentityCredential = 
                                  new ManagedIdentityCredentialBuilder()
               .build();
        return  managedIdentityCredential;
   }

   @Bean
   public CosmosClientBuilder cosmosClientBuilder(ManagedIdentityCredential dbCredential) {
       CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder();
       cosmosClientBuilder.credential(dbCredential).endpoint(url);
       return cosmosClientBuilder;
   }
}

The above class is marked with profile annotations and thus works only when the respective Spring profile is activated. So, activate the container’s profile by modifying CMD in the Dockerfile, like this:

Docker
CMD ["java", "-jar", "-Dspring.profiles.active=prod","/rkord.jar"]

Additionally, create a CosmosDBTestConfig class that loads access keys and connects to Cosmos DB using AzureKeyCredential. Also, update the test-based application.properties for the associated properties.

Java
@Configuration
public class CosmosTestConfig extends AbstractCosmosConfiguration {
   @Value("${cosmos.dbname}")
   private  String dbname;
   @Value("${cosmos.loc}")
   private String url;
   @Value("${cosmos.key}")
   private String key;

   protected String getDatabaseName() {
       return dbname;
   }

   @Bean
   public AzureKeyCredential dbCredential() {
        return  new AzureKeyCredential(key);
   }

   @Bean
   public CosmosClientBuilder cosmosClientBuilder(AzureKeyCredential dbCredential) {
       CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder();
       cosmosClientBuilder.credential(dbCredential).endpoint(url);
       return cosmosClientBuilder;
   }
}

Push the changes to the GitHub repository to invoke building and deployment. After successful deployment, determine the application URL from the AKS ingress console.

Image 4

Summary

We've now explored how managed identity enables containerized Spring Boot web apps deployed on Azure Kubernetes Service. Since the process eliminates the need for credentials, it eliminates the challenges around secured credentials exchange. This way, developers can remain focused on the business they need to support rather than handling diverse deployment challenges like updating and securing credentials.

Managed identities do more than connect to applications running on Azure App Service or Azure Kubernetes Service. Continue to the third and final part of this series to learn how to use Azure AD managed identities to obtain key store credentials.

To learn more about Java Azure Identity library, check out Azure authentication with Java and Azure Identity.

This article is part of the series 'Managed Identity in Java Apps View All

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionwhere is the dbname property being used after its values are loaded from properties file. Pin
Member 159445237-Mar-23 7:41
Member 159445237-Mar-23 7:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.