EventDestination: Implement VerifyCertificate
VerifyCertificate is a property on the Redfish EventDestination schema.
It specifies that this property is:
``` An indication of whether the service will verify the certificate of
the server referenced by the `Destination` property prior to sending the
event ```
To keep prior behavior, and to ensure behavior that's secure by default,
if the user omits the property, it is assumed to be true. This property
is also persisted and restored.
Tested:
Redfish-Event-Listener succeeds with the following procedure
Start Redfish-Event-Listener
PATCH /redfish/v1/Subscriptions/<subid> VerifyCertificate: false
POST /redfish/v1/EventService/Actions/EventService.SubmitTestEvent
Redfish-Event-Listener then hits an internal error, due to an encoding
compatibility unrelated to this patch, but is documented in the receiver
[1]
POST of a subscription with VerifyCertificate: false set, succeeds.
[1] https://github.com/DMTF/Redfish-Event-Listener/blob/6f3f98beafc89fa9bbf86aa4f8cac6c1987390fb/RedfishEventListener_v1.py#L61
Change-Id: I27e0a3fe87b4dbd0432bfaa22ebf593c3955db11
Signed-off-by: Ravi Teja <raviteja28031990@gmail.com>
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/http/http_client.hpp b/http/http_client.hpp
index 107f943..62e7df6 100644
--- a/http/http_client.hpp
+++ b/http/http_client.hpp
@@ -134,8 +134,8 @@
std::string subId;
std::shared_ptr<ConnectionPolicy> connPolicy;
boost::urls::url host;
+ ensuressl::VerifyCertificate verifyCert;
uint32_t connId;
-
// Data buffers
http::request<bmcweb::HttpBody> req;
using parser_type = http::response_parser<bmcweb::HttpBody>;
@@ -608,7 +608,7 @@
if (ssl)
{
std::optional<boost::asio::ssl::context> sslCtx =
- ensuressl::getSSLClientContext();
+ ensuressl::getSSLClientContext(verifyCert);
if (!sslCtx)
{
@@ -633,10 +633,11 @@
explicit ConnectionInfo(
boost::asio::io_context& iocIn, const std::string& idIn,
const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
- const boost::urls::url_view_base& hostIn, unsigned int connIdIn) :
+ const boost::urls::url_view_base& hostIn,
+ ensuressl::VerifyCertificate verifyCertIn, unsigned int connIdIn) :
subId(idIn),
- connPolicy(connPolicyIn), host(hostIn), connId(connIdIn), ioc(iocIn),
- resolver(iocIn), conn(iocIn), timer(iocIn)
+ connPolicy(connPolicyIn), host(hostIn), verifyCert(verifyCertIn),
+ connId(connIdIn), ioc(iocIn), resolver(iocIn), conn(iocIn), timer(iocIn)
{
initializeConnection(host.scheme() == "https");
}
@@ -651,6 +652,7 @@
boost::urls::url destIP;
std::vector<std::shared_ptr<ConnectionInfo>> connections;
boost::container::devector<PendingRequest> requestQueue;
+ ensuressl::VerifyCertificate verifyCert;
friend class HttpClient;
@@ -820,7 +822,7 @@
unsigned int newId = static_cast<unsigned int>(connections.size());
auto& ret = connections.emplace_back(std::make_shared<ConnectionInfo>(
- ioc, id, connPolicy, destIP, newId));
+ ioc, id, connPolicy, destIP, verifyCert, newId));
BMCWEB_LOG_DEBUG("Added connection {} to pool {}",
connections.size() - 1, id);
@@ -832,9 +834,11 @@
explicit ConnectionPool(
boost::asio::io_context& iocIn, const std::string& idIn,
const std::shared_ptr<ConnectionPolicy>& connPolicyIn,
- const boost::urls::url_view_base& destIPIn) :
+ const boost::urls::url_view_base& destIPIn,
+ ensuressl::VerifyCertificate verifyCertIn) :
ioc(iocIn),
- id(idIn), connPolicy(connPolicyIn), destIP(destIPIn)
+ id(idIn), connPolicy(connPolicyIn), destIP(destIPIn),
+ verifyCert(verifyCertIn)
{
BMCWEB_LOG_DEBUG("Initializing connection pool for {}", id);
@@ -876,28 +880,37 @@
// Send a request to destIP where additional processing of the
// result is not required
void sendData(std::string&& data, const boost::urls::url_view_base& destUri,
+ ensuressl::VerifyCertificate verifyCert,
const boost::beast::http::fields& httpHeader,
const boost::beast::http::verb verb)
{
const std::function<void(Response&)> cb = genericResHandler;
- sendDataWithCallback(std::move(data), destUri, httpHeader, verb, cb);
+ sendDataWithCallback(std::move(data), destUri, verifyCert, httpHeader,
+ verb, cb);
}
// Send request to destIP and use the provided callback to
// handle the response
void sendDataWithCallback(std::string&& data,
const boost::urls::url_view_base& destUrl,
+ ensuressl::VerifyCertificate verifyCert,
const boost::beast::http::fields& httpHeader,
const boost::beast::http::verb verb,
const std::function<void(Response&)>& resHandler)
{
- std::string clientKey = std::format("{}://{}", destUrl.scheme(),
+ std::string_view verify = "ssl_verify";
+ if (verifyCert == ensuressl::VerifyCertificate::NoVerify)
+ {
+ verify = "ssl no verify";
+ }
+ std::string clientKey = std::format("{}{}://{}", verify,
+ destUrl.scheme(),
destUrl.encoded_host_and_port());
auto pool = connectionPools.try_emplace(clientKey);
if (pool.first->second == nullptr)
{
pool.first->second = std::make_shared<ConnectionPool>(
- ioc, clientKey, connPolicy, destUrl);
+ ioc, clientKey, connPolicy, destUrl, verifyCert);
}
// Send the data using either the existing connection pool or the
// newly created connection pool
diff --git a/include/event_service_store.hpp b/include/event_service_store.hpp
index f7f03ab..e59a7d8 100644
--- a/include/event_service_store.hpp
+++ b/include/event_service_store.hpp
@@ -15,6 +15,7 @@
std::string id;
boost::urls::url destinationUrl;
std::string protocol;
+ bool verifyCertificate = true;
std::string retryPolicy;
std::string customText;
std::string eventFormatType;
@@ -69,6 +70,15 @@
}
subvalue->protocol = *value;
}
+ else if (element.first == "VerifyCertificate")
+ {
+ const bool* value = element.second.get_ptr<const bool*>();
+ if (value == nullptr)
+ {
+ continue;
+ }
+ subvalue->verifyCertificate = *value;
+ }
else if (element.first == "DeliveryRetryPolicy")
{
const std::string* value =
diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp
index 8ef3a6e..0f14143 100644
--- a/include/persistent_data.hpp
+++ b/include/persistent_data.hpp
@@ -301,6 +301,7 @@
subscription["SubscriptionType"] = subValue.subscriptionType;
subscription["MetricReportDefinitions"] =
subValue.metricReportDefinitions;
+ subscription["VerifyCertificate"] = subValue.verifyCertificate;
subscriptions.emplace_back(std::move(subscription));
}
diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp
index ce1e638..97e9929 100644
--- a/include/ssl_key_handler.hpp
+++ b/include/ssl_key_handler.hpp
@@ -636,7 +636,14 @@
return std::make_shared<boost::asio::ssl::context>(std::move(sslCtx));
}
-inline std::optional<boost::asio::ssl::context> getSSLClientContext()
+enum class VerifyCertificate
+{
+ Verify,
+ NoVerify
+};
+
+inline std::optional<boost::asio::ssl::context>
+ getSSLClientContext(VerifyCertificate verifyCertificate)
{
namespace fs = std::filesystem;
@@ -662,8 +669,14 @@
return std::nullopt;
}
+ int mode = boost::asio::ssl::verify_peer;
+ if (verifyCertificate == VerifyCertificate::NoVerify)
+ {
+ mode = boost::asio::ssl::verify_none;
+ }
+
// Verify the remote server's certificate
- sslCtx.set_verify_mode(boost::asio::ssl::verify_peer, ec);
+ sslCtx.set_verify_mode(mode, ec);
if (ec)
{
BMCWEB_LOG_ERROR("SSL context set_verify_mode failed");
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
index 93ebc88..045ad8c 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -292,8 +292,10 @@
// A connection pool will be created if one does not already exist
if (client)
{
- client->sendData(std::move(msg), destinationUrl, httpHeaders,
- boost::beast::http::verb::post);
+ client->sendData(
+ std::move(msg), destinationUrl,
+ static_cast<ensuressl::VerifyCertificate>(verifyCertificate),
+ httpHeaders, boost::beast::http::verb::post);
return true;
}
@@ -568,6 +570,7 @@
subValue->id = newSub->id;
subValue->destinationUrl = newSub->destinationUrl;
subValue->protocol = newSub->protocol;
+ subValue->verifyCertificate = newSub->verifyCertificate;
subValue->retryPolicy = newSub->retryPolicy;
subValue->customText = newSub->customText;
subValue->eventFormatType = newSub->eventFormatType;
diff --git a/redfish-core/include/redfish_aggregator.hpp b/redfish-core/include/redfish_aggregator.hpp
index bc0300a..649ddb4 100644
--- a/redfish-core/include/redfish_aggregator.hpp
+++ b/redfish-core/include/redfish_aggregator.hpp
@@ -737,8 +737,9 @@
{
url.set_query(targetURI.query());
}
- client.sendDataWithCallback(std::move(data), url, thisReq.fields(),
- thisReq.method(), cb);
+ client.sendDataWithCallback(std::move(data), url,
+ ensuressl::VerifyCertificate::Verify,
+ thisReq.fields(), thisReq.method(), cb);
}
// Forward a request for a collection URI to each known satellite BMC
@@ -759,8 +760,9 @@
url.set_query(thisReq.url().query());
}
std::string data = thisReq.body();
- client.sendDataWithCallback(std::move(data), url, thisReq.fields(),
- thisReq.method(), cb);
+ client.sendDataWithCallback(std::move(data), url,
+ ensuressl::VerifyCertificate::Verify,
+ thisReq.fields(), thisReq.method(), cb);
}
}
@@ -785,8 +787,9 @@
std::string data = thisReq.body();
- client.sendDataWithCallback(std::move(data), url, thisReq.fields(),
- thisReq.method(), cb);
+ client.sendDataWithCallback(std::move(data), url,
+ ensuressl::VerifyCertificate::Verify,
+ thisReq.fields(), thisReq.method(), cb);
}
}
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index aebc282..dab53fe 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -278,6 +278,7 @@
}
std::string destUrl;
std::string protocol;
+ std::optional<bool> verifyCertificate;
std::optional<std::string> context;
std::optional<std::string> subscriptionType;
std::optional<std::string> eventFormatType2;
@@ -294,7 +295,8 @@
"EventFormatType", eventFormatType2, "HttpHeaders", headers,
"RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
"DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
- mrdJsonArray, "ResourceTypes", resTypes))
+ mrdJsonArray, "ResourceTypes", resTypes, "VerifyCertificate",
+ verifyCertificate))
{
return;
}
@@ -433,6 +435,11 @@
}
subValue->protocol = protocol;
+ if (verifyCertificate)
+ {
+ subValue->verifyCertificate = *verifyCertificate;
+ }
+
if (eventFormatType2)
{
if (std::ranges::find(supportedEvtFormatTypes, *eventFormatType2) ==
@@ -672,6 +679,8 @@
asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
+ asyncResp->res.jsonValue["VerifyCertificate"] =
+ subValue->verifyCertificate;
nlohmann::json::array_t mrdJsonArray;
for (const auto& mdrUri : subValue->metricReportDefinitions)
@@ -706,9 +715,11 @@
std::optional<std::string> context;
std::optional<std::string> retryPolicy;
+ std::optional<bool> verifyCertificate;
std::optional<std::vector<nlohmann::json::object_t>> headers;
if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context,
+ "VerifyCertificate", verifyCertificate,
"DeliveryRetryPolicy", retryPolicy,
"HttpHeaders", headers))
{
@@ -754,6 +765,11 @@
subValue->retryPolicy = *retryPolicy;
}
+ if (verifyCertificate)
+ {
+ subValue->verifyCertificate = *verifyCertificate;
+ }
+
EventServiceManager::getInstance().updateSubscriptionData();
});
BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")