Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure response timeout configurable #888

Merged
merged 4 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public abstract class PicoCliAzureKeyVaultParameters implements AzureKeyVaultPar
paramLabel = "<CLIENT_SECRET>")
private String clientSecret;

@Option(
names = {"--azure-response-timeout"},
description = "The response timeout to be used by the http client (in seconds)",
paramLabel = "<AZURE_RESPONSE_TIMEOUT>")
private long timeout = 60;

@Override
public boolean isAzureKeyVaultEnabled() {
return azureKeyVaultEnabled;
Expand Down Expand Up @@ -90,4 +96,9 @@ public String getClientId() {
public String getClientSecret() {
return clientSecret;
}

@Override
public long getTimeout() {
return timeout;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ private MappedResults<ArtifactSigner> bulkLoadSigners(
azureKeyVaultConfig.getClientSecret(),
azureKeyVaultConfig.getKeyVaultName(),
azureKeyVaultConfig.getTenantId(),
azureKeyVaultConfig.getAuthenticationMode());
azureKeyVaultConfig.getAuthenticationMode(),
azureKeyVaultConfig.getTimeout());
final SecpAzureBulkLoader secpAzureBulkLoader =
new SecpAzureBulkLoader(azureKeyVault, azureSignerFactory);
final MappedResults<ArtifactSigner> azureResult =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -68,33 +71,48 @@ 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)
.clientSecret(clientSecret)
.tenantId(tenantId)
.executorService(executorService)
.build();
return new AzureKeyVault(tokenCredential, vaultName);
return new AzureKeyVault(tokenCredential, vaultName, timeout);
}

public static AzureKeyVault createUsingManagedIdentity(
final Optional<String> clientId, final String vaultName) {
final Optional<String> 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<String> fetchSecret(final String secretName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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<String> hexKey = azureKeyVault.fetchSecret(SECRET_NAME);
Assertions.assertThat(hexKey).isNotEmpty().get().isEqualTo(EXPECTED_KEY);
}
Expand All @@ -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);

Check failure

Code scanning / CodeQL

Hard-coded credential in API call

Hard-coded value flows to [sensitive API call](1).
Assertions.assertThatExceptionOfType(RuntimeException.class)
.isThrownBy(() -> azureKeyVault.fetchSecret(SECRET_NAME))
.withMessageContaining("Invalid client secret");
Expand All @@ -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(
Expand All @@ -88,15 +89,15 @@ 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();
}

@Test
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<SimpleEntry<String, String>> result =
azureKeyVault.mapSecrets(SimpleEntry::new, Collections.emptyMap());
Expand All @@ -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<String> result =
azureKeyVault.mapKeyProperties(KeyProperties::getName, Collections.emptyMap());
Expand All @@ -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<SimpleEntry<String, String>> result =
azureKeyVault.mapSecrets(SimpleEntry::new, Map.of("ENV", "TEST"));
Expand All @@ -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<String> result =
azureKeyVault.mapKeyProperties(KeyProperties::getName, Map.of("ENV", "TEST"));
Expand All @@ -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<SimpleEntry<String, String>> result =
azureKeyVault.mapSecrets(SimpleEntry::new, Map.of("INVALID_TAG", "INVALID_TEST"));
Expand All @@ -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<String> result =
azureKeyVault.mapKeyProperties(
Expand All @@ -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<SimpleEntry<String, String>> result =
azureKeyVault.mapSecrets(
Expand Down Expand Up @@ -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<String> result =
azureKeyVault.mapKeyProperties(
Expand Down Expand Up @@ -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<SimpleEntry<String, String>> result =
azureKeyVault.mapSecrets(
Expand All @@ -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<String> result =
azureKeyVault.mapKeyProperties(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ private EthSecpArtifactSigner createSigner(
keyName,
azureKeyVaultParameters.getClientId(),
azureKeyVaultParameters.getClientSecret(),
azureKeyVaultParameters.getTenantId())));
azureKeyVaultParameters.getTenantId(),
azureKeyVaultParameters.getTimeout())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,32 @@ public AzureKeyVault createAzureKeyVault(final AzureKeyVaultParameters azureKeyV
azureKeyVaultParameters.getClientSecret(),
azureKeyVaultParameters.getKeyVaultName(),
azureKeyVaultParameters.getTenantId(),
azureKeyVaultParameters.getAuthenticationMode());
azureKeyVaultParameters.getAuthenticationMode(),
azureKeyVaultParameters.getTimeout());
}

public AzureKeyVault createAzureKeyVault(
final String clientId,
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ public interface AzureKeyVaultParameters {
String getClientSecret();

Map<String, String> getTags();

long getTimeout();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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() {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -50,6 +52,7 @@ public AzureSecretSigningMetadata(
azureAuthenticationMode == null
? AzureAuthenticationMode.CLIENT_SECRET
: azureAuthenticationMode;
this.timeout = timeout;
}

@Override
Expand Down Expand Up @@ -97,4 +100,9 @@ public Map<String, String> getTags() {
// user is already providing the secret name to load the secret from.
return Collections.emptyMap();
}

@Override
public long getTimeout() {
return timeout;
}
}
Loading