Fix Key Vault JCA configuration lifetime and Spring AIA support - #50163
Fix Key Vault JCA configuration lifetime and Spring AIA support#50163Moary Chen (moarychan) wants to merge 11 commits into
Conversation
|
Azure Pipelines: Successfully started running 3 pipeline(s). 33 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR exposes the Key Vault JCA AIA-download opt-out through Spring SSL bundle configuration and fixes a lifetime/configuration issue by capturing the AIA-disable setting per Key Vault client/keystore instance (instead of relying on a mutable JVM-global system property during lazy chain completion).
Changes:
- Added Spring SSL bundle properties for
disable-aia-downloadand mapped them toazure.keyvault.jca.disable-aia-downloadper keystore/truststore initialization. - Updated Key Vault JCA internals to pass
disableAiaDownloadexplicitly through certificate loading/chain completion and to retain the setting perKeyVaultClient. - Updated/expanded unit tests and documentation to reflect the new per-client behavior and new Spring configuration surface.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/spring/spring-cloud-azure-starter-keyvault-jca/pom.xml | Bumps the Key Vault JCA dependency to the beta version that contains the per-client AIA configuration changes. |
| sdk/spring/spring-cloud-azure-autoconfigure/pom.xml | Updates optional Key Vault JCA dependency version to align with the new behavior. |
| sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/properties/AzureKeyVaultSslBundleProperties.java | Adds disableAiaDownload boolean to SSL bundle keystore/truststore configuration model. |
| sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrar.java | Maps the new Spring property to azure.keyvault.jca.disable-aia-download prior to JCA keystore initialization. |
| sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrarTests.java | Verifies the registrar sets the system property appropriately per store initialization. |
| sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultJcaAutoConfigurationTests.java | Verifies property binding for truststore.disable-aia-download (and default false for keystore in another bundle). |
| sdk/spring/CHANGELOG.md | Documents new Spring configuration knobs for disabling AIA downloads. |
| sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java | Captures disableAiaDownload at keystore construction time and passes it to certificate/cache layers. |
| sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java | Adds disableAiaDownload as a constructor parameter and uses it when loading certificate chains. |
| sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java | Threads disableAiaDownload through client creation/update to retain per-client settings. |
| sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/CertificateUtil.java | Makes AIA-disable an explicit input to secret-bundle certificate parsing/chain completion. |
| sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/utils/AiaCertificateChainUtil.java | Updates AIA completion API to be explicitly gated by a passed disableAiaDownload flag. |
| sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/utils/AiaCertificateChainTest.java | Updates tests to call new APIs and adds coverage for per-client setting retention. |
| sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/utils/CertificateUtilTest.java | Updates tests to use the new loadCertificatesFromSecretBundleValue(..., disableAiaDownload) signature. |
| sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/utils/CertificateOrderTest.java | Updates tests to use the new certificate-loading signature. |
| sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java | Updates static mocking for the new CertificateUtil overload signature. |
| sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificatesTest.java | Updates updateKeyVaultClient call sites for the new parameter. |
| sdk/keyvault/azure-security-keyvault-jca/README.md | Documents the per-keystore capture semantics for azure.keyvault.jca.disable-aia-download. |
| sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md | Updates release notes to clarify per-client capture/retention of the AIA-disable setting. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.
Suppressed comments (4)
sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java:127
- The JavaDoc lists system property keys using camelCase (tenantId/clientId/clientSecret/managedIdentity), but the implementation reads hyphenated keys (tenant-id/client-id/client-secret/managed-identity). This mismatch can mislead users configuring the provider.
* The constructor uses System.getProperty for
* <code>azure.keyvault.uri</code>,
* <code>azure.keyvault.tenantId</code>,
* <code>azure.keyvault.clientId</code>,
* <code>azure.keyvault.clientSecret</code>,
* <code>azure.keyvault.managedIdentity</code>, and
* <code>azure.keyvault.jca.disable-aia-download</code> to initialize the
sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/utils/AiaCertificateChainTest.java:492
- This test modifies the JVM-global
azure.keyvault.jca.disable-aia-downloadsystem property but never restores it. Cleaning up the prior value avoids leaking global state into other tests.
// Simulate another SSL bundle replacing the JVM-global value before this client lazily loads its chain.
System.setProperty(AiaCertificateChainUtil.DISABLE_AIA_DOWNLOAD_PROPERTY, "true");
try (MockedStatic<HttpUtil> httpMock = Mockito.mockStatic(HttpUtil.class)) {
httpMock
.when(() -> HttpUtil.get(secretId + HttpUtil.API_VERSION_POSTFIX,
Collections.singletonMap("Authorization", "Bearer test-token")))
.thenReturn(JsonConverterUtil.toJson(secretBundle));
mockAiaResponse(httpMock, AIA_INTERMEDIATE_URL, intermediateCert.getEncoded());
mockAiaResponse(httpMock, AIA_ROOT_URL, rootCert.getEncoded());
Certificate[] result = keyVaultClient.getCertificateChainForVersion(certificateVersion);
assertArrayEquals(new Certificate[] { leafCert, intermediateCert, rootCert }, result,
"The client must keep the AIA setting captured when it was constructed");
httpMock.verify(() -> HttpUtil.getBytesWithMetadata(AIA_INTERMEDIATE_URL), Mockito.times(1));
httpMock.verify(() -> HttpUtil.getBytesWithMetadata(AIA_ROOT_URL), Mockito.times(1));
}
sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/jca/AzureKeyVaultSslBundleRegistrarTests.java:300
- This test leaves the global system property
azure.keyvault.jca.disable-aia-downloadset after the registrar runs (it ends as "false" for the truststore). Persisting global state can make later tests order-dependent/flaky.
try (MockedStatic<KeyStore> keyStoreMockedStatic = mockStatic(KeyStore.class)) {
KeyStore keyStore = Mockito.mock(KeyStore.class);
List<String> configuredValues = new ArrayList<>();
keyStoreMockedStatic.when(() -> KeyStore.getInstance(KeyVaultJcaProvider.PROVIDER_NAME))
.thenAnswer(invocation -> {
configuredValues.add(System.getProperty("azure.keyvault.jca.disable-aia-download"));
return keyStore;
});
AzureKeyVaultJcaProperties.JcaVaultProperties vaultProperties
= new AzureKeyVaultJcaProperties.JcaVaultProperties();
vaultProperties.setEndpoint("https://test.vault.azure.net/");
jcaProperties.getVaults().put("keyvault1", vaultProperties);
AzureKeyVaultSslBundleProperties.KeyVaultSslBundleProperties bundleProperties
= new AzureKeyVaultSslBundleProperties.KeyVaultSslBundleProperties();
bundleProperties.getKeystore().setKeyvaultRef("keyvault1");
bundleProperties.getKeystore().setDisableAiaDownload(true);
bundleProperties.getTruststore().setKeyvaultRef("keyvault1");
sslBundleProperties.getKeyvault().put("testBundle", bundleProperties);
registrar.registerBundles(Mockito.mock(SslBundleRegistry.class));
assertThat(configuredValues).containsExactly("true", "false");
}
sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/utils/AiaCertificateChainTest.java:461
- This test modifies the JVM-global
azure.keyvault.jca.disable-aia-downloadsystem property but never restores it. Even though the test is simulating another bundle overwriting the value, leaving it set can leak into other tests and cause order-dependent failures.
This issue also appears on line 475 of the same file.
// Simulate another SSL bundle replacing the JVM-global value before this client lazily loads its chain.
System.setProperty(AiaCertificateChainUtil.DISABLE_AIA_DOWNLOAD_PROPERTY, "false");
try (MockedStatic<HttpUtil> httpMock = Mockito.mockStatic(HttpUtil.class)) {
httpMock
.when(() -> HttpUtil.get(secretId + HttpUtil.API_VERSION_POSTFIX,
Collections.singletonMap("Authorization", "Bearer test-token")))
.thenReturn(JsonConverterUtil.toJson(secretBundle));
mockAiaResponse(httpMock, AIA_INTERMEDIATE_URL, intermediateCert.getEncoded());
mockAiaResponse(httpMock, AIA_ROOT_URL, rootCert.getEncoded());
Certificate[] result = keyVaultClient.getCertificateChainForVersion(certificateVersion);
assertArrayEquals(new Certificate[] { leafCert }, result,
"The client must keep the AIA setting captured when it was constructed");
httpMock.verify(() -> HttpUtil.getBytesWithMetadata(AIA_INTERMEDIATE_URL), Mockito.never());
httpMock.verify(() -> HttpUtil.getBytesWithMetadata(AIA_ROOT_URL), Mockito.never());
}
|
Addressed the four suppressed Copilot comments from review 4949901732 in a800640:
Validation:
|
vcolin7
left a comment
There was a problem hiding this comment.
Functionally, the changes look fine to me. I added a few comments on sticking to the established way of declaring these system properties.
I think we should also get somebody from your team to review this PR because I'm not all that familiar with how things should be done in Spring, Moary Chen (@moarychan).
|
Azure Pipelines: Successfully started running 3 pipeline(s). 33 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Summary
azure.keyvault.jca.disable-aia-downloadas a per-KeyStore setting. The default remainsfalse.spring.ssl.bundle.keyvault.<bundle-name>.keystore.disable-aia-downloadandspring.ssl.bundle.keyvault.<bundle-name>.truststore.disable-aia-download.KeyVaultJcaPropertyNames.Bug fix
JCA previously read KeyStore settings from system properties at different times. With multiple Spring SSL bundles, a later bundle could overwrite values before an earlier KeyStore lazily loaded its certificates.
KeyVaultLoadStoreParameternow carries the complete KeyStore, client, cache, filter, path, and AIA configuration. Default construction, system-property initialization, and explicitKeyStore.load(...)each use one configuration snapshot per KeyStore.