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

Add TLS/mTLS options and configure the GraphQL HTTP service #7910

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786)
- Add a method to get all the transaction in the pool, to the `TransactionPoolService`, to easily access the transaction pool content from plugins [#7813](https://github.com/hyperledger/besu/pull/7813)
- Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832)
- Add TLS/mTLS options and configure the GraphQL HTTP service[#7910](https://github.com/hyperledger/besu/pull/7910)

### Bug fixes
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,36 @@ public class GraphQlOptions {
private final CorsAllowedOriginsProperty graphQLHttpCorsAllowedOrigins =
new CorsAllowedOriginsProperty();

@CommandLine.Option(
names = {"--graphql-tls-enabled"},
description = "Enable TLS for GraphQL HTTP service")
private Boolean graphqlTlsEnabled = false;

@CommandLine.Option(
names = {"--graphql-tls-keystore-file"},
description = "Path to the TLS keystore file for GraphQL HTTP service")
private String graphqlTlsKeystoreFile;

@CommandLine.Option(
names = {"--graphql-tls-keystore-password-file"},
description = "Path to the file containing the password for the TLS keystore")
private String graphqlTlsKeystorePasswordFile;

@CommandLine.Option(
names = {"--graphql-mtls-enabled"},
description = "Enable mTLS for GraphQL HTTP service")
private Boolean graphqlMtlsEnabled = false;

@CommandLine.Option(
names = {"--graphql-tls-truststore-file"},
description = "Path to the TLS truststore file for GraphQL HTTP service")
private String graphqlTlsTruststoreFile;

@CommandLine.Option(
names = {"--graphql-tls-truststore-password-file"},
description = "Path to the file containing the password for the TLS truststore")
private String graphqlTlsTruststorePasswordFile;

/** Default constructor */
public GraphQlOptions() {}

Expand All @@ -72,7 +102,28 @@ public void validate(final Logger logger, final CommandLine commandLine) {
commandLine,
"--graphql-http-enabled",
!isGraphQLHttpEnabled,
asList("--graphql-http-cors-origins", "--graphql-http-host", "--graphql-http-port"));
asList(
"--graphql-http-cors-origins",
"--graphql-http-host",
"--graphql-http-port",
"--graphql-tls-enabled"));

CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--graphql-tls-enabled",
!graphqlTlsEnabled,
asList(
"--graphql-tls-keystore-file",
"--graphql-tls-keystore-password-file",
"--graphql-mtls-enabled"));

CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--graphql-mtls-enabled",
!graphqlMtlsEnabled,
asList("--graphql-tls-truststore-file", "--graphql-tls-truststore-password-file"));
}

