Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.

Commit 08686ad

Browse files
pulluribBhanu Pullurimacfarla
authored andcommitted
Add RPC HTTP options to specify custom truststore and password (#7978)
* Add RPC HTTP options to specify custom truststore and it's password * Update error logs to indicate options to use Signed-off-by: Bhanu Pulluri <[email protected]> --------- Signed-off-by: Bhanu Pulluri <[email protected]> Signed-off-by: Bhanu Pulluri <[email protected]> Co-authored-by: Bhanu Pulluri <[email protected]> Co-authored-by: Sally MacFarlane <[email protected]>
1 parent 5a691e8 commit 08686ad

File tree

7 files changed

+252
-17
lines changed

7 files changed

+252
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
- Fast Sync
1818

1919
### Additions and Improvements
20+
- Add RPC HTTP options to specify custom truststore and its password [#7978](https://github.com/hyperledger/besu/pull/7978)
2021
- Retrieve all transaction receipts for a block in one request [#6646](https://github.com/hyperledger/besu/pull/6646)
2122

23+
2224
### Bug fixes
2325
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
2426

besu/src/main/java/org/hyperledger/besu/cli/options/JsonRpcHttpOptions.java

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,20 @@ public class JsonRpcHttpOptions {
176176
"Enable to accept clients certificate signed by a valid CA for client authentication (default: ${DEFAULT-VALUE})")
177177
private final Boolean isRpcHttpTlsCAClientsEnabled = false;
178178

179+
@CommandLine.Option(
180+
names = {"--rpc-http-tls-truststore-file"},
181+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
182+
description = "Path to the truststore file for the JSON-RPC HTTP service.",
183+
arity = "1")
184+
private final Path rpcHttpTlsTruststoreFile = null;
185+
186+
@CommandLine.Option(
187+
names = {"--rpc-http-tls-truststore-password-file"},
188+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
189+
description = "Path to the file containing the password for the truststore.",
190+
arity = "1")
191+
private final Path rpcHttpTlsTruststorePasswordFile = null;
192+
179193
@CommandLine.Option(
180194
names = {"--rpc-http-tls-protocol", "--rpc-http-tls-protocols"},
181195
description = "Comma separated list of TLS protocols to support (default: ${DEFAULT-VALUE})",
@@ -306,7 +320,6 @@ public JsonRpcConfiguration jsonRpcConfiguration(
306320
jsonRpcConfiguration.setHost(
307321
Strings.isNullOrEmpty(rpcHttpHost) ? defaultHostAddress : rpcHttpHost);
308322
jsonRpcConfiguration.setHostsAllowlist(hostsAllowlist);
309-
;
310323
jsonRpcConfiguration.setHttpTimeoutSec(timoutSec);
311324
return jsonRpcConfiguration;
312325
}
@@ -330,7 +343,18 @@ private void checkRpcTlsClientAuthOptionsDependencies(
330343
commandLine,
331344
"--rpc-http-tls-client-auth-enabled",
332345
!isRpcHttpTlsClientAuthEnabled,
333-
asList("--rpc-http-tls-known-clients-file", "--rpc-http-tls-ca-clients-enabled"));
346+
asList(
347+
"--rpc-http-tls-known-clients-file",
348+
"--rpc-http-tls-ca-clients-enabled",
349+
"--rpc-http-tls-truststore-file",
350+
"--rpc-http-tls-truststore-password-file"));
351+
352+
CommandLineUtils.checkOptionDependencies(
353+
logger,
354+
commandLine,
355+
"--rpc-http-tls-truststore-file",
356+
rpcHttpTlsTruststoreFile == null,
357+
asList("--rpc-http-tls-truststore-password-file"));
334358
}
335359

336360
private void checkRpcTlsOptionsDependencies(final Logger logger, final CommandLine commandLine) {
@@ -392,12 +416,31 @@ private void validateTls(final CommandLine commandLine) {
392416
"File containing password to unlock keystore is required when TLS is enabled for JSON-RPC HTTP endpoint");
393417
}
394418

395-
if (isRpcHttpTlsClientAuthEnabled
396-
&& !isRpcHttpTlsCAClientsEnabled
397-
&& rpcHttpTlsKnownClientsFile == null) {
398-
throw new CommandLine.ParameterException(
399-
commandLine,
400-
"Known-clients file must be specified or CA clients must be enabled when TLS client authentication is enabled for JSON-RPC HTTP endpoint");
419+
if (isRpcHttpTlsClientAuthEnabled) {
420+
if (!isRpcHttpTlsCAClientsEnabled
421+
&& rpcHttpTlsKnownClientsFile == null
422+
&& rpcHttpTlsTruststoreFile == null) {
423+
throw new CommandLine.ParameterException(
424+
commandLine,
425+
"Configuration error: TLS client authentication is enabled, but none of the following options are provided: "
426+
+ "1. Specify a known-clients file (--rpc-http-tls-known-clients-file) and/or Enable CA clients (--rpc-http-tls-ca-clients-enabled). "
427+
+ "2. Specify a truststore file and its password file (--rpc-http-tls-truststore-file and --rpc-http-tls-truststore-password-file). "
428+
+ "Only one of these options must be configured");
429+
}
430+
431+
if (rpcHttpTlsTruststoreFile != null && rpcHttpTlsTruststorePasswordFile == null) {
432+
throw new CommandLine.ParameterException(
433+
commandLine,
434+
"Configuration error: A truststore file is specified for JSON RPC HTTP endpoint, but the corresponding truststore password file (--rpc-http-tls-truststore-password-file) is missing");
435+
}
436+
437+
if ((isRpcHttpTlsCAClientsEnabled || rpcHttpTlsKnownClientsFile != null)
438+
&& rpcHttpTlsTruststoreFile != null) {
439+
throw new CommandLine.ParameterException(
440+
commandLine,
441+
"Configuration error: Truststore file (--rpc-http-tls-truststore-file) cannot be used together with CA clients (--rpc-http-tls-ca-clients-enabled) or a known-clients (--rpc-http-tls-known-clients-file) option. "
442+
+ "These options are mutually exclusive. Choose either truststore-based authentication or known-clients/CA clients configuration.");
443+
}
401444
}
402445

403446
rpcHttpTlsProtocols.retainAll(getJDKEnabledProtocols());
@@ -441,10 +484,17 @@ private boolean isRpcTlsConfigurationRequired() {
441484

442485
private TlsClientAuthConfiguration rpcHttpTlsClientAuthConfiguration() {
443486
if (isRpcHttpTlsClientAuthEnabled) {
444-
return TlsClientAuthConfiguration.Builder.aTlsClientAuthConfiguration()
445-
.withKnownClientsFile(rpcHttpTlsKnownClientsFile)
446-
.withCaClientsEnabled(isRpcHttpTlsCAClientsEnabled)
447-
.build();
487+
TlsClientAuthConfiguration.Builder tlsClientAuthConfigurationBuilder =
488+
TlsClientAuthConfiguration.Builder.aTlsClientAuthConfiguration()
489+
.withKnownClientsFile(rpcHttpTlsKnownClientsFile)
490+
.withCaClientsEnabled(isRpcHttpTlsCAClientsEnabled)
491+
.withTruststorePath(rpcHttpTlsTruststoreFile);
492+
493+
if (rpcHttpTlsTruststorePasswordFile != null) {
494+
tlsClientAuthConfigurationBuilder.withTruststorePasswordSupplier(
495+
new FileBasedPasswordProvider(rpcHttpTlsTruststorePasswordFile));
496+
}
497+
return tlsClientAuthConfigurationBuilder.build();
448498
}
449499

450500
return null;

besu/src/test/java/org/hyperledger/besu/cli/options/JsonRpcHttpOptionsTest.java

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,10 @@ public void rpcHttpTlsClientAuthWithoutKnownFileReportsError() {
332332
assertThat(commandOutput.toString(UTF_8)).isEmpty();
333333
assertThat(commandErrorOutput.toString(UTF_8))
334334
.contains(
335-
"Known-clients file must be specified or CA clients must be enabled when TLS client authentication is enabled for JSON-RPC HTTP endpoint");
335+
"Configuration error: TLS client authentication is enabled, but none of the following options are provided: "
336+
+ "1. Specify a known-clients file (--rpc-http-tls-known-clients-file) and/or Enable CA clients (--rpc-http-tls-ca-clients-enabled). "
337+
+ "2. Specify a truststore file and its password file (--rpc-http-tls-truststore-file and --rpc-http-tls-truststore-password-file). "
338+
+ "Only one of these options must be configured");
336339
}
337340

338341
@Test
@@ -342,6 +345,7 @@ public void rpcHttpTlsClientAuthWithKnownClientFile() {
342345
final String keystoreFile = "/tmp/test.p12";
343346
final String keystorePasswordFile = "/tmp/test.txt";
344347
final String knownClientFile = "/tmp/knownClientFile";
348+
345349
parseCommand(
346350
"--rpc-http-enabled",
347351
"--rpc-http-host",
@@ -422,6 +426,90 @@ public void rpcHttpTlsClientAuthWithCAClient() {
422426
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
423427
}
424428

429+
@Test
430+
public void rpcHttpTlsClientAuthWithTrustStore() throws IOException {
431+
final String host = "1.2.3.4";
432+
final int port = 1234;
433+
final String keystoreFile = "/tmp/test.p12";
434+
final String keystorePasswordFile = "/tmp/test.txt";
435+
final String truststoreFile = "/tmp/truststore.p12";
436+
final String truststorePasswordFile = "/tmp/truststore.txt";
437+
438+
Files.writeString(Path.of(truststorePasswordFile), "password");
439+
parseCommand(
440+
"--rpc-http-enabled",
441+
"--rpc-http-host",
442+
host,
443+
"--rpc-http-port",
444+
String.valueOf(port),
445+
"--rpc-http-tls-enabled",
446+
"--rpc-http-tls-keystore-file",
447+
keystoreFile,
448+
"--rpc-http-tls-keystore-password-file",
449+
keystorePasswordFile,
450+
"--rpc-http-tls-client-auth-enabled",
451+
"--rpc-http-tls-truststore-file",
452+
truststoreFile,
453+
"--rpc-http-tls-truststore-password-file",
454+
truststorePasswordFile);
455+
456+
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
457+
verify(mockRunnerBuilder).build();
458+
459+
assertThat(jsonRpcConfigArgumentCaptor.getValue().getHost()).isEqualTo(host);
460+
assertThat(jsonRpcConfigArgumentCaptor.getValue().getPort()).isEqualTo(port);
461+
final Optional<TlsConfiguration> tlsConfiguration =
462+
jsonRpcConfigArgumentCaptor.getValue().getTlsConfiguration();
463+
assertThat(tlsConfiguration.isPresent()).isTrue();
464+
assertThat(tlsConfiguration.get().getKeyStorePath()).isEqualTo(Path.of(keystoreFile));
465+
assertThat(tlsConfiguration.get().getClientAuthConfiguration().isPresent()).isTrue();
466+
assertThat(tlsConfiguration.get().getClientAuthConfiguration().get().getTruststorePath())
467+
.isEqualTo(Optional.of(Path.of(truststoreFile)));
468+
assertThat(tlsConfiguration.get().getClientAuthConfiguration().get().getTrustStorePassword())
469+
.isEqualTo(Files.readString(Path.of(truststorePasswordFile)));
470+
471+
assertThat(commandOutput.toString(UTF_8)).isEmpty();
472+
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
473+
}
474+
475+
@Test
476+
public void rpcHttpTlsClientAuthWithTrustStoreAndKnownClientsFileReportsError()
477+
throws IOException {
478+
final String host = "1.2.3.4";
479+
final int port = 1234;
480+
final String keystoreFile = "/tmp/test.p12";
481+
final String keystorePasswordFile = "/tmp/test.txt";
482+
final String truststoreFile = "/tmp/truststore.p12";
483+
final String truststorePasswordFile = "/tmp/truststore.txt";
484+
final String knownClientFile = "/tmp/knownClientFile";
485+
486+
Files.writeString(Path.of(truststorePasswordFile), "password");
487+
parseCommand(
488+
"--rpc-http-enabled",
489+
"--rpc-http-host",
490+
host,
491+
"--rpc-http-port",
492+
String.valueOf(port),
493+
"--rpc-http-tls-enabled",
494+
"--rpc-http-tls-keystore-file",
495+
keystoreFile,
496+
"--rpc-http-tls-keystore-password-file",
497+
keystorePasswordFile,
498+
"--rpc-http-tls-client-auth-enabled",
499+
"--rpc-http-tls-truststore-file",
500+
truststoreFile,
501+
"--rpc-http-tls-truststore-password-file",
502+
truststorePasswordFile,
503+
"--rpc-http-tls-known-clients-file",
504+
knownClientFile);
505+
506+
assertThat(commandOutput.toString(UTF_8)).isEmpty();
507+
assertThat(commandErrorOutput.toString(UTF_8))
508+
.contains(
509+
"Configuration error: Truststore file (--rpc-http-tls-truststore-file) cannot be used together with CA clients (--rpc-http-tls-ca-clients-enabled) or a known-clients (--rpc-http-tls-known-clients-file) option. "
510+
+ "These options are mutually exclusive. Choose either truststore-based authentication or known-clients/CA clients configuration.");
511+
}
512+
425513
@Test
426514
public void rpcHttpTlsClientAuthWithCAClientAndKnownClientFile() {
427515
final String host = "1.2.3.4";

besu/src/test/resources/everything_config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ rpc-http-tls-keystore-password-file="none.passwd"
8484
rpc-http-tls-client-auth-enabled=false
8585
rpc-http-tls-known-clients-file="rpc_tls_clients.txt"
8686
rpc-http-tls-ca-clients-enabled=false
87+
rpc-http-tls-truststore-file="none.pfx"
88+
rpc-http-tls-truststore-password-file="none.passwd"
8789
rpc-http-authentication-jwt-algorithm="RS256"
8890
rpc-ws-authentication-jwt-algorithm="RS256"
8991
rpc-http-tls-protocols=["TLSv1.2,TlSv1.1"]

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ private void applyTlsConfig(final HttpServerOptions httpServerOptions) {
429429
try {
430430
httpServerOptions
431431
.setSsl(true)
432-
.setPfxKeyCertOptions(
432+
.setKeyCertOptions(
433433
new PfxOptions()
434434
.setPath(tlsConfiguration.getKeyStorePath().toString())
435435
.setPassword(tlsConfiguration.getKeyStorePassword()))
@@ -472,6 +472,14 @@ private void applyTlsClientAuth(
472472
httpServerOptions.setTrustOptions(
473473
allowlistClients(
474474
knownClientsFile, clientAuthConfiguration.isCaClientsEnabled())));
475+
clientAuthConfiguration
476+
.getTruststorePath()
477+
.ifPresent(
478+
truststorePath ->
479+
httpServerOptions.setTrustOptions(
480+
new PfxOptions()
481+
.setPath(truststorePath.toString())
482+
.setPassword(clientAuthConfiguration.getTrustStorePassword())));
475483
}
476484

477485
private String tlsLogMessage() {

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/tls/TlsClientAuthConfiguration.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,23 @@
1717
import java.nio.file.Path;
1818
import java.util.Objects;
1919
import java.util.Optional;
20+
import java.util.function.Supplier;
2021

2122
public class TlsClientAuthConfiguration {
2223
private final Optional<Path> knownClientsFile;
2324
private final boolean caClientsEnabled;
25+
private final Optional<Path> truststorePath;
26+
private final Supplier<String> trustStorePasswordSupplier;
2427

2528
private TlsClientAuthConfiguration(
26-
final Optional<Path> knownClientsFile, final boolean caClientsEnabled) {
29+
final Optional<Path> knownClientsFile,
30+
final boolean caClientsEnabled,
31+
final Optional<Path> truststorePath,
32+
final Supplier<String> trustStorePasswordSupplier) {
2733
this.knownClientsFile = knownClientsFile;
2834
this.caClientsEnabled = caClientsEnabled;
35+
this.truststorePath = truststorePath;
36+
this.trustStorePasswordSupplier = trustStorePasswordSupplier;
2937
}
3038

3139
public Optional<Path> getKnownClientsFile() {
@@ -36,9 +44,19 @@ public boolean isCaClientsEnabled() {
3644
return caClientsEnabled;
3745
}
3846

47+
public Optional<Path> getTruststorePath() {
48+
return truststorePath;
49+
}
50+
51+
public String getTrustStorePassword() {
52+
return trustStorePasswordSupplier.get();
53+
}
54+
3955
public static final class Builder {
4056
private Path knownClientsFile;
4157
private boolean caClientsEnabled;
58+
private Path truststorePath;
59+
private Supplier<String> trustStorePasswordSupplier;
4260

4361
private Builder() {}
4462

@@ -56,12 +74,29 @@ public Builder withCaClientsEnabled(final boolean caClientsEnabled) {
5674
return this;
5775
}
5876

77+
public Builder withTruststorePath(final Path truststorePath) {
78+
this.truststorePath = truststorePath;
79+
return this;
80+
}
81+
82+
public Builder withTruststorePasswordSupplier(final Supplier<String> keyStorePasswordSupplier) {
83+
this.trustStorePasswordSupplier = keyStorePasswordSupplier;
84+
return this;
85+
}
86+
5987
public TlsClientAuthConfiguration build() {
60-
if (!caClientsEnabled) {
88+
if (!caClientsEnabled && truststorePath == null) {
6189
Objects.requireNonNull(knownClientsFile, "Known Clients File is required");
6290
}
91+
if (!caClientsEnabled && knownClientsFile == null) {
92+
Objects.requireNonNull(truststorePath, "Truststore File is required");
93+
}
94+
6395
return new TlsClientAuthConfiguration(
64-
Optional.ofNullable(knownClientsFile), caClientsEnabled);
96+
Optional.ofNullable(knownClientsFile),
97+
caClientsEnabled,
98+
Optional.ofNullable(truststorePath),
99+
trustStorePasswordSupplier);
65100
}
66101
}
67102
}

0 commit comments

Comments
 (0)