Skip to content

Commit cc5ff64

Browse files
committed
Changes done for v1.0.8
1 parent ff361a3 commit cc5ff64

File tree

12 files changed

+215
-18
lines changed

12 files changed

+215
-18
lines changed

com.ibm.streamsx.websocket/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
Changes
22
=======
3+
## v1.0.8:
4+
* Sep/22/2020
5+
* Added logic to stop the Boost ASIO run loop on operator shutdown in order for an immediate closure of the TLS and non-TLS server ports in the Source and Sink operators.
6+
* Added an optional tlsCipherWhitelist parameter in the Source and Sink operators for the user to specify approved TLS/SSL ciphers thereby avoiding the use of any security vulnerable ciphers.
7+
* Added logic in the HttpPost operator to emit an output tuple even when the tuple processing logic encounters an exception.
8+
39
## v1.0.7:
410
* Sep/01/2020
511
* Made the WebSocketSource operator friendlier to browser-based client applications so that they can do HTTP GET for fetching files such as html, css, js, png, gif, favicon etc.

com.ibm.streamsx.websocket/com.ibm.streamsx.websocket.op/WebSocketSink/WebSocketSink.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,16 @@
313313
<type>uint32</type>
314314
<cardinality>1</cardinality>
315315
</parameter>
316+
317+
<parameter>
318+
<name>tlsCipherWhitelist</name>
319+
<description>This parameter can be used to specify a string containing one or more comma separated TLS/SSL ciphers that should be used during TLS/SSL connection negotiations with clients. It is handy when there is a need to avoid using ciphers that are found to have security vulnerabilities. (Default is an empty string.)</description>
320+
<optional>true</optional>
321+
<rewriteAllowed>true</rewriteAllowed>
322+
<expressionMode>AttributeFree</expressionMode>
323+
<type>rstring</type>
324+
<cardinality>1</cardinality>
325+
</parameter>
316326
</parameters>
317327

318328
<inputPorts>