/**
Expand All @@ -93,6 +144,13 @@ public GraphQLConfiguration graphQLConfiguration(
graphQLConfiguration.setHostsAllowlist(hostsAllowlist);
graphQLConfiguration.setCorsAllowedDomains(graphQLHttpCorsAllowedOrigins);
graphQLConfiguration.setHttpTimeoutSec(timoutSec);
graphQLConfiguration.setTlsEnabled(graphqlTlsEnabled);
graphQLConfiguration.setTlsKeyStorePath(graphqlTlsKeystoreFile);
graphQLConfiguration.setTlsKeyStorePasswordFile(graphqlTlsKeystorePasswordFile);
graphQLConfiguration.setMtlsEnabled(graphqlMtlsEnabled);
graphQLConfiguration.setTlsTrustStorePath(graphqlTlsTruststoreFile);
graphQLConfiguration.setTlsTrustStorePasswordFile(graphqlTlsTruststorePasswordFile);

return graphQLConfiguration;
}

Expand Down
6 changes: 6 additions & 0 deletions besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ graphql-http-enabled=false
graphql-http-host="6.7.8.9"
graphql-http-port=6789
graphql-http-cors-origins=["none"]
graphql-tls-enabled=false
graphql-tls-keystore-file="none.pfx"
graphql-tls-keystore-password-file="none.passwd"
graphql-mtls-enabled=false
graphql-tls-truststore-file="none.pfx"
graphql-tls-truststore-password-file="none.passwd"

# WebSockets API
rpc-ws-enabled=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -44,6 +47,13 @@ public class GraphQLConfiguration {
private List<String> hostsAllowlist = Arrays.asList("localhost", DEFAULT_GRAPHQL_HTTP_HOST);
private long httpTimeoutSec = TimeoutOptions.defaultOptions().getTimeoutSeconds();

private String tlsKeyStorePath;
private String tlsKeyStorePasswordFile;
private String tlsTrustStorePath;
private String tlsTrustStorePasswordFile;
private boolean tlsEnabled;
private boolean mtlsEnabled;

/**
* Creates a default configuration for GraphQL.
*
Expand Down Expand Up @@ -174,6 +184,58 @@ public void setHttpTimeoutSec(final long httpTimeoutSec) {
this.httpTimeoutSec = httpTimeoutSec;
}

public String getTlsKeyStorePath() {
return tlsKeyStorePath;
}

public void setTlsKeyStorePath(final String tlsKeyStorePath) {
this.tlsKeyStorePath = tlsKeyStorePath;
}

public String getTlsKeyStorePassword() throws Exception {
return new String(
Files.readAllBytes(Paths.get(tlsKeyStorePasswordFile)), Charset.defaultCharset())
.trim();
}

public void setTlsKeyStorePasswordFile(final String tlsKeyStorePasswordFile) {
this.tlsKeyStorePasswordFile = tlsKeyStorePasswordFile;
}

public String getTlsTrustStorePath() {
return tlsTrustStorePath;
}

public void setTlsTrustStorePath(final String tlsTrustStorePath) {
this.tlsTrustStorePath = tlsTrustStorePath;
}

public String getTlsTrustStorePassword() throws Exception {
return new String(
Files.readAllBytes(Paths.get(tlsTrustStorePasswordFile)), Charset.defaultCharset())
.trim();
}

public void setTlsTrustStorePasswordFile(final String tlsTrustStorePasswordFile) {
this.tlsTrustStorePasswordFile = tlsTrustStorePasswordFile;
}

public boolean isTlsEnabled() {
return tlsEnabled;
}

public void setTlsEnabled(final boolean tlsEnabled) {
this.tlsEnabled = tlsEnabled;
}

public boolean isMtlsEnabled() {
return mtlsEnabled;
}

public void setMtlsEnabled(final boolean mtlsEnabled) {
this.mtlsEnabled = mtlsEnabled;
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
Expand All @@ -60,6 +61,7 @@
import io.vertx.core.json.Json;
import io.vertx.core.json.jackson.JacksonCodec;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
Expand Down Expand Up @@ -147,13 +149,43 @@ private void validateConfig(final GraphQLConfiguration config) {
public CompletableFuture<?> start() {
LOG.info("Starting GraphQL HTTP 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)
.setCompressionSupported(true));
HttpServerOptions options =
new HttpServerOptions()
.setHost(config.getHost())
.setPort(config.getPort())
.setHandle100ContinueAutomatically(true)
.setCompressionSupported(true);

if (config.isTlsEnabled()) {
try {
options
.setSsl(true)
.setKeyCertOptions(
new JksOptions()
.setPath(config.getTlsKeyStorePath())
.setPassword(config.getTlsKeyStorePassword()));
} catch (Exception e) {
LOG.error("Failed to get TLS keystore password", e);
return CompletableFuture.failedFuture(e);
}

if (config.isMtlsEnabled()) {
try {
options
.setTrustOptions(
new JksOptions()
.setPath(config.getTlsTrustStorePath())
.setPassword(config.getTlsTrustStorePassword()))
.setClientAuth(ClientAuth.REQUIRED);
} catch (Exception e) {
LOG.error("Failed to get TLS truststore password", e);
return CompletableFuture.failedFuture(e);
}
}
}

LOG.info("Options {}", options);
httpServer = vertx.createHttpServer(options);

// Handle graphql http requests
final Router router = Router.router(vertx);
Expand Down Expand Up @@ -303,7 +335,8 @@ public String url() {
if (httpServer == null) {
return "";
}
return NetworkUtility.urlForSocketAddress("http", socketAddress());
String scheme = config.isTlsEnabled() ? "https" : "http";
return NetworkUtility.urlForSocketAddress(scheme, socketAddress());
}

// Empty Get/Post requests to / will be redirected to /graphql using 308 Permanent Redirect
Expand Down
Loading