Skip to content

Commit e17ebc5

Browse files
pulluribBhanu Pullurifab-10
authored
Add TLS/mTLS options for websockets (#7854)
* 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>
1 parent 3ebf50e commit e17ebc5

File tree

6 files changed

+764
-11
lines changed

6 files changed

+764
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
### Additions and Improvements
1212
- Fine tune already seen txs tracker when a tx is removed from the pool [#7755](https://github.com/hyperledger/besu/pull/7755)
13+
- Support for enabling and configuring TLS/mTLS in WebSocket service. [#7854](https://github.com/hyperledger/besu/pull/7854)
1314
- Create and publish Besu BOM (Bill of Materials) [#7615](https://github.com/hyperledger/besu/pull/7615)
1415
- Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786)
1516
- 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)

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

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,71 @@ public class RpcWebsocketOptions {
120120
arity = "1")
121121
private final File rpcWsAuthenticationPublicKeyFile = null;
122122

123+
@CommandLine.Option(
124+
names = {"--rpc-ws-ssl-enabled"},
125+
description = "Enable SSL/TLS for the WebSocket RPC service")
126+
private final Boolean isRpcWsSslEnabled = false;
127+
128+
@CommandLine.Option(
129+
names = {"--rpc-ws-ssl-keystore-file"},
130+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
131+
description = "Path to the keystore file for the WebSocket RPC service")
132+
private String rpcWsKeyStoreFile = null;
133+
134+
@CommandLine.Option(
135+
names = {"--rpc-ws-ssl-keystore-password"},
136+
paramLabel = "<PASSWORD>",
137+
description = "Password for the WebSocket RPC keystore file")
138+
private String rpcWsKeyStorePassword = null;
139+
140+
@CommandLine.Option(
141+
names = {"--rpc-ws-ssl-key-file"},
142+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
143+
description = "Path to the PEM key file for the WebSocket RPC service")
144+
private String rpcWsKeyFile = null;
145+
146+
@CommandLine.Option(
147+
names = {"--rpc-ws-ssl-cert-file"},
148+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
149+
description = "Path to the PEM cert file for the WebSocket RPC service")
150+
private String rpcWsCertFile = null;
151+
152+
@CommandLine.Option(
153+
names = {"--rpc-ws-ssl-keystore-type"},
154+
paramLabel = "<TYPE>",
155+
description = "Type of the WebSocket RPC keystore (JKS, PKCS12, PEM)")
156+
private String rpcWsKeyStoreType = null;
157+
158+
// For client authentication (mTLS)
159+
@CommandLine.Option(
160+
names = {"--rpc-ws-ssl-client-auth-enabled"},
161+
description = "Enable client authentication for the WebSocket RPC service")
162+
private final Boolean isRpcWsClientAuthEnabled = false;
163+
164+
@CommandLine.Option(
165+
names = {"--rpc-ws-ssl-truststore-file"},
166+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
167+
description = "Path to the truststore file for the WebSocket RPC service")
168+
private String rpcWsTrustStoreFile = null;
169+
170+
@CommandLine.Option(
171+
names = {"--rpc-ws-ssl-truststore-password"},
172+
paramLabel = "<PASSWORD>",
173+
description = "Password for the WebSocket RPC truststore file")
174+
private String rpcWsTrustStorePassword = null;
175+
176+
@CommandLine.Option(
177+
names = {"--rpc-ws-ssl-trustcert-file"},
178+
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
179+
description = "Path to the PEM trustcert file for the WebSocket RPC service")
180+
private String rpcWsTrustCertFile = null;
181+
182+
@CommandLine.Option(
183+
names = {"--rpc-ws-ssl-truststore-type"},
184+
paramLabel = "<TYPE>",
185+
description = "Type of the truststore (JKS, PKCS12, PEM)")
186+
private String rpcWsTrustStoreType = null;
187+
123188
/** Default Constructor. */
124189
public RpcWebsocketOptions() {}
125190

@@ -184,7 +249,61 @@ private void checkOptionDependencies(final Logger logger, final CommandLine comm
184249
"--rpc-ws-authentication-enabled",
185250
"--rpc-ws-authentication-credentials-file",
186251
"--rpc-ws-authentication-public-key-file",
187-
"--rpc-ws-authentication-jwt-algorithm"));
252+
"--rpc-ws-authentication-jwt-algorithm",
253+
"--rpc-ws-ssl-enabled"));
254+
255+
CommandLineUtils.checkOptionDependencies(
256+
logger,
257+
commandLine,
258+
"--rpc-ws-ssl-enabled",
259+
!isRpcWsSslEnabled,
260+
List.of(
261+
"--rpc-ws-ssl-keystore-file",
262+
"--rpc-ws-ssl-keystore-type",
263+
"--rpc-ws-ssl-client-auth-enabled"));
264+
265+
CommandLineUtils.checkOptionDependencies(
266+
logger,
267+
commandLine,
268+
"--rpc-ws-ssl-client-auth-enabled",
269+
!isRpcWsClientAuthEnabled,
270+
List.of(
271+
"--rpc-ws-ssl-truststore-file",
272+
"--rpc-ws-ssl-truststore-type",
273+
"--rpc-ws-ssl-trustcert-file"));
274+
275+
if (isRpcWsSslEnabled) {
276+
if ("PEM".equalsIgnoreCase(rpcWsKeyStoreType)) {
277+
CommandLineUtils.checkOptionDependencies(
278+
logger,
279+
commandLine,
280+
"--rpc-ws-ssl-key-file",
281+
rpcWsKeyFile == null,
282+
List.of("--rpc-ws-ssl-cert-file"));
283+
CommandLineUtils.checkOptionDependencies(
284+
logger,
285+
commandLine,
286+
"--rpc-ws-ssl-cert-file",
287+
rpcWsCertFile == null,
288+
List.of("--rpc-ws-ssl-key-file"));
289+
} else {
290+
CommandLineUtils.checkOptionDependencies(
291+
logger,
292+
commandLine,
293+
"--rpc-ws-ssl-keystore-file",
294+
rpcWsKeyStoreFile == null,
295+
List.of("--rpc-ws-ssl-keystore-password"));
296+
}
297+
}
298+
299+
if (isRpcWsClientAuthEnabled && !"PEM".equalsIgnoreCase(rpcWsTrustStoreType)) {
300+
CommandLineUtils.checkOptionDependencies(
301+
logger,
302+
commandLine,
303+
"--rpc-ws-ssl-truststore-file",
304+
rpcWsTrustStoreFile == null,
305+
List.of("--rpc-ws-ssl-truststore-password"));
306+
}
188307