com.ibm.streamsx.websocket/com.ibm.streamsx.websocket.op/WebSocketSink/WebSocketSink_cpp.cgt

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/*
99
============================================================
1010
First created on: Apr/28/2020
11-
Last modified on: Aug/31/2020
11+
Last modified on: Sep/22/2020
1212

1313
This particular operator (WebSocketSink) is used to
1414
send text (plain text, JSON, XML) and/or binary data to
@@ -55,6 +55,9 @@
5555
#include <boost/exception/to_string.hpp>
5656
#include <boost/thread/thread.hpp>
5757

58+
// Necessary headers for setting user specified TLS ciphers.
59+
#include <openssl/ssl.h>
60+
5861
// For the core logic below, we will need these namespaces from websocketpp.
5962
using websocketpp::lib::placeholders::_1;
6063
using websocketpp::lib::placeholders::_2;
@@ -182,6 +185,10 @@ my $tlsPort = $model->getParameterByName("tlsPort");
182185
# Default: 443
183186
$tlsPort = $tlsPort ? $tlsPort->getValueAt(0)->getCppExpression() : 443;
184187

188+
my $tlsCipherWhitelist = $model->getParameterByName("tlsCipherWhitelist");
189+
# Default: Empty string
190+
$tlsCipherWhitelist = $tlsCipherWhitelist ? $tlsCipherWhitelist->getValueAt(0)->getCppExpression() : "";
191+
185192
my $certificateFileName = $model->getParameterByName("certificateFileName");
186193
# Default: Default is to read ws-server.pem from the etc sub-directory of the application.
187194
$certificateFileName = $certificateFileName ? $certificateFileName->getValueAt(0)->getCppExpression() : "";
@@ -343,6 +350,12 @@ MY_OPERATOR::MY_OPERATOR() {
343350
// will lead to a compiler error. So, only for the string based copy from a
344351
// perl variable, the following check must be done for an empty string and
345352
// then it should be done as shown below.
353+
<% if ($tlsCipherWhitelist eq "") { %>
354+
tlsCipherWhitelist = "";
355+
<% } else { %>
356+
tlsCipherWhitelist = <%=$tlsCipherWhitelist%>;
357+
<%}%>
358+
346359
<% if ($certificateFileName eq "") { %>
347360
certificateFileName = "";
348361
<% } else { %>
@@ -416,6 +429,7 @@ MY_OPERATOR::MY_OPERATOR() {
416429
"-->Channel " << boost::to_string(udpChannelNumber) <<
417430
". Following are the user configured operator parameters: "
418431
"tlsPort=" << tlsPort <<
432+
", tlsCipherWhitelist=" << tlsCipherWhitelist <<
419433
", certificateFileName=" << certificateFileName <<
420434
", certificatePassword length=" << certificatePassword.length() <<
421435
", trustedClientCertificateFileName=" << trustedClientCertificateFileName <<
@@ -544,6 +558,15 @@ void MY_OPERATOR::prepareToShutdown() {
544558
// This operator is being shutdown now.
545559
// We can empty the following containers.
546560
client_connections_map.clear();
561+
562+
// At this time, we can stop the boost ASIO run loop that
563+
// got started below in the ws_server method of this sink operator.
564+
if(iosRunLoopStarted == true) {
565+
// It will stop the ios run loop and make the
566+
// sink operator ws_server thread exit. It should also help
567+
// in the immediate closure of the TLS and non-TLS ports.
568+
ios.stop();
569+
}
547570
} // End of prepareToShutdown.
548571

549572
// Processing for source and threaded operators
@@ -851,9 +874,8 @@ void MY_OPERATOR::ws_server() {
851874
// corresponding close brace at the end of the code block below.
852875
//
853876
// while(!getPE().getShutdownRequested()) {
854-
// Set up an external io_service to run both endpoints on. This is not
855-
// strictly necessary, but simplifies thread management a bit.
856-
boost::asio::io_service ios;
877+
878+
iosRunLoopStarted = false;
857879

858880
// If the user opted for an additional non-TLS (plain)
859881
// Websocket endpoint, let us create that as well.
@@ -972,6 +994,7 @@ void MY_OPERATOR::ws_server() {
972994
// This will block until the server socket gets closed.
973995
// For additional details, please refer to the commentary and
974996
// logic in the prepareToShutdown method
997+
iosRunLoopStarted = true;
975998
ios.run();
976999
// }
9771000
} // End of ws_server
@@ -1261,6 +1284,27 @@ MY_OPERATOR::context_ptr MY_OPERATOR::on_tls_init(
12611284
| boost::asio::ssl::context::no_tlsv1_1
12621285
| boost::asio::ssl::context::single_dh_use);
12631286
ctx->set_password_callback(bind(&MY_OPERATOR::get_private_key_password, this));
1287+
1288+
// If the user has configured specific TLS ciphers to be used when
1289+
// establishing the TLS connection, then we can set it now.
1290+
// Reference URLs:
1291+
// https://stackoverflow.com/questions/50058521/using-specific-cipher-for-ssl-tls-in-boostasio
1292+
// https://www.ibm.com/support/knowledgecenter/SSB23S_1.1.0.2020/gtpc2/cpp_ssl_ctx_set_cipher_list.html
1293+
// https://www.ibm.com/support/knowledgecenter/SSB23S_1.1.0.2020/gtps7/s5sple1.html
1294+
if(tlsCipherWhitelist.length() > 0) {
1295+
// e-g: "DES-CBC3-SHA,AES128-SHA,AES256-SHA"
1296+
if (SSL_CTX_set_cipher_list(ctx->native_handle(),
1297+
tlsCipherWhitelist.c_str()) <= 0) {
1298+
std::cout << "Operator " << operatorPhysicalName <<
1299+
"-->Channel " << boost::to_string(udpChannelNumber) <<
1300+
"-->" << "Error in setting user provided TLS cipher list." << std::endl;
1301+
1302+
throw std::runtime_error(
1303+
std::string("WebSocketSink_cpp.cgt: Error in on_tls_init: ") +
1304+
"Error in setting user provided TLS cipher list.");
1305+
}
1306+
}
1307+
12641308
ctx->use_certificate_chain_file(certificateFileName.c_str());
12651309
ctx->use_private_key_file(certificateFileName.c_str(),
12661310
boost::asio::ssl::context::pem);

com.ibm.streamsx.websocket/com.ibm.streamsx.websocket.op/WebSocketSink/WebSocketSink_h.cgt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/*
99
============================================================
1010
First created on: Apr/27/2020
11-
Last modified on: Aug/31/2020
11+
Last modified on: Sep/21/2020
1212

1313
This include file contains the necessary inclusion of
1414
other C++ header files, declaration of member variables
@@ -57,6 +57,11 @@ public:
5757
std::string operatorPhysicalName;
5858
SPL::int32 udpChannelNumber;
5959
SPL::uint32 tlsPort;
60+
// A comma separated approved TLS/SSL ciphers can be specified.
61+
// If there is a need to avoid using insecure vulnerable ciphers,
62+
// this can be used to specify the approved ciphers to be used.
63+
// e-g: "DES-CBC3-SHA,AES128-SHA,AES256-SHA"
64+
std::string tlsCipherWhitelist;
6065
std::string certificateFileName;
6166
std::string certificatePassword;
6267
std::string trustedClientCertificateFileName;
@@ -79,6 +84,10 @@ public:
7984
SPL::uint32 maxClientConnectionsAllowed;
8085
server_non_tls endpoint_non_tls;
8186
server_tls endpoint_tls;
87+
// Set up an external io_service to run the endpoint on. This is not
88+
// strictly necessary, but simplifies thread management a bit.
89+
boost::asio::io_service ios;
90+
bool iosRunLoopStarted;
8291
SPL::boolean tlsEndpointStarted;
8392
SPL::boolean nonTlsEndpointStarted;
8493
SPL::int64 timeOfPreviousStaleConnectionRemoval;

com.ibm.streamsx.websocket/com.ibm.streamsx.websocket.op/WebSocketSource/WebSocketSource.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,16 @@
484484
<type>boolean</type>
485485
<cardinality>1</cardinality>
486486
</parameter>
487+
488+
<parameter>
489+
<name>tlsCipherWhitelist</name>
490+
<description>This parameter can be used to specify a string containing one or more comma separated TLS/SSL ciphers that should be used during TLS/SSL connection negotiations with clients. It is handy when there is a need to avoid using ciphers that are found to have security vulnerabilities. (Default is an empty string.)</description>
491+
<optional>true</optional>
492+
<rewriteAllowed>true</rewriteAllowed>
493+
<expressionMode>AttributeFree</expressionMode>
494+
<type>rstring</type>
495+
<cardinality>1</cardinality>
496+
</parameter>
487497
</parameters>
488498

489499
<inputPorts>

com.ibm.streamsx.websocket/com.ibm.streamsx.websocket.op/WebSocketSource/WebSocketSource_cpp.cgt

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/*
99
============================================================
1010
First created on: Feb/22/2020
11-
Last modified on: Aug/31/2020
11+
Last modified on: Sep/22/2020
1212

1313
This particular operator (WebSocketSource) is used to
1414
receive either text data or binary data from one or more
@@ -52,6 +52,9 @@
5252
#include <boost/exception/to_string.hpp>
5353
#include <boost/thread/thread.hpp>
5454

55+
// Necessary headers for setting user specified TLS ciphers.
56+
#include <openssl/ssl.h>
57+
5558
// For the core logic below, we will need these namespaces from websocketpp.
5659
using websocketpp::lib::placeholders::_1;
5760
using websocketpp::lib::placeholders::_2;
@@ -508,6 +511,10 @@ my $tlsPort = $model->getParameterByName("tlsPort");
508511
# Default: 443
509512
$tlsPort = $tlsPort ? $tlsPort->getValueAt(0)->getCppExpression() : 443;
510513

514+
my $tlsCipherWhitelist = $model->getParameterByName("tlsCipherWhitelist");
515+
# Default: Empty string
516+
$tlsCipherWhitelist = $tlsCipherWhitelist ? $tlsCipherWhitelist->getValueAt(0)->getCppExpression() : "";
517+
511518
my $certificateFileName = $model->getParameterByName("certificateFileName");
512519
# Default: Default is to read ws-server.pem from the etc sub-directory of the application.
513520
$certificateFileName = $certificateFileName ? $certificateFileName->getValueAt(0)->getCppExpression() : "";
@@ -735,6 +742,12 @@ MY_OPERATOR::MY_OPERATOR() {
735742
// will lead to a compiler error. So, only for the string based copy from a
736743
// perl variable, the following check must be done for an empty string and
737744
// then it should be done as shown below.
745+
<% if ($tlsCipherWhitelist eq "") { %>
746+
tlsCipherWhitelist = "";
747+
<% } else { %>
748+
tlsCipherWhitelist = <%=$tlsCipherWhitelist%>;
749+
<%}%>
750+
738751
<% if ($certificateFileName eq "") { %>
739752
certificateFileName = "";
740753
<% } else { %>
@@ -821,6 +834,7 @@ MY_OPERATOR::MY_OPERATOR() {
821834
"-->Channel " << boost::to_string(udpChannelNumber) <<
822835
". Following are the user configured operator parameters: "
823836
"tlsPort=" << tlsPort <<
837+
", tlsCipherWhitelist=" << tlsCipherWhitelist <<
824838
", certificateFileName=" << certificateFileName <<
825839
", certificatePassword length=" << certificatePassword.length() <<
826840
", trustedClientCertificateFileName=" << trustedClientCertificateFileName <<
@@ -961,6 +975,15 @@ void MY_OPERATOR::prepareToShutdown() {
961975
// This operator is being shutdown now.
962976
// We can empty the following containers.
963977
client_connections_map.clear();
978+
979+
// At this time, we can stop the boost ASIO run loop that
980+
// got started below in the process method of this source operator.
981+
if(iosRunLoopStarted == true) {
982+
// It will stop the ios run loop and make the
983+
// source operator thread exit. It should also help
984+
// in the immediate closure of the TLS and non-TLS ports.
985+
ios.stop();
986+
}
964987
} // End of prepareToShutdown.
965988

966989
// Processing for source and threaded operators
@@ -995,10 +1018,9 @@ void MY_OPERATOR::process(uint32_t idx) {
9951018
// corresponding close brace at the end of the code block below.
9961019
//
9971020
// while(!getPE().getShutdownRequested()) {
998-
// Set up an external io_service to run both endpoints on. This is not
999-
// strictly necessary, but simplifies thread management a bit.
1000-
boost::asio::io_service ios;
10011021

1022+
iosRunLoopStarted = false;
1023+
10021024
// If the user opted for an additional non-TLS (plain)
10031025
// Websocket endpoint, let us create that as well.
10041026
// This non_tls insecure endpoint is here for a very
@@ -1128,6 +1150,7 @@ void MY_OPERATOR::process(uint32_t idx) {
11281150
// This will block until the server socket gets closed.
11291151
// For additional details, please refer to the commentary and
11301152
// logic in the prepareToShutdown method
1153+
iosRunLoopStarted = true;
11311154
ios.run();
11321155
// }
11331156
} // End of process (for source operators)
@@ -2297,6 +2320,27 @@ MY_OPERATOR::context_ptr MY_OPERATOR::on_tls_init(
22972320
| boost::asio::ssl::context::no_tlsv1_1
22982321
| boost::asio::ssl::context::single_dh_use);
22992322
ctx->set_password_callback(bind(&MY_OPERATOR::get_private_key_password, this));
2323+
2324+
// If the user has configured specific TLS ciphers to be used when
2325+
// establishing the TLS connection, then we can set it now.
2326+
// Reference URLs:
2327+
// https://stackoverflow.com/questions/50058521/using-specific-cipher-for-ssl-tls-in-boostasio
2328+
// https://www.ibm.com/support/knowledgecenter/SSB23S_1.1.0.2020/gtpc2/cpp_ssl_ctx_set_cipher_list.html
2329+
// https://www.ibm.com/support/knowledgecenter/SSB23S_1.1.0.2020/gtps7/s5sple1.html
2330+
if(tlsCipherWhitelist.length() > 0) {
2331+
// e-g: "DES-CBC3-SHA,AES128-SHA,AES256-SHA"
2332+
if (SSL_CTX_set_cipher_list(ctx->native_handle(),
2333+
tlsCipherWhitelist.c_str()) <= 0) {
2334+
std::cout << "Operator " << operatorPhysicalName <<
2335+
"-->Channel " << boost::to_string(udpChannelNumber) <<
2336+
"-->" << "Error in setting user provided TLS cipher list." << std::endl;
2337+
2338+
throw std::runtime_error(
2339+
std::string("WebSocketSource_cpp.cgt: Error in on_tls_init: ") +
2340+
"Error in setting user provided TLS cipher list.");
2341+
}
2342+
}
2343+
23002344
// Let us now load the server's private key and
23012345
// public certificate holding PEM file.
23022346
ctx->use_certificate_chain_file(certificateFileName.c_str());

0 commit comments

Comments
 (0)