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

[BESU-77] - Enable TLS for JSON-RPC HTTP Service #271

Merged
merged 81 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
9c52a7c
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 20, 2019
78b93d7
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 23, 2019
f36fc99
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Dec 23, 2019
d03eadc
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 23, 2019
d59ed36
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 23, 2019
fd9800d
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 24, 2019
6b7832e
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Dec 24, 2019
cbbbb97
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 24, 2019
f2afeda
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
36873bc
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
c693311
spotless fix
usmansaleem Dec 27, 2019
1b74c62
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
93b23bd
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
952df48
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
88dafd7
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
ced35b1
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
41e1458
[BESU-77] - Enable TLS for JSON-RPC HTTP Service
usmansaleem Dec 27, 2019
68ec213
circleci tls test
usmansaleem Dec 27, 2019
bfd219b
disable client side authentication in tls unit test
usmansaleem Dec 27, 2019
d93013f
disable client side authentication in tls unit test
usmansaleem Dec 27, 2019
b3dec60
disable client side authentication in tls unit test
usmansaleem Dec 27, 2019
fefc376
more ssl logging
usmansaleem Dec 27, 2019
4d65735
circle update jdk version
usmansaleem Dec 27, 2019
8ec7836
increasing tls handshake timeout
usmansaleem Dec 29, 2019
366defb
circleci jdk13 docker image
usmansaleem Dec 29, 2019
0efad5b
circleci libsodium version for buster docker image
usmansaleem Dec 29, 2019
ae6be9d
ssl all debug
usmansaleem Dec 29, 2019
5ba7728
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Dec 29, 2019
42deca4
reverting circleci docker image changes
usmansaleem Dec 29, 2019
184feac
simplyfying method signatures in tls unit test and reducing ssl debug…
usmansaleem Dec 29, 2019
1b82a61
increasing ssl handshake timeout
usmansaleem Dec 29, 2019
22e0d25
ssl debug logging all
usmansaleem Dec 29, 2019
9f625c2
some more logging in tls unit test
usmansaleem Dec 29, 2019
40a5366
trying to use localhost in tls unit test
usmansaleem Dec 29, 2019
5d6e3e6
using less secure random generator in tls unit test to speed up in CI
usmansaleem Dec 29, 2019
38facbd
supressing warnings about securerandom in tls unit test
usmansaleem Dec 29, 2019
904e069
supressing warnings about securerandom in tls unit test
usmansaleem Dec 30, 2019
009e236
reverting ssl debug logging and refactoring tls http client to a sepa…
usmansaleem Dec 30, 2019
d359f01
Adding license header
usmansaleem Dec 30, 2019
b4ed266
Change to more secure non-blocking random generator. Change back to o…
usmansaleem Dec 30, 2019
f58361e
Adding unknown clients test case
usmansaleem Dec 30, 2019
cd0c116
Use system property java.security.egd from circleci for gradle unit t…
usmansaleem Dec 31, 2019
a6103e3
spotless fix
usmansaleem Dec 31, 2019
2e645c2
spotless fix
usmansaleem Dec 31, 2019
00a7d73
errorprone fixes
usmansaleem Dec 31, 2019
252ac96
assert IOException in tls unit test case
usmansaleem Dec 31, 2019
a67bbe9
spotless fix
usmansaleem Dec 31, 2019
3b2857e
tls unit test - removing securerandom so that is uses default
usmansaleem Dec 31, 2019
adff202
circleci resetting system property
usmansaleem Dec 31, 2019
9beb98a
revert assigning java.security.egd in gradle
usmansaleem Dec 31, 2019
4ec1152
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 1, 2020
d9bec41
review suggestion - ofNullable
usmansaleem Jan 1, 2020
e287b4c
review suggestion - moving some methods out of jsonrpchttpservice
usmansaleem Jan 2, 2020
2af0252
review suggestion - moving some methods out of jsonrpchttpservice
usmansaleem Jan 2, 2020
56afb02
spotless fix
usmansaleem Jan 2, 2020
45bb39e
review suggestion - refactoring test TlsHttpClient
usmansaleem Jan 2, 2020
9579395
simplifying tlsconfiguration
usmansaleem Jan 3, 2020
c376913
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 3, 2020
e14fe8d
spotless fix
usmansaleem Jan 3, 2020
0188e20
tls misconfiguration unit test - pass 1
usmansaleem Jan 6, 2020
ba2c17d
tls misconfiguration unit test - pass 2
usmansaleem Jan 7, 2020
b043538
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 7, 2020
a209866
tlsconfiguration invalid known clients file error handling
usmansaleem Jan 7, 2020
d61ec6d
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 7, 2020
b305b42
merge fixes
usmansaleem Jan 7, 2020
ed3964c
spotless fixes
usmansaleem Jan 8, 2020
98713cc
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 9, 2020
c7ca9b6
final keyword in catch
usmansaleem Jan 9, 2020
cc5ab46
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 9, 2020
53e4f03
merge conflicts
usmansaleem Jan 9, 2020
f92ca14
review suggestion - besu command tls cli verifications
usmansaleem Jan 9, 2020
f24f8a0
review suggestion - updating tlsconfiguration
usmansaleem Jan 10, 2020
c6e3f6b
review suggestions - using keystore in description
usmansaleem Jan 10, 2020
8a45d26
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 10, 2020
258a8b0
errorpront unused method fix
usmansaleem Jan 10, 2020
f98974d
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 12, 2020
549e9c9
review suggestions - introducing tls filebasedpasswordprovider and re…
usmansaleem Jan 13, 2020
6608c68
errorprone reported fix
usmansaleem Jan 13, 2020
4ad5037
review suggestion - updating tlsconfiguration using public constructor
usmansaleem Jan 13, 2020
a0a9ded
review suggestion - updating tlsconfiguration usage in test case
usmansaleem Jan 13, 2020
9387555
Merge remote-tracking branch 'upstream/master' into feature/rpc_tls_u…
usmansaleem Jan 13, 2020
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
94 changes: 93 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration;
import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration;
import org.hyperledger.besu.ethereum.api.tls.TlsConfigurationException;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.MiningParameters;
Expand Down Expand Up @@ -141,8 +143,10 @@
import java.util.stream.Collectors;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
Expand Down Expand Up @@ -447,6 +451,32 @@ void setBannedNodeIds(final List<String> values) {
"Require authentication for the JSON-RPC HTTP service (default: ${DEFAULT-VALUE})")
private final Boolean isRpcHttpAuthenticationEnabled = false;

