Skip to content

Commit

Permalink
Add TLS/mTLS options for websockets (#7854)
Browse files Browse the repository at this point in the history
* Fix incorrect duration for THREE_MINUTES from 1 minute to 3 minutes

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Add options to enable TLS/mTLS for websocket connections

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Revert an irrelevant change

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Update tests, options and option dependencies

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Fix CHANGELOG entry

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Fix CHANGELOG entry

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

---------

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>
Co-authored-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>
Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net>
  • Loading branch information
3 people authored Nov 8, 2024
1 parent 3ebf50e commit e17ebc5
Show file tree
Hide file tree
Showing 6 changed files with 764 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Additions and Improvements
- Fine tune already seen txs tracker when a tx is removed from the pool [#7755](https://github.com/hyperledger/besu/pull/7755)
- Support for enabling and configuring TLS/mTLS in WebSocket service. [#7854](https://github.com/hyperledger/besu/pull/7854)
- Create and publish Besu BOM (Bill of Materials) [#7615](https://github.com/hyperledger/besu/pull/7615)
- 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,71 @@ public class RpcWebsocketOptions {
arity = "1")
private final File rpcWsAuthenticationPublicKeyFile = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-enabled"},
description = "Enable SSL/TLS for the WebSocket RPC service")
private final Boolean isRpcWsSslEnabled = false;

@CommandLine.Option(
names = {"--rpc-ws-ssl-keystore-file"},
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
description = "Path to the keystore file for the WebSocket RPC service")
private String rpcWsKeyStoreFile = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-keystore-password"},
paramLabel = "<PASSWORD>",
description = "Password for the WebSocket RPC keystore file")
private String rpcWsKeyStorePassword = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-key-file"},
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
description = "Path to the PEM key file for the WebSocket RPC service")
private String rpcWsKeyFile = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-cert-file"},
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
description = "Path to the PEM cert file for the WebSocket RPC service")
private String rpcWsCertFile = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-keystore-type"},
paramLabel = "<TYPE>",
description = "Type of the WebSocket RPC keystore (JKS, PKCS12, PEM)")
private String rpcWsKeyStoreType = null;

// For client authentication (mTLS)
@CommandLine.Option(
names = {"--rpc-ws-ssl-client-auth-enabled"},
description = "Enable client authentication for the WebSocket RPC service")
private final Boolean isRpcWsClientAuthEnabled = false;

@CommandLine.Option(
names = {"--rpc-ws-ssl-truststore-file"},
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
description = "Path to the truststore file for the WebSocket RPC service")
private String rpcWsTrustStoreFile = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-truststore-password"},
paramLabel = "<PASSWORD>",
description = "Password for the WebSocket RPC truststore file")
private String rpcWsTrustStorePassword = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-trustcert-file"},
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
description = "Path to the PEM trustcert file for the WebSocket RPC service")
private String rpcWsTrustCertFile = null;

@CommandLine.Option(
names = {"--rpc-ws-ssl-truststore-type"},
paramLabel = "<TYPE>",
description = "Type of the truststore (JKS, PKCS12, PEM)")
private String rpcWsTrustStoreType = null;

/** Default Constructor. */
public RpcWebsocketOptions() {}

Expand Down Expand Up @@ -184,7 +249,61 @@ private void checkOptionDependencies(final Logger logger, final CommandLine comm
"--rpc-ws-authentication-enabled",
"--rpc-ws-authentication-credentials-file",
"--rpc-ws-authentication-public-key-file",
"--rpc-ws-authentication-jwt-algorithm"));
"--rpc-ws-authentication-jwt-algorithm",
"--rpc-ws-ssl-enabled"));

CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-ssl-enabled",
!isRpcWsSslEnabled,
List.of(
"--rpc-ws-ssl-keystore-file",
"--rpc-ws-ssl-keystore-type",
"--rpc-ws-ssl-client-auth-enabled"));

CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-ssl-client-auth-enabled",
!isRpcWsClientAuthEnabled,
List.of(
"--rpc-ws-ssl-truststore-file",
"--rpc-ws-ssl-truststore-type",
"--rpc-ws-ssl-trustcert-file"));

if (isRpcWsSslEnabled) {
if ("PEM".equalsIgnoreCase(rpcWsKeyStoreType)) {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-ssl-key-file",
rpcWsKeyFile == null,
List.of("--rpc-ws-ssl-cert-file"));
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-ssl-cert-file",
rpcWsCertFile == null,
List.of("--rpc-ws-ssl-key-file"));
} else {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-ssl-keystore-file",
rpcWsKeyStoreFile == null,
List.of("--rpc-ws-ssl-keystore-password"));
}
}

if (isRpcWsClientAuthEnabled && !"PEM".equalsIgnoreCase(rpcWsTrustStoreType)) {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-ssl-truststore-file",
rpcWsTrustStoreFile == null,
List.of("--rpc-ws-ssl-truststore-password"));
}

