Skip to content

Commit c38feb2

Browse files
Self-Healing-Open ignore iamopen from other nodes when open (#7561)
Co-authored-by: Amaury Chamayou <amchamay@microsoft.com> Co-authored-by: Amaury Chamayou <amaury@xargs.fr>
1 parent 3e8ac07 commit c38feb2

File tree

6 files changed

+270
-61
lines changed

6 files changed

+270
-61
lines changed

include/ccf/service/tables/self_healing_open.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
// Licensed under the Apache 2.0 License.
33
#pragma once
44

5+
#include "ccf/crypto/pem.h"
6+
#include "ccf/crypto/sha256_hash.h"
7+
#include "ccf/crypto/verifier.h"
58
#include "ccf/ds/enum_formatter.h"
69
#include "ccf/ds/json.h"
710
#include "ccf/ds/quote_info.h"
811
#include "ccf/service/map.h"
912
#include "ccf/service/node_info_network.h"
13+
#include "ccf/tx_id.h"
1014

1115
namespace ccf
1216
{
@@ -26,17 +30,30 @@ namespace ccf
2630
DECLARE_JSON_TYPE(Identity);
2731
DECLARE_JSON_REQUIRED_FIELDS(Identity, intrinsic_id, published_address);
2832

29-
struct NodeInfo
33+
inline std::string service_fingerprint_from_pem(const ccf::crypto::Pem& pem)
3034
{
31-
ccf::QuoteInfo quote_info;
35+
return ccf::crypto::Sha256Hash(
36+
ccf::crypto::public_key_der_from_cert(pem.raw()))
37+
.hex_str();
38+
}
39+
40+
struct RequestNodeInfo
41+
{
42+
QuoteInfo quote_info;
3243
Identity identity;
33-
std::vector<uint8_t> cert_der;
34-
std::string service_identity;
44+
std::vector<uint8_t> service_cert_der;
3545
};
36-
37-
DECLARE_JSON_TYPE(NodeInfo);
46+
DECLARE_JSON_TYPE(RequestNodeInfo);
3847
DECLARE_JSON_REQUIRED_FIELDS(
39-
NodeInfo, quote_info, identity, cert_der, service_identity);
48+
RequestNodeInfo, quote_info, identity, service_cert_der);
49+
50+
struct NodeInfo : RequestNodeInfo
51+
{
52+
std::vector<uint8_t> node_cert_der;
53+
};
54+
55+
DECLARE_JSON_TYPE_WITH_BASE(NodeInfo, RequestNodeInfo);
56+
DECLARE_JSON_REQUIRED_FIELDS(NodeInfo, node_cert_der);
4057

4158
enum class StateMachine : uint8_t
4259
{
@@ -66,7 +83,7 @@ namespace ccf
6683

6784
using NodeInfoMap =
6885
ServiceMap<IntrinsicIdentifier, ccf::self_healing_open::NodeInfo>;
69-
using Gossips = ServiceMap<IntrinsicIdentifier, ccf::kv::Version>;
86+
using Gossips = ServiceMap<IntrinsicIdentifier, ccf::TxID>;
7087
using ChosenNode = ServiceValue<IntrinsicIdentifier>;
7188
using Votes = ServiceSet<IntrinsicIdentifier>;
7289
using SMState = ServiceValue<ccf::self_healing_open::StateMachine>;

src/node/rpc/self_healing_open_handlers.h

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
#pragma once
44

55
#include "ccf/common_auth_policies.h"
6+
#include "ccf/crypto/verifier.h"
67
#include "ccf/endpoint_context.h"
78
#include "ccf/json_handler.h"
89
#include "ccf/node_context.h"
910
#include "ccf/odata_error.h"
11+
#include "ccf/service/operator_feature.h"
12+
#include "ccf/service/tables/nodes.h"
1013
#include "ccf/service/tables/self_healing_open.h"
1114
#include "node/node_configuration_subsystem.h"
1215
#include "node/rpc/node_frontend_utils.h"
@@ -22,7 +25,7 @@ namespace ccf::node
2225
static HandlerJsonParamsAndForward wrap_self_healing_open(
2326
SelfHealingOpenHandler<Input> cb, ccf::AbstractNodeContext& node_context)
2427
{
25-
return [cb = std::move(cb), node_context](
28+
return [cb = std::move(cb), &node_context](
2629
endpoints::EndpointContext& args, const nlohmann::json& params) {
2730
auto config = node_context.get_subsystem<NodeConfigurationSubsystem>();
2831
auto node_operation = node_context.get_subsystem<AbstractNodeOperation>();
@@ -45,7 +48,7 @@ namespace ccf::node
4548
auto in = params.get<Input>();
4649
self_healing_open::RequestNodeInfo info = in.info;
4750

48-
// ---- Validate the quote and store the node info ----
51+
// ---- Validate the quote against our store and store the node info ----
4952

5053
auto cert_der = ccf::crypto::public_key_der_from_cert(
5154
args.rpc_ctx->get_session_context()->caller_cert);
@@ -68,6 +71,8 @@ namespace ccf::node
6871
"Self-healing-open message from intrinsic id {} has a valid quote",
6972
info.identity.intrinsic_id);
7073

74+
// ---- The sender now has trusted code ----
75+
7176
// Validating that we haven't heard from this node before, of if we have
7277
// that the cert hasn't changed
7378
auto* node_info_handle = args.tx.rw<self_healing_open::NodeInfoMap>(
@@ -78,11 +83,11 @@ namespace ccf::node
7883
if (existing_node_info.has_value())
7984
{
8085
// If we have seen this node before, check that the cert is the same
81-
if (existing_node_info->cert_der != cert_der)
86+
if (existing_node_info->node_cert_der != cert_der)
8287
{
8388
auto message = fmt::format(
8489
"Self-healing-open message from intrinsic id {} is invalid: "
85-
"certificate has changed",
90+
"certificate public key has changed",
8691
info.identity.intrinsic_id);
8792
LOG_FAIL_FMT("{}", message);
8893
return make_error(
@@ -92,10 +97,9 @@ namespace ccf::node
9297
else
9398
{
9499
self_healing_open::NodeInfo src_info{
95-
.quote_info = info.quote_info,
96-
.identity = info.identity,
97-
.cert_der = cert_der,
98-
.service_identity = info.service_identity};
100+
info,
101+
cert_der,
102+
};
99103
node_info_handle->put(info.identity.intrinsic_id, src_info);
100104
}
101105

@@ -201,8 +205,52 @@ namespace ccf::node
201205
.install();
202206

203207
auto self_healing_open_iamopen =
204-
[](auto& args, self_healing_open::TaggedWithNodeInfo in)
205-
-> std::optional<ErrorDetails> {
208+
[&node_context](
209+
auto& args,
210+
self_healing_open::IAmOpenRequest in) -> std::optional<ErrorDetails> {
211+
auto sm_state = args.tx
212+
.template ro<self_healing_open::SMState>(
213+
Tables::SELF_HEALING_OPEN_SM_STATE)
214+
->get();
215+
if (!sm_state.has_value())
216+
{
217+
throw std::logic_error(
218+
"Self-healing-open state machine state is not set");
219+
}
220+
221+
if (
222+
sm_state.value() == self_healing_open::StateMachine::OPENING ||
223+
sm_state.value() == self_healing_open::StateMachine::OPEN)
224+
{
225+
auto node_operation =
226+
node_context.get_subsystem<AbstractNodeOperation>();
227+
auto& self_iamopen_request =
228+
node_operation->self_healing_open().get_iamopen_request(args.tx);
229+
230+
auto myid = fmt::format(
231+
"{}:{} previously {}@{}",
232+
self_iamopen_request.info.identity.intrinsic_id,
233+
self_healing_open::service_fingerprint_from_pem(
234+
crypto::cert_der_to_pem(
235+
self_iamopen_request.info.service_cert_der)),
236+
self_iamopen_request.prev_service_fingerprint,
237+
self_iamopen_request.txid.to_str());
238+
auto inid = fmt::format(
239+
"{}:{} previously {}@{}",
240+
in.info.identity.intrinsic_id,
241+
self_healing_open::service_fingerprint_from_pem(
242+
crypto::cert_der_to_pem(in.info.service_cert_der)),
243+
in.prev_service_fingerprint,
244+
in.txid.to_str());
245+
LOG_INFO_FMT(
246+
"{} is already open, ignoring IAmOpen from {}", myid, inid);
247+
248+
return ErrorDetails{
249+
.status = HTTP_STATUS_BAD_REQUEST,
250+
.code = ccf::errors::InvalidNodeState,
251+
.msg = "Node is already open, ignoring iamopen request"};
252+
}
253+
206254
LOG_TRACE_FMT(
207255
"Self-healing-open: receive IAmOpen from {}",
208256
in.info.identity.intrinsic_id);
@@ -220,9 +268,8 @@ namespace ccf::node
220268
.make_endpoint(
221269
"/self_healing_open/iamopen",
222270
HTTP_PUT,
223-
json_adapter(
224-
wrap_self_healing_open<self_healing_open::TaggedWithNodeInfo>(
225-
self_healing_open_iamopen, node_context)),
271+
json_adapter(wrap_self_healing_open<self_healing_open::IAmOpenRequest>(
272+
self_healing_open_iamopen, node_context)),
226273
no_auth_required)
227274
.set_forwarding_required(endpoints::ForwardingRequired::Never)
228275
.set_openapi_hidden(true)

0 commit comments

Comments
 (0)