189308
if (isRpcWsAuthenticationEnabled) {
190309
CommandLineUtils.checkOptionDependencies(
@@ -222,6 +341,18 @@ public WebSocketConfiguration webSocketConfiguration(
222341
webSocketConfiguration.setAuthenticationPublicKeyFile(rpcWsAuthenticationPublicKeyFile);
223342
webSocketConfiguration.setAuthenticationAlgorithm(rpcWebsocketsAuthenticationAlgorithm);
224343
webSocketConfiguration.setTimeoutSec(wsTimoutSec);
344+
webSocketConfiguration.setSslEnabled(isRpcWsSslEnabled);
345+
webSocketConfiguration.setKeyStorePath(rpcWsKeyStoreFile);
346+
webSocketConfiguration.setKeyStorePassword(rpcWsKeyStorePassword);
347+
webSocketConfiguration.setKeyStoreType(rpcWsKeyStoreType);
348+
webSocketConfiguration.setClientAuthEnabled(isRpcWsClientAuthEnabled);
349+
webSocketConfiguration.setTrustStorePath(rpcWsTrustStoreFile);
350+
webSocketConfiguration.setTrustStorePassword(rpcWsTrustStorePassword);
351+
webSocketConfiguration.setTrustStoreType(rpcWsTrustStoreType);
352+
webSocketConfiguration.setKeyPath(rpcWsKeyFile);
353+
webSocketConfiguration.setCertPath(rpcWsCertFile);
354+
webSocketConfiguration.setTrustCertPath(rpcWsTrustCertFile);
355+
225356
return webSocketConfiguration;
226357
}
227358

besu/src/test/resources/everything_config.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,19 @@ rpc-ws-max-frame-size=65535
120120
rpc-ws-authentication-enabled=false
121121
rpc-ws-authentication-credentials-file="none"
122122
rpc-ws-authentication-jwt-public-key-file="none"
123+
rpc-ws-ssl-enabled=false
124+
rpc-ws-ssl-keystore-file="none.pfx"
125+
rpc-ws-ssl-keystore-password="none.passwd"
126+
rpc-ws-ssl-keystore-type="none"
127+
rpc-ws-ssl-client-auth-enabled=false
128+
rpc-ws-ssl-truststore-file="none.pfx"
129+
rpc-ws-ssl-truststore-password="none.passwd"
130+
rpc-ws-ssl-truststore-type="none"
131+
rpc-ws-ssl-key-file="none.pfx"
132+
rpc-ws-ssl-cert-file="none.pfx"
133+
rpc-ws-ssl-trustcert-file="none.pfx"
134+
135+
123136

124137
# API
125138
api-gas-price-blocks=100

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketConfiguration.java

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Collections;
2626
import java.util.List;
2727
import java.util.Objects;
28+
import java.util.Optional;
2829

2930
import com.google.common.base.MoreObjects;
3031

@@ -49,6 +50,21 @@ public class WebSocketConfiguration {
4950
private int maxActiveConnections;
5051
private int maxFrameSize;
5152

53+
private boolean isSslEnabled = false;
54+
private Optional<String> keyStorePath = Optional.empty();
55+
private Optional<String> keyStorePassword = Optional.empty();
56+
private Optional<String> keyStoreType = Optional.of("JKS"); // Default to JKS
57+
58+
private boolean clientAuthEnabled = false;
59+
private Optional<String> trustStorePath = Optional.empty();
60+
private Optional<String> trustStorePassword = Optional.empty();
61+
private Optional<String> trustStoreType = Optional.of("JKS"); // Default to JKS
62+
63+
// For PEM format
64+
private Optional<String> keyPath = Optional.empty();
65+
private Optional<String> certPath = Optional.empty();
66+
private Optional<String> trustCertPath = Optional.empty();
67+
5268
public static WebSocketConfiguration createDefault() {
5369
final WebSocketConfiguration config = new WebSocketConfiguration();
5470
config.setEnabled(false);
@@ -159,6 +175,102 @@ public void setTimeoutSec(final long timeoutSec) {
159175
this.timeoutSec = timeoutSec;
160176
}
161177

178+
public boolean isSslEnabled() {
179+
return isSslEnabled;
180+
}
181+
182+
public void setSslEnabled(final boolean isSslEnabled) {
183+
this.isSslEnabled = isSslEnabled;
184+
}
185+
186+
public Optional<String> getKeyStorePath() {
187+
return keyStorePath;
188+
}
189+
190+
public void setKeyStorePath(final String keyStorePath) {
191+
this.keyStorePath = Optional.ofNullable(keyStorePath);
192+
}
193+
194+
public Optional<String> getKeyStorePassword() {
195+
return keyStorePassword;
196+
}
197+
198+
public void setKeyStorePassword(final String keyStorePassword) {
199+
this.keyStorePassword = Optional.ofNullable(keyStorePassword);
200+
}
201+
202+
// Keystore Type
203+
public Optional<String> getKeyStoreType() {
204+
return keyStoreType;
205+
}
206+
207+
public void setKeyStoreType(final String keyStoreType) {
208+
this.keyStoreType = Optional.ofNullable(keyStoreType);
209+
}
210+
211+
// Key Path (for PEM)
212+
public Optional<String> getKeyPath() {
213+
return keyPath;
214+
}
215+
216+
public void setKeyPath(final String keyPath) {
217+
this.keyPath = Optional.ofNullable(keyPath);
218+
}
219+
220+
// Cert Path (for PEM)
221+
public Optional<String> getCertPath() {
222+
return certPath;
223+
}
224+
225+
public void setCertPath(final String certPath) {
226+
this.certPath = Optional.ofNullable(certPath);
227+
}
228+
229+
// Client Authentication Enabled
230+
public boolean isClientAuthEnabled() {
231+
return clientAuthEnabled;
232+
}
233+
234+
public void setClientAuthEnabled(final boolean clientAuthEnabled) {
235+
this.clientAuthEnabled = clientAuthEnabled;
236+
}
237+
238+
// Truststore Path
239+
public Optional<String> getTrustStorePath() {
240+
return trustStorePath;
241+
}
242+
243+
public void setTrustStorePath(final String trustStorePath) {
244+
this.trustStorePath = Optional.ofNullable(trustStorePath);
245+
}
246+
247+
// Truststore Password
248+
public Optional<String> getTrustStorePassword() {
249+
return trustStorePassword;
250+
}
251+
252+
public void setTrustStorePassword(final String trustStorePassword) {
253+
this.trustStorePassword = Optional.ofNullable(trustStorePassword);
254+
}
255+
256+
// Truststore Type
257+
public Optional<String> getTrustStoreType() {
258+
return trustStoreType;
259+
}
260+
261+
public void setTrustStoreType(final String trustStoreType) {
262+
this.trustStoreType = Optional.ofNullable(trustStoreType);
263+
}
264+
265+
// Trust Cert Path (for PEM)
266+
public Optional<String> getTrustCertPath() {
267+
return trustCertPath;
268+
}
269+
270+
public void setTrustCertPath(final String trustCertPath) {
271+
this.trustCertPath = Optional.ofNullable(trustCertPath);
272+
}
273+
162274
@Override
163275
public boolean equals(final Object o) {
164276
if (this == o) {

0 commit comments

Comments
 (0)