if (isRpcWsAuthenticationEnabled) {
CommandLineUtils.checkOptionDependencies(
Expand Down Expand Up @@ -222,6 +341,18 @@ public WebSocketConfiguration webSocketConfiguration(
webSocketConfiguration.setAuthenticationPublicKeyFile(rpcWsAuthenticationPublicKeyFile);
webSocketConfiguration.setAuthenticationAlgorithm(rpcWebsocketsAuthenticationAlgorithm);
webSocketConfiguration.setTimeoutSec(wsTimoutSec);
webSocketConfiguration.setSslEnabled(isRpcWsSslEnabled);
webSocketConfiguration.setKeyStorePath(rpcWsKeyStoreFile);
webSocketConfiguration.setKeyStorePassword(rpcWsKeyStorePassword);
webSocketConfiguration.setKeyStoreType(rpcWsKeyStoreType);
webSocketConfiguration.setClientAuthEnabled(isRpcWsClientAuthEnabled);
webSocketConfiguration.setTrustStorePath(rpcWsTrustStoreFile);
webSocketConfiguration.setTrustStorePassword(rpcWsTrustStorePassword);
webSocketConfiguration.setTrustStoreType(rpcWsTrustStoreType);
webSocketConfiguration.setKeyPath(rpcWsKeyFile);
webSocketConfiguration.setCertPath(rpcWsCertFile);
webSocketConfiguration.setTrustCertPath(rpcWsTrustCertFile);

return webSocketConfiguration;
}

Expand Down
13 changes: 13 additions & 0 deletions besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ rpc-ws-max-frame-size=65535
rpc-ws-authentication-enabled=false
rpc-ws-authentication-credentials-file="none"
rpc-ws-authentication-jwt-public-key-file="none"
rpc-ws-ssl-enabled=false
rpc-ws-ssl-keystore-file="none.pfx"
rpc-ws-ssl-keystore-password="none.passwd"
rpc-ws-ssl-keystore-type="none"
rpc-ws-ssl-client-auth-enabled=false
rpc-ws-ssl-truststore-file="none.pfx"
rpc-ws-ssl-truststore-password="none.passwd"
rpc-ws-ssl-truststore-type="none"
rpc-ws-ssl-key-file="none.pfx"
rpc-ws-ssl-cert-file="none.pfx"
rpc-ws-ssl-trustcert-file="none.pfx"



# API
api-gas-price-blocks=100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import com.google.common.base.MoreObjects;

Expand All @@ -49,6 +50,21 @@ public class WebSocketConfiguration {
private int maxActiveConnections;
private int maxFrameSize;

private boolean isSslEnabled = false;
private Optional<String> keyStorePath = Optional.empty();
private Optional<String> keyStorePassword = Optional.empty();
private Optional<String> keyStoreType = Optional.of("JKS"); // Default to JKS

private boolean clientAuthEnabled = false;
private Optional<String> trustStorePath = Optional.empty();
private Optional<String> trustStorePassword = Optional.empty();
private Optional<String> trustStoreType = Optional.of("JKS"); // Default to JKS

// For PEM format
private Optional<String> keyPath = Optional.empty();
private Optional<String> certPath = Optional.empty();
private Optional<String> trustCertPath = Optional.empty();

public static WebSocketConfiguration createDefault() {
final WebSocketConfiguration config = new WebSocketConfiguration();
config.setEnabled(false);
Expand Down Expand Up @@ -159,6 +175,102 @@ public void setTimeoutSec(final long timeoutSec) {
this.timeoutSec = timeoutSec;
}

public boolean isSslEnabled() {
return isSslEnabled;
}

public void setSslEnabled(final boolean isSslEnabled) {
this.isSslEnabled = isSslEnabled;
}

public Optional<String> getKeyStorePath() {
return keyStorePath;
}

public void setKeyStorePath(final String keyStorePath) {
this.keyStorePath = Optional.ofNullable(keyStorePath);
}

public Optional<String> getKeyStorePassword() {
return keyStorePassword;
}

public void setKeyStorePassword(final String keyStorePassword) {
this.keyStorePassword = Optional.ofNullable(keyStorePassword);
}

// Keystore Type
public Optional<String> getKeyStoreType() {
return keyStoreType;
}

public void setKeyStoreType(final String keyStoreType) {
this.keyStoreType = Optional.ofNullable(keyStoreType);
}

// Key Path (for PEM)
public Optional<String> getKeyPath() {
return keyPath;
}

public void setKeyPath(final String keyPath) {
this.keyPath = Optional.ofNullable(keyPath);
}

// Cert Path (for PEM)
public Optional<String> getCertPath() {
return certPath;
}

public void setCertPath(final String certPath) {
this.certPath = Optional.ofNullable(certPath);
}

// Client Authentication Enabled
public boolean isClientAuthEnabled() {
return clientAuthEnabled;
}

public void setClientAuthEnabled(final boolean clientAuthEnabled) {
this.clientAuthEnabled = clientAuthEnabled;
}

// Truststore Path
public Optional<String> getTrustStorePath() {
return trustStorePath;
}

public void setTrustStorePath(final String trustStorePath) {
this.trustStorePath = Optional.ofNullable(trustStorePath);
}

// Truststore Password
public Optional<String> getTrustStorePassword() {
return trustStorePassword;
}

public void setTrustStorePassword(final String trustStorePassword) {
this.trustStorePassword = Optional.ofNullable(trustStorePassword);
}

// Truststore Type
public Optional<String> getTrustStoreType() {
return trustStoreType;
}

public void setTrustStoreType(final String trustStoreType) {
this.trustStoreType = Optional.ofNullable(trustStoreType);
}

// Trust Cert Path (for PEM)
public Optional<String> getTrustCertPath() {
return trustCertPath;
}

public void setTrustCertPath(final String trustCertPath) {
this.trustCertPath = Optional.ofNullable(trustCertPath);
}

@Override
public boolean equals(final Object o) {
if (this == o) {
Expand Down
Loading

0 comments on commit e17ebc5

Please sign in to comment.