3

I have a Java Spring boot application that just reads the secret from Azure Key Vault, below are steps used

  • Created an Application Registration

enter image description here

  • Copied the App Registration details

enter image description here

  • Generated Secret

enter image description here

  • Granted access on Azure Key Vault

enter image description here

and below is my Java Spring Boot application

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.contoso</groupId>
    <artifactId>keyvault</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>keyvault</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
        <azure.version>2.3.5</azure.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-keyvault-secrets-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.microsoft.azure</groupId>
                <artifactId>azure-spring-boot-bom</artifactId>
                <version>${azure.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

azure.keyvault.client-id=7a111923-xxxxxxxx-xxxxxx-31be31d233dd
azure.keyvault.client-key=gt_~k02yF_xxxxxx_vn3r1.GW
azure.keyvault.enabled=true
azure.keyvault.tenant-id=9cef136a-xxxx-xxxx-b7d3-d9d8a5a84182
azure.keyvault.uri=https://contosokvxxx.vault.azure.net/

KeyvaultApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;

@SpringBootApplication
@RestController
public class KeyvaultApplication {

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

  @GetMapping("get")
  public String get() {
    return connectionString;
  }

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

  public void run(String... varl) throws Exception {
    System.out.println(String.format("\nConnection String stored in Azure Key Vault:\n%s\n",connectionString));
  }  

}

It works and I could read the Secret from Azure Key Vault

enter image description here

so decided to remove the secrets from the application.properties and commented as below

#azure.keyvault.client-id=7a111923-1xxxxxxxx-31be31d233dd
#azure.keyvault.client-key=gt_~k02yF_xxxxxxxx-Hr6vn3r1.GW
azure.keyvault.enabled=true
#azure.keyvault.tenant-id=9cef136axxxxxxx-3-d9d8a5a84182
azure.keyvault.uri=https://contosokvxxx.vault.azure.net/

and added an Environment variable as shown below using setx

setx AzureServicesAuthConnectionString "RunAs=App;AppId=bb01c08b-xxxxxxxx-106;TenantId=9cef1-xxxxxxx-d9d8a5a84182;AppKey=xxxxxxxx-4Dpg-E3zrj~"

The project compiles without any isssues

>mvn clean compile package

but failed running

>mvn spring-boot:run

Error:

11:40:27.334 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to configure KeyVault property source
        at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessorHelper.addKeyVaultPropertySource(KeyVaultEnvironmentPostProcessorHelper.java:110)
        at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessor.postProcessEnvironment(KeyVaultEnvironmentPostProcessor.java:47)

Caused by: java.lang.RuntimeException: Max retries 3 times exceeded. Error Details: ManagedIdentityCredential authentication unavailable. Connection to IMDS endpoint cannot be established, Network is unreachable: no further information.
        at com.azure.core.http.policy.RetryPolicy.lambda$attemptAsync$1(RetryPolicy.java:116)
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:88)
One Developer
  • 99
  • 5
  • 43
  • 103

2 Answers2

3

Error Details: ManagedIdentityCredential authentication unavailable. Connection to IMDS endpoint cannot be established, Network is unreachable: no further information.

The error means you could not connect to the Azure Instance Metadata Service endpoint, it is a REST Endpoint that is available at a well-known non-routable IP address (169.254.169.254), MSI use it to get the token, it can be accessed only from within the Azure service e.g. web app, VM, etc. Simply put, you could not use MSI(managed identity) in local.

To use MSI get secret from the azure keyvault, follow this to deploy your application to azure web app, enable the system-assigned identity or user-assigned identity, then remove the azure.keyvault.client-key from application.properties, change the azure.keyvault.client-id with the MSI's client id, add it to the access policy of the keyvault, details follow this.

azure.keyvault.client-id=56rqs994-0o66-43o3-9roo-8e3534d0cb23
azure.keyvault.enabled=true
azure.keyvault.tenant-id=72s988os-86s1-41ns-91ab-2q7pq011qo47
azure.keyvault.uri=https://contosokv.vault.azure.net/   

Reference - Tutorial: Reading a secret from Azure Key Vault in a Spring Boot application

Joy Wang
  • 39,905
  • 3
  • 30
  • 54
  • Does it mean that we can't use the MSI on our local machine? Eg: setting the Environment variable, setx AzureServicesAuthConnectionString "RunAs=App;AppId=bb01c08b-xxxxxxxx-106;TenantId=9cef1-xxxxxxx-d9d8a5a84182;AppKey=xxxxxxxx-4Dpg-E3zrj~" – One Developer Nov 19 '20 at 07:38
  • I don't want to store the Azure Key vault confidential connection details in my Code repo, I may ignore the "application. properties" from checking in into the repo. However how do I ensure that the application works post deployment without the configuration settings? – One Developer Nov 19 '20 at 07:42
  • I kind want to have the same deployment experience on both the developers machine and on the target servers. – One Developer Nov 19 '20 at 07:46
  • 1
    @KarthikeyanVijayakumar Yes, it means you cannot use MSI in local, as I mentioned, the IMDS endpoint is just available in azure service. And as I know, the [`AzureServicesAuthConnectionString`](https://learn.microsoft.com/en-us/azure/key-vault/general/service-to-service-authentication#connection-string-support) is the feature in .net, it also uses the custom service principal instead of MSI. – Joy Wang Nov 19 '20 at 07:47
  • 1
    @KarthikeyanVijayakumar From your question, your app should work in local, to ensure it works after deployment, just change the `application.properties` like the doc, and I don't think you can miss the `azure.keyvault.uri` in it. – Joy Wang Nov 19 '20 at 07:52
  • May be a stupid question still I don't know the answer. How do I check-in the application.properties file only with azure.keyvault.uri while the development machine still needs other configuration including secrets? – One Developer Nov 19 '20 at 08:27
  • Does it mean that I have to use two different application.properties files on the development machine and don't checkin the file that has the confidential data? – One Developer Nov 19 '20 at 08:29
  • 2
    @KarthikeyanVijayakumar Yes, in the dev machine, `application.properties` needs the `azure.keyvault.client-key`, because you are using your own service principal to auth, in web app, you don't need it, because it will use MSI to auth. – Joy Wang Nov 19 '20 at 08:33
1

Please try to follow this sample.

It shows you how to use the Azure App Configuration service together with Azure Key Vault in a Java Spring application. It also sets the environment variables to connect key vault.

These Key Vault credentials are only used within your application. Your application authenticates directly with Key Vault using these credentials without involving the App Configuration service. The Key Vault provides authentication for both your application and your App Configuration service without sharing or exposing keys.

unknown
  • 6,778
  • 1
  • 5
  • 14
  • still this is not addressing the original issue as I have to still have the App configuration endpoint stored in the properties file. Example # Use default application name and no profile configured # Keys starting with /application/ will be matched spring: cloud: azure: appconfiguration: stores: - connection-string: "Endpoint=https://xxx.azconfig.io;xxxx' – One Developer Nov 18 '20 at 13:53
  • more over for the purposes of my project, I just want to use the Key vault with Spring boot without storing the credentials on the properties file. – One Developer Nov 18 '20 at 13:57
  • 1
    The environment variables only support to store `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, and `AZURE_TENANT_ID`. The other properties such as url need to be set in the properties file. – unknown Nov 19 '20 at 02:53