@Option(
names = {"--rpc-http-tls-enabled"},
description = "Enable TLS for the JSON-RPC HTTP service (default: ${DEFAULT-VALUE})")
private final Boolean isRpcHttpTlsEnabled = false;

@Option(
names = {"--rpc-http-tls-keystore-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"PKCS#12 format key store file for the JSON-RPC HTTP service. Required if TLS is enabled.")
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
private final Path rpcHttpTlsKeyStoreFile = null;

@Option(
names = {"--rpc-http-tls-keystore-password-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"Key store password file for the JSON-RPC HTTP service. Required if TLS is enabled.")
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
private final Path rpcHttpTlsKeyStorePasswordFile = null;

@Option(
names = {"--rpc-http-tls-known-clients-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"Known clients common name and fingerprints to enable TLS client authentication for the JSON-RPC HTTP service.")
private final Path rpcHttpTlsKnownClientsFile = null;

@Option(
names = {"--rpc-ws-enabled"},
description = "Set to start the JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})")
Expand Down Expand Up @@ -1154,6 +1184,15 @@ private GraphQLConfiguration graphQLConfiguration() {
}

private JsonRpcConfiguration jsonRpcConfiguration() {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-http-tls-enabled",
!isRpcHttpTlsEnabled,
asList(
"--rpc-http-tls-keystore-file",
"--rpc-http-tls-keystore-password-file",
"--rpc-http-tls-known-clients-file"));

CommandLineUtils.checkOptionDependencies(
logger,
Expand All @@ -1168,7 +1207,11 @@ private JsonRpcConfiguration jsonRpcConfiguration() {
"--rpc-http-port",
"--rpc-http-authentication-enabled",
"--rpc-http-authentication-credentials-file",
"--rpc-http-authentication-public-key-file"));
"--rpc-http-authentication-public-key-file",
"--rpc-http-tls-enabled",
"--rpc-http-tls-keystore-file",
"--rpc-http-tls-keystore-password-file",
"--rpc-http-tls-known-clients-file"));

if (isRpcHttpAuthenticationEnabled
&& rpcHttpAuthenticationCredentialsFile() == null
Expand All @@ -1178,6 +1221,20 @@ && rpcHttpAuthenticationPublicKeyFile() == null) {
"Unable to authenticate JSON-RPC HTTP endpoint without a supplied credentials file or authentication public key file");
}

if (isRpcHttpEnabled && isRpcHttpTlsEnabled) {
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved
if (rpcHttpTlsKeyStoreFile == null) {
throw new ParameterException(
commandLine,
"Key store file is required when TLS is enabled for JSON-RPC HTTP endpoint");
}

if (rpcHttpTlsKeyStorePasswordFile == null) {
throw new ParameterException(
commandLine,
"Key store password file is required when TLS is enabled for JSON-RPC HTTP endpoint");
}
}

final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(isRpcHttpEnabled);
jsonRpcConfiguration.setHost(rpcHttpHost);
Expand All @@ -1188,9 +1245,44 @@ && rpcHttpAuthenticationPublicKeyFile() == null) {
jsonRpcConfiguration.setAuthenticationEnabled(isRpcHttpAuthenticationEnabled);
jsonRpcConfiguration.setAuthenticationCredentialsFile(rpcHttpAuthenticationCredentialsFile());
jsonRpcConfiguration.setAuthenticationPublicKeyFile(rpcHttpAuthenticationPublicKeyFile());

if (isRpcHttpEnabled && isRpcHttpTlsEnabled) {
jsonRpcConfiguration.setTlsConfiguration(getRpcHttpTlsConfiguration());
}
return jsonRpcConfiguration;
}

private TlsConfiguration getRpcHttpTlsConfiguration() {
final String password;
try {
password =
Files.asCharSource(rpcHttpTlsKeyStorePasswordFile.toFile(), Charsets.UTF_8)
.readFirstLine();
if (password == null) {
throw new ParameterException(
commandLine, "Unable to read key store password for JSON-RPC HTTP endpoint");
}
} catch (IOException e) {
throw new ParameterException(
commandLine, "Unable to read key store password file for JSON-RPC HTTP endpoint", e);
}

try {
return TlsConfiguration.TlsConfigurationBuilder.aTlsConfiguration()
.withKeyStorePath(rpcHttpTlsKeyStoreFile.normalize())
.withKeyStorePassword(password)
.withKnownClientsFile(rpcHttpTlsKnownClientsFile)
.build();
} catch (TlsConfigurationException e) {
throw new ParameterException(
commandLine,
String.format(
"Unable to initialize TLS configuration for JSON-RPC HTTP endpoint: %s",
e.getMessage()),
e);
}
}

private WebSocketConfiguration webSocketConfiguration() {

CommandLineUtils.checkOptionDependencies(
Expand Down
4 changes: 4 additions & 0 deletions besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ rpc-http-cors-origins=["none"]
rpc-http-authentication-enabled=false
rpc-http-authentication-credentials-file="none"
rpc-http-authentication-jwt-public-key-file="none"
rpc-http-tls-enabled=false
rpc-http-tls-keystore-file="none.pfx"
rpc-http-tls-keystore-password-file="none.passwd"
rpc-http-tls-known-clients-file="rpc_tls_clients.txt"

# GRAPHQL HTTP
graphql-http-enabled=false
Expand Down
1 change: 1 addition & 0 deletions ethereum/api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ dependencies {
implementation 'io.vertx:vertx-unit'
implementation 'io.vertx:vertx-web'
implementation 'org.apache.tuweni:tuweni-bytes'
implementation 'org.apache.tuweni:tuweni-net'
implementation 'org.apache.tuweni:tuweni-toml'
implementation 'org.apache.tuweni:tuweni-units'
implementation 'org.bouncycastle:bcprov-jdk15on'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc;

import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import com.google.common.base.MoreObjects;

Expand All @@ -37,6 +40,7 @@ public class JsonRpcConfiguration {
private boolean authenticationEnabled = false;
private String authenticationCredentialsFile;
private File authenticationPublicKeyFile;
private Optional<TlsConfiguration> tlsConfiguration = Optional.empty();

public static JsonRpcConfiguration createDefault() {
final JsonRpcConfiguration config = new JsonRpcConfiguration();
Expand Down Expand Up @@ -128,6 +132,14 @@ public void setAuthenticationPublicKeyFile(final File authenticationPublicKeyFil
this.authenticationPublicKeyFile = authenticationPublicKeyFile;
}

public Optional<TlsConfiguration> getTlsConfiguration() {
return tlsConfiguration;
}

public void setTlsConfiguration(final TlsConfiguration tlsConfiguration) {
this.tlsConfiguration = Optional.ofNullable(tlsConfiguration);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponseType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcUnauthorizedResponse;
import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.nat.upnp.UpnpNatManager;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
import org.hyperledger.besu.util.ExceptionUtils;
import org.hyperledger.besu.util.NetworkUtility;

import java.net.InetSocketAddress;
Expand All @@ -56,6 +58,8 @@
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.VertxException;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
Expand Down Expand Up @@ -165,12 +169,7 @@ public CompletableFuture<?> start() {
LOG.info("Starting JsonRPC service on {}:{}", config.getHost(), config.getPort());

// Create the HTTP server and a router object.
httpServer =
vertx.createHttpServer(
new HttpServerOptions()
.setHost(config.getHost())
.setPort(config.getPort())
.setHandle100ContinueAutomatically(true));
httpServer = vertx.createHttpServer(getHttpServerOptions());
usmansaleem marked this conversation as resolved.
Show resolved Hide resolved

// Handle json rpc requests
final Router router = Router.router(vertx);
Expand Down Expand Up @@ -220,41 +219,85 @@ public CompletableFuture<?> start() {
}

final CompletableFuture<?> resultFuture = new CompletableFuture<>();
httpServer
.requestHandler(router)
.listen(
res -> {
if (!res.failed()) {
resultFuture.complete(null);
final int actualPort = httpServer.actualPort();
LOG.info(
"JsonRPC service started and listening on {}:{}", config.getHost(), actualPort);
config.setPort(actualPort);
// Request that a NAT port forward for our server port
if (natManager.isPresent()) {
natManager
.get()
.requestPortForward(
config.getPort(), UpnpNatManager.Protocol.TCP, "besu-json-rpc");
try {
httpServer
.requestHandler(router)
.listen(
res -> {
if (!res.failed()) {
resultFuture.complete(null);
final int actualPort = httpServer.actualPort();
LOG.info(
"JsonRPC service started and listening on {}:{}{}",
config.getHost(),
actualPort,
tlsLogMessage());
config.setPort(actualPort);
// Request that a NAT port forward for our server port
if (natManager.isPresent()) {
natManager
.get()
.requestPortForward(
config.getPort(), UpnpNatManager.Protocol.TCP, "besu-json-rpc");
}
return;
}
return;
}
httpServer = null;
final Throwable cause = res.cause();
if (cause instanceof SocketException) {
resultFuture.completeExceptionally(
new JsonRpcServiceException(
String.format(
"Failed to bind Ethereum JSON RPC listener to %s:%s: %s",
config.getHost(), config.getPort(), cause.getMessage())));
return;
}
resultFuture.completeExceptionally(cause);
});
httpServer = null;
final Throwable cause = res.cause();
if (cause instanceof SocketException) {
resultFuture.completeExceptionally(
new JsonRpcServiceException(
String.format(
"Failed to bind Ethereum JSON RPC listener to %s:%s: %s",
config.getHost(), config.getPort(), cause.getMessage())));
return;
}
resultFuture.completeExceptionally(cause);
});
} catch (VertxException e) {
httpServer = null;
resultFuture.completeExceptionally(
new JsonRpcServiceException(
String.format(
"Ethereum JSON RPC listener failed to start: %s",
ExceptionUtils.rootCause(e).getMessage())));
}

return resultFuture;
}

private HttpServerOptions getHttpServerOptions() {
final HttpServerOptions httpServerOptions =
new HttpServerOptions()
.setHost(config.getHost())
.setPort(config.getPort())
.setHandle100ContinueAutomatically(true);

return applyTlsConfig(httpServerOptions);
}

private HttpServerOptions applyTlsConfig(final HttpServerOptions origHttpServerOptions) {
if (config.getTlsConfiguration().isEmpty()) {
return origHttpServerOptions;
}

final HttpServerOptions httpServerOptions = new HttpServerOptions(origHttpServerOptions);
final TlsConfiguration tlsConfiguration = config.getTlsConfiguration().get();
httpServerOptions.setSsl(true).setPfxKeyCertOptions(tlsConfiguration.getPfxKeyCertOptions());

if (tlsConfiguration.getTrustOptions().isPresent()) {
httpServerOptions
.setClientAuth(ClientAuth.REQUIRED)
.setTrustOptions(tlsConfiguration.getTrustOptions().get());
}

return httpServerOptions;
}

private String tlsLogMessage() {
return config.getTlsConfiguration().isPresent() ? " with TLS enabled." : "";
}

private Handler<RoutingContext> checkWhitelistHostHeader() {
return event -> {
final Optional<String> hostHeader = getAndValidateHostHeader(event);
Expand Down Expand Up @@ -323,7 +366,11 @@ public String url() {
if (httpServer == null) {
return "";
}
return NetworkUtility.urlForSocketAddress("http", socketAddress());
return NetworkUtility.urlForSocketAddress(getScheme(), socketAddress());
}

private String getScheme() {
return config.getTlsConfiguration().isPresent() ? "https" : "http";
}

private void handleJsonRPCRequest(final RoutingContext routingContext) {
Expand Down
Loading