From 73a57412f00f5b2fe719515807199eba54adac0a Mon Sep 17 00:00:00 2001 From: Gabriel Fukushima Date: Tue, 29 Aug 2023 07:55:02 +1000 Subject: [PATCH] Azure response timeout configurable (#888) * Add azure response timeout option to CLI and metadata --- CHANGELOG.md | 3 ++ .../PicoCliAzureKeyVaultParameters.java | 11 ++++++ .../pegasys/web3signer/core/Eth1Runner.java | 3 +- .../keystorage/azure/AzureKeyVault.java | 34 ++++++++++++++----- .../keystorage/azure/AzureKeyVaultTest.java | 29 ++++++++-------- .../bulkloading/SecpAzureBulkLoader.java | 3 +- .../signing/config/AzureKeyVaultFactory.java | 19 ++++++++--- .../config/AzureKeyVaultParameters.java | 2 ++ .../metadata/AzureKeySigningMetadata.java | 9 ++++- .../metadata/AzureSecretSigningMetadata.java | 10 +++++- ...zureSecretSigningMetadataDeserializer.java | 15 +++++++- .../Secp256k1ArtifactSignerFactory.java | 3 +- .../signing/secp256k1/azure/AzureConfig.java | 14 ++++++-- .../azure/AzureKeyVaultSignerFactory.java | 3 +- .../config/AzureKeyVaultFactoryTest.java | 19 +++++++---- .../azure/AzureKeyVaultSignerTest.java | 11 ++++-- .../DefaultAzureKeyVaultParameters.java | 27 ++++++++++++++- 17 files changed, 169 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba20859ca..32e617a7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Breaking Changes - Eth2 Signing request body: deprecating `signingRoot` in favor of `signing_root` property. `signingRoot` will be removed in a future release. +### Features Added +- Add `--azure-response-timeout` to allow request response timeout to be configurable, the field `timeout` is also accepted in the Azure metadata file. [#888](https://github.com/Consensys/web3signer/pull/888) + ## 23.8.1 ### Bugs fixed diff --git a/commandline/src/main/java/tech/pegasys/web3signer/commandline/PicoCliAzureKeyVaultParameters.java b/commandline/src/main/java/tech/pegasys/web3signer/commandline/PicoCliAzureKeyVaultParameters.java index 944fba6b4..a3c2b4a89 100644 --- a/commandline/src/main/java/tech/pegasys/web3signer/commandline/PicoCliAzureKeyVaultParameters.java +++ b/commandline/src/main/java/tech/pegasys/web3signer/commandline/PicoCliAzureKeyVaultParameters.java @@ -61,6 +61,12 @@ public abstract class PicoCliAzureKeyVaultParameters implements AzureKeyVaultPar paramLabel = "") private String clientSecret; + @Option( + names = {"--azure-response-timeout"}, + description = "The response timeout to be used by the http client (in seconds)", + paramLabel = "") + private long timeout = 60; + @Override public boolean isAzureKeyVaultEnabled() { return azureKeyVaultEnabled; @@ -90,4 +96,9 @@ public String getClientId() { public String getClientSecret() { return clientSecret; } + + @Override + public long getTimeout() { + return timeout; + } } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java b/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java index 388f72890..7b6238b23 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java @@ -223,7 +223,8 @@ private MappedResults bulkLoadSigners( azureKeyVaultConfig.getClientSecret(), azureKeyVaultConfig.getKeyVaultName(), azureKeyVaultConfig.getTenantId(), - azureKeyVaultConfig.getAuthenticationMode()); + azureKeyVaultConfig.getAuthenticationMode(), + azureKeyVaultConfig.getTimeout()); final SecpAzureBulkLoader secpAzureBulkLoader = new SecpAzureBulkLoader(azureKeyVault, azureSignerFactory); final MappedResults azureResult = diff --git a/keystorage/src/main/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVault.java b/keystorage/src/main/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVault.java index 9dea3fb32..0b55a44f7 100644 --- a/keystorage/src/main/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVault.java +++ b/keystorage/src/main/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVault.java @@ -17,6 +17,7 @@ import java.net.URI; import java.net.http.HttpRequest; +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; @@ -31,7 +32,9 @@ import com.azure.core.credential.TokenCredential; import com.azure.core.credential.TokenRequestContext; import com.azure.core.exception.ResourceNotFoundException; +import com.azure.core.http.HttpClient; import com.azure.core.http.rest.PagedIterable; +import com.azure.core.util.HttpClientOptions; import com.azure.identity.ClientSecretCredentialBuilder; import com.azure.identity.ManagedIdentityCredentialBuilder; import com.azure.security.keyvault.keys.KeyClient; @@ -68,7 +71,8 @@ public static AzureKeyVault createUsingClientSecretCredentials( final String clientSecret, final String tenantId, final String vaultName, - final ExecutorService executorService) { + final ExecutorService executorService, + final long timeout) { final TokenCredential tokenCredential = new ClientSecretCredentialBuilder() .clientId(clientId) @@ -76,25 +80,39 @@ public static AzureKeyVault createUsingClientSecretCredentials( .tenantId(tenantId) .executorService(executorService) .build(); - return new AzureKeyVault(tokenCredential, vaultName); + return new AzureKeyVault(tokenCredential, vaultName, timeout); } public static AzureKeyVault createUsingManagedIdentity( - final Optional clientId, final String vaultName) { + final Optional clientId, final String vaultName, final long timeout) { final ManagedIdentityCredentialBuilder managedIdentityCredentialBuilder = new ManagedIdentityCredentialBuilder(); clientId.ifPresent(managedIdentityCredentialBuilder::clientId); - return new AzureKeyVault(managedIdentityCredentialBuilder.build(), vaultName); + return new AzureKeyVault(managedIdentityCredentialBuilder.build(), vaultName, timeout); } - private AzureKeyVault(final TokenCredential tokenCredential, final String vaultName) { + private AzureKeyVault( + final TokenCredential tokenCredential, final String vaultName, final long timeout) { this.tokenCredential = tokenCredential; final String vaultUrl = constructAzureKeyVaultUrl(vaultName); - secretClient = - new SecretClientBuilder().vaultUrl(vaultUrl).credential(tokenCredential).buildClient(); + final HttpClient customisedHttpClient = + HttpClient.createDefault( + new HttpClientOptions().setResponseTimeout(Duration.ofSeconds(timeout))); - keyClient = new KeyClientBuilder().vaultUrl(vaultUrl).credential(tokenCredential).buildClient(); + secretClient = + new SecretClientBuilder() + .httpClient(customisedHttpClient) + .vaultUrl(vaultUrl) + .credential(tokenCredential) + .buildClient(); + + keyClient = + new KeyClientBuilder() + .httpClient(customisedHttpClient) + .vaultUrl(vaultUrl) + .credential(tokenCredential) + .buildClient(); } public Optional fetchSecret(final String secretName) { diff --git a/keystorage/src/test/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVaultTest.java b/keystorage/src/test/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVaultTest.java index 8c2113a0b..c875eb8ef 100644 --- a/keystorage/src/test/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVaultTest.java +++ b/keystorage/src/test/java/tech/pegasys/web3signer/keystorage/azure/AzureKeyVaultTest.java @@ -45,6 +45,7 @@ public class AzureKeyVaultTest { private static final String EXPECTED_KEY2 = "0x5aba5b89c1d8b731dba1ba29128a4070df0dbfd7e0a67edb40ae7f860cd3ca1c"; private final ExecutorService azureExecutor = Executors.newCachedThreadPool(); + private final long AZURE_DEFAULT_TIMEOUT = 60; @BeforeAll public static void setup() { @@ -58,7 +59,7 @@ public static void setup() { void fetchExistingSecretKeyFromAzureVault() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final Optional hexKey = azureKeyVault.fetchSecret(SECRET_NAME); Assertions.assertThat(hexKey).isNotEmpty().get().isEqualTo(EXPECTED_KEY); } @@ -67,7 +68,7 @@ void fetchExistingSecretKeyFromAzureVault() { void connectingWithInvalidClientSecretThrowsException() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, "invalid", TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, "invalid", TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); Assertions.assertThatExceptionOfType(RuntimeException.class) .isThrownBy(() -> azureKeyVault.fetchSecret(SECRET_NAME)) .withMessageContaining("Invalid client secret"); @@ -77,7 +78,7 @@ void connectingWithInvalidClientSecretThrowsException() { void connectingWithInvalidClientIdThrowsException() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - "invalid", CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + "invalid", CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); Assertions.assertThatExceptionOfType(RuntimeException.class) .isThrownBy(() -> azureKeyVault.fetchSecret(SECRET_NAME)) .withMessageContaining( @@ -88,7 +89,7 @@ void connectingWithInvalidClientIdThrowsException() { void nonExistingSecretReturnEmpty() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); assertThat(azureKeyVault.fetchSecret("X-" + SECRET_NAME)).isEmpty(); } @@ -96,7 +97,7 @@ void nonExistingSecretReturnEmpty() { void secretsCanBeMappedUsingCustomMappingFunction() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults> result = azureKeyVault.mapSecrets(SimpleEntry::new, Collections.emptyMap()); @@ -113,7 +114,7 @@ void secretsCanBeMappedUsingCustomMappingFunction() { void keyPropertiesCanBeMappedUsingCustomMappingFunction() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults result = azureKeyVault.mapKeyProperties(KeyProperties::getName, Collections.emptyMap()); @@ -128,7 +129,7 @@ void keyPropertiesCanBeMappedUsingCustomMappingFunction() { void mapSecretsUsingTags() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults> result = azureKeyVault.mapSecrets(SimpleEntry::new, Map.of("ENV", "TEST")); @@ -150,7 +151,7 @@ void mapSecretsUsingTags() { void mapKeyPropertiesUsingTags() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults result = azureKeyVault.mapKeyProperties(KeyProperties::getName, Map.of("ENV", "TEST")); @@ -165,7 +166,7 @@ void mapKeyPropertiesUsingTags() { void mapSecretsWhenTagsDoesNotExist() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults> result = azureKeyVault.mapSecrets(SimpleEntry::new, Map.of("INVALID_TAG", "INVALID_TEST")); @@ -181,7 +182,7 @@ void mapSecretsWhenTagsDoesNotExist() { void mapKeyPropertiesWhenTagsDoesNotExist() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults result = azureKeyVault.mapKeyProperties( @@ -198,7 +199,7 @@ void mapKeyPropertiesWhenTagsDoesNotExist() { void mapSecretsThrowsAwayObjectsWhichFailMapper() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults> result = azureKeyVault.mapSecrets( @@ -226,7 +227,7 @@ void mapSecretsThrowsAwayObjectsWhichFailMapper() { void mapKeyPropertiesThrowsAwayObjectsWhichFailMapper() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults result = azureKeyVault.mapKeyProperties( @@ -255,7 +256,7 @@ void mapKeyPropertiesThrowsAwayObjectsWhichFailMapper() { void mapSecretsThrowsAwayObjectsWhichMapToNull() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults> result = azureKeyVault.mapSecrets( @@ -282,7 +283,7 @@ void mapSecretsThrowsAwayObjectsWhichMapToNull() { void mapKeyPropertiesThrowsAwayObjectsWhichMapToNull() { final AzureKeyVault azureKeyVault = createUsingClientSecretCredentials( - CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor); + CLIENT_ID, CLIENT_SECRET, TENANT_ID, VAULT_NAME, azureExecutor, AZURE_DEFAULT_TIMEOUT); final MappedResults result = azureKeyVault.mapKeyProperties( diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/bulkloading/SecpAzureBulkLoader.java b/signing/src/main/java/tech/pegasys/web3signer/signing/bulkloading/SecpAzureBulkLoader.java index b762447b3..b68498889 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/bulkloading/SecpAzureBulkLoader.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/bulkloading/SecpAzureBulkLoader.java @@ -46,6 +46,7 @@ private EthSecpArtifactSigner createSigner( keyName, azureKeyVaultParameters.getClientId(), azureKeyVaultParameters.getClientSecret(), - azureKeyVaultParameters.getTenantId()))); + azureKeyVaultParameters.getTenantId(), + azureKeyVaultParameters.getTimeout()))); } } diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactory.java b/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactory.java index f031ec770..3582f5920 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactory.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactory.java @@ -31,7 +31,8 @@ public AzureKeyVault createAzureKeyVault(final AzureKeyVaultParameters azureKeyV azureKeyVaultParameters.getClientSecret(), azureKeyVaultParameters.getKeyVaultName(), azureKeyVaultParameters.getTenantId(), - azureKeyVaultParameters.getAuthenticationMode()); + azureKeyVaultParameters.getAuthenticationMode(), + azureKeyVaultParameters.getTimeout()); } public AzureKeyVault createAzureKeyVault( @@ -39,15 +40,23 @@ public AzureKeyVault createAzureKeyVault( final String clientSecret, final String keyVaultName, final String tenantId, - final AzureAuthenticationMode mode) { + final AzureAuthenticationMode mode, + final long httpClientTimeout) { switch (mode) { case USER_ASSIGNED_MANAGED_IDENTITY: - return AzureKeyVault.createUsingManagedIdentity(Optional.of(clientId), keyVaultName); + return AzureKeyVault.createUsingManagedIdentity( + Optional.of(clientId), keyVaultName, httpClientTimeout); case SYSTEM_ASSIGNED_MANAGED_IDENTITY: - return AzureKeyVault.createUsingManagedIdentity(Optional.empty(), keyVaultName); + return AzureKeyVault.createUsingManagedIdentity( + Optional.empty(), keyVaultName, httpClientTimeout); default: return AzureKeyVault.createUsingClientSecretCredentials( - clientId, clientSecret, tenantId, keyVaultName, getOrCreateExecutor()); + clientId, + clientSecret, + tenantId, + keyVaultName, + getOrCreateExecutor(), + httpClientTimeout); } } diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultParameters.java b/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultParameters.java index 112c9fa52..08d9a4f63 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultParameters.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultParameters.java @@ -29,4 +29,6 @@ public interface AzureKeyVaultParameters { String getClientSecret(); Map getTags(); + + long getTimeout(); } diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureKeySigningMetadata.java b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureKeySigningMetadata.java index 0bd095780..57f1cf03e 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureKeySigningMetadata.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureKeySigningMetadata.java @@ -25,6 +25,7 @@ public class AzureKeySigningMetadata extends SigningMetadata { private final String tenantId; private final String vaultName; private final String keyName; + private final long timeout; @JsonCreator public AzureKeySigningMetadata( @@ -33,13 +34,15 @@ public AzureKeySigningMetadata( @JsonProperty("tenantId") final String tenantId, @JsonProperty("vaultName") final String vaultName, @JsonProperty("keyName") final String keyName, - @JsonProperty(value = "keyType") final KeyType keyType) { + @JsonProperty(value = "keyType") final KeyType keyType, + @JsonProperty(value = "timeout") final long timeout) { super(TYPE, keyType != null ? keyType : KeyType.SECP256K1); this.clientId = clientId; this.clientSecret = clientSecret; this.tenantId = tenantId; this.vaultName = vaultName; this.keyName = keyName; + this.timeout = (timeout != 0) ? timeout : 60; } public String getClientId() { @@ -62,6 +65,10 @@ public String getKeyName() { return keyName; } + public long getTimeout() { + return timeout; + } + @Override public ArtifactSigner createSigner(final ArtifactSignerFactory artifactSignerFactory) { return artifactSignerFactory.create(this); diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadata.java b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadata.java index a47bb9f29..ca49315d0 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadata.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadata.java @@ -31,6 +31,7 @@ public class AzureSecretSigningMetadata extends SigningMetadata implements Azure private final String vaultName; private final String secretName; private final AzureAuthenticationMode authenticationMode; + private final long timeout; public AzureSecretSigningMetadata( final String clientId, @@ -39,7 +40,8 @@ public AzureSecretSigningMetadata( final String vaultName, final String secretName, final AzureAuthenticationMode azureAuthenticationMode, - final KeyType keyType) { + final KeyType keyType, + final long timeout) { super(TYPE, keyType != null ? keyType : KeyType.BLS); this.clientId = clientId; this.clientSecret = clientSecret; @@ -50,6 +52,7 @@ public AzureSecretSigningMetadata( azureAuthenticationMode == null ? AzureAuthenticationMode.CLIENT_SECRET : azureAuthenticationMode; + this.timeout = timeout; } @Override @@ -97,4 +100,9 @@ public Map getTags() { // user is already providing the secret name to load the secret from. return Collections.emptyMap(); } + + @Override + public long getTimeout() { + return timeout; + } } diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadataDeserializer.java b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadataDeserializer.java index 41ee26609..812e3a2e4 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadataDeserializer.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/AzureSecretSigningMetadataDeserializer.java @@ -36,6 +36,7 @@ public class AzureSecretSigningMetadataDeserializer private static final String SECRET_NAME = "secretName"; private static final String AUTH_MODE = "authenticationMode"; private static final String KEY_TYPE = "keyType"; + private static final String TIMEOUT = "timeout"; @SuppressWarnings("Unused") public AzureSecretSigningMetadataDeserializer() { @@ -55,6 +56,8 @@ public AzureSecretSigningMetadata deserialize( String tenantId = null; String vaultName = null; String secretName = null; + long timeout = 60; + AzureAuthenticationMode authenticationMode = null; final JsonNode node = parser.getCodec().readTree(parser); @@ -94,10 +97,20 @@ public AzureSecretSigningMetadata deserialize( if (node.get(CLIENT_SECRET) != null) { clientSecret = node.get(CLIENT_SECRET).asText(); } + if (node.get(TIMEOUT) != null) { + timeout = node.get(TIMEOUT).asLong(); + } final AzureSecretSigningMetadata azureSecretSigningMetadata = new AzureSecretSigningMetadata( - clientId, clientSecret, tenantId, vaultName, secretName, authenticationMode, keyType); + clientId, + clientSecret, + tenantId, + vaultName, + secretName, + authenticationMode, + keyType, + timeout); validate(parser, azureSecretSigningMetadata); diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/Secp256k1ArtifactSignerFactory.java b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/Secp256k1ArtifactSignerFactory.java index fc23832b6..a78394996 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/Secp256k1ArtifactSignerFactory.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/config/metadata/Secp256k1ArtifactSignerFactory.java @@ -108,7 +108,8 @@ public ArtifactSigner create(final AzureKeySigningMetadata azureSigningMetadata) "", azureSigningMetadata.getClientId(), azureSigningMetadata.getClientSecret(), - azureSigningMetadata.getTenantId()); + azureSigningMetadata.getTenantId(), + azureSigningMetadata.getTimeout()); return signerFactory.apply(azureCloudSignerFactory.createSigner(config)); } diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureConfig.java b/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureConfig.java index 0ef1ea587..4efc52a31 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureConfig.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureConfig.java @@ -23,6 +23,7 @@ public class AzureConfig { private final String tenantId; // empty string means latest in azure private static final String KEY_LATEST_VERSION = ""; + private final long timeout; @JsonCreator public AzureConfig( @@ -31,13 +32,15 @@ public AzureConfig( final String keyVersion, final String clientId, final String clientSecret, - final String tenantId) { + final String tenantId, + final long timeout) { this.keyVaultName = keyVaultName; this.keyName = keyName; this.keyVersion = keyVersion; this.clientId = clientId; this.clientSecret = clientSecret; this.tenantId = tenantId; + this.timeout = timeout; } public AzureConfig( @@ -45,8 +48,9 @@ public AzureConfig( final String keyName, final String clientId, final String clientSecret, - final String tenantId) { - this(keyVaultName, keyName, KEY_LATEST_VERSION, clientId, clientSecret, tenantId); + final String tenantId, + final long timeout) { + this(keyVaultName, keyName, KEY_LATEST_VERSION, clientId, clientSecret, tenantId, timeout); } public String getKeyVaultName() { @@ -72,4 +76,8 @@ public String getClientSecret() { public String getTenantId() { return tenantId; } + + public long getTimeout() { + return timeout; + } } diff --git a/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerFactory.java b/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerFactory.java index d51634a51..dfa68e58b 100644 --- a/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerFactory.java +++ b/signing/src/main/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerFactory.java @@ -58,7 +58,8 @@ public Signer createSigner(final AzureConfig config) { config.getClientSecret(), config.getKeyVaultName(), config.getTenantId(), - AzureAuthenticationMode.CLIENT_SECRET); + AzureAuthenticationMode.CLIENT_SECRET, + config.getTimeout()); } catch (final Exception e) { LOG.error("Failed to connect to vault", e); throw new SignerInitializationException(INACCESSIBLE_KEY_ERROR, e); diff --git a/signing/src/test/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactoryTest.java b/signing/src/test/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactoryTest.java index f4efac819..2395102b9 100644 --- a/signing/src/test/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactoryTest.java +++ b/signing/src/test/java/tech/pegasys/web3signer/signing/config/AzureKeyVaultFactoryTest.java @@ -21,6 +21,7 @@ class AzureKeyVaultFactoryTest { final AzureKeyVaultFactory azureKeyVaultFactory = new AzureKeyVaultFactory(); + final long DEFAULT_AZURE_TIMEOUT = 60; @AfterEach void shutdownExecutor() { @@ -39,7 +40,8 @@ void createsExecutorWhenUsingClientSecretMode() { "clientSecret", "keyVaultName", "tenantId", - AzureAuthenticationMode.CLIENT_SECRET); + AzureAuthenticationMode.CLIENT_SECRET, + DEFAULT_AZURE_TIMEOUT); assertThat(azureKeyVaultFactory.getExecutorServiceCache().get()).isNotNull(); } @@ -50,7 +52,8 @@ void reusesExecutorWhenUsingClientSecretMode() { "clientSecret", "keyVaultName", "tenantId", - AzureAuthenticationMode.CLIENT_SECRET); + AzureAuthenticationMode.CLIENT_SECRET, + DEFAULT_AZURE_TIMEOUT); final ExecutorService executorService = azureKeyVaultFactory.getExecutorServiceCache().get(); assertThat(executorService).isNotNull(); @@ -59,7 +62,8 @@ void reusesExecutorWhenUsingClientSecretMode() { "clientSecret", "keyVaultName", "tenantId", - AzureAuthenticationMode.CLIENT_SECRET); + AzureAuthenticationMode.CLIENT_SECRET, + DEFAULT_AZURE_TIMEOUT); final ExecutorService executorService2 = azureKeyVaultFactory.getExecutorServiceCache().get(); assertThat(executorService).isSameAs(executorService2); } @@ -71,7 +75,8 @@ void doesNotCreateExecutorWhenUsingUserAssignedMode() { "clientSecret", "keyVaultName", "tenantId", - AzureAuthenticationMode.USER_ASSIGNED_MANAGED_IDENTITY); + AzureAuthenticationMode.USER_ASSIGNED_MANAGED_IDENTITY, + DEFAULT_AZURE_TIMEOUT); assertThat(azureKeyVaultFactory.getExecutorServiceCache().get()).isNull(); } @@ -82,7 +87,8 @@ void doesNotCreateExecutorWhenUsingSystemAssignedMode() { "clientSecret", "keyVaultName", "tenantId", - AzureAuthenticationMode.SYSTEM_ASSIGNED_MANAGED_IDENTITY); + AzureAuthenticationMode.SYSTEM_ASSIGNED_MANAGED_IDENTITY, + DEFAULT_AZURE_TIMEOUT); assertThat(azureKeyVaultFactory.getExecutorServiceCache().get()).isNull(); } @@ -93,7 +99,8 @@ void closeShutdownsExecutor() { "clientSecret", "keyVaultName", "tenantId", - AzureAuthenticationMode.CLIENT_SECRET); + AzureAuthenticationMode.CLIENT_SECRET, + DEFAULT_AZURE_TIMEOUT); final ExecutorService executorService = azureKeyVaultFactory.getExecutorServiceCache().get(); assertThat(executorService).isNotNull(); diff --git a/signing/src/test/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerTest.java b/signing/src/test/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerTest.java index 6b86c1da5..3fa551e11 100644 --- a/signing/src/test/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerTest.java +++ b/signing/src/test/java/tech/pegasys/web3signer/signing/secp256k1/azure/AzureKeyVaultSignerTest.java @@ -43,6 +43,7 @@ public class AzureKeyVaultSignerTest { private static final String KEY_NAME = "TestKey2"; // uses curve name P-256K private static final String UNSUPPORTED_CURVE_KEY_NAME = "TestKeyP521"; + private static final long AZURE_DEFAULT_TIMEOUT = 60; @BeforeAll static void preChecks() { @@ -59,7 +60,12 @@ static void preChecks() { void azureSignerCanSign() throws SignatureException { final AzureConfig config = new AzureConfig( - AZURE_KEY_VAULT_NAME, KEY_NAME, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID); + AZURE_KEY_VAULT_NAME, + KEY_NAME, + AZURE_CLIENT_ID, + AZURE_CLIENT_SECRET, + AZURE_TENANT_ID, + AZURE_DEFAULT_TIMEOUT); final Signer azureNonHashedDataSigner = new AzureKeyVaultSignerFactory(new AzureKeyVaultFactory(), new AzureHttpClientFactory()) @@ -92,7 +98,8 @@ public void azureKeyWithUnsupportedCurveThrowsError() { "", AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, - AZURE_TENANT_ID); + AZURE_TENANT_ID, + AZURE_DEFAULT_TIMEOUT); final AzureKeyVaultSignerFactory factory = new AzureKeyVaultSignerFactory(new AzureKeyVaultFactory(), new AzureHttpClientFactory()); diff --git a/signing/src/testFixtures/java/tech/pegasys/web3signer/signing/config/DefaultAzureKeyVaultParameters.java b/signing/src/testFixtures/java/tech/pegasys/web3signer/signing/config/DefaultAzureKeyVaultParameters.java index 775327789..349dad8c4 100644 --- a/signing/src/testFixtures/java/tech/pegasys/web3signer/signing/config/DefaultAzureKeyVaultParameters.java +++ b/signing/src/testFixtures/java/tech/pegasys/web3signer/signing/config/DefaultAzureKeyVaultParameters.java @@ -18,19 +18,28 @@ public class DefaultAzureKeyVaultParameters implements AzureKeyVaultParameters { + private static long AZURE_DEFAULT_TIMEOUT = 60; + private final String keyVaultName; private final AzureAuthenticationMode authenticationMode; private final String clientId; private final String tenantId; private final String clientSecret; private final Map tags = new HashMap<>(); + private final long timeout; public DefaultAzureKeyVaultParameters( final String keyVaultName, final String clientId, final String tenantId, final String clientSecret) { - this(keyVaultName, clientId, tenantId, clientSecret, Collections.emptyMap()); + this( + keyVaultName, + clientId, + tenantId, + clientSecret, + Collections.emptyMap(), + AZURE_DEFAULT_TIMEOUT); } public DefaultAzureKeyVaultParameters( @@ -39,12 +48,23 @@ public DefaultAzureKeyVaultParameters( final String tenantId, final String clientSecret, final Map tags) { + this(keyVaultName, clientId, tenantId, clientSecret, tags, AZURE_DEFAULT_TIMEOUT); + } + + public DefaultAzureKeyVaultParameters( + final String keyVaultName, + final String clientId, + final String tenantId, + final String clientSecret, + final Map tags, + final long timeout) { this.keyVaultName = keyVaultName; this.clientId = clientId; this.tenantId = tenantId; this.clientSecret = clientSecret; this.authenticationMode = AzureAuthenticationMode.CLIENT_SECRET; this.tags.putAll(tags); + this.timeout = timeout; } @Override @@ -81,4 +101,9 @@ public AzureAuthenticationMode getAuthenticationMode() { public Map getTags() { return tags; } + + @Override + public long getTimeout() { + return timeout; + } }