CCF
Loading...
Searching...
No Matches
self_healing_open_handlers.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
7#include "ccf/json_handler.h"
8#include "ccf/node_context.h"
9#include "ccf/odata_error.h"
14
15namespace ccf::node
16{
17 template <typename Input>
18 using SelfHealingOpenHandler = std::function<std::optional<ErrorDetails>(
19 endpoints::EndpointContext& args, Input& in)>;
20
21 template <typename Input>
22 static HandlerJsonParamsAndForward wrap_self_healing_open(
24 {
25 return [cb = std::move(cb), node_context](
26 endpoints::EndpointContext& args, const nlohmann::json& params) {
27 auto config = node_context.get_subsystem<NodeConfigurationSubsystem>();
28 auto node_operation = node_context.get_subsystem<AbstractNodeOperation>();
29 if (config == nullptr || node_operation == nullptr)
30 {
31 return make_error(
32 HTTP_STATUS_BAD_REQUEST,
33 ccf::errors::InvalidNodeState,
34 "Unable to open self-healing-open subsystems");
35 }
36
37 if (!config->get().node_config.recover.self_healing_open.has_value())
38 {
39 return make_error(
40 HTTP_STATUS_BAD_REQUEST,
41 ccf::errors::InvalidNodeState,
42 "This node cannot do self-healing-open");
43 }
44
45 auto in = params.get<Input>();
47
48 // ---- Validate the quote and store the node info ----
49
51 args.rpc_ctx->get_session_context()->caller_cert);
52
54 QuoteVerificationResult verify_result = node_operation->verify_quote(
55 args.tx, info.quote_info, cert_der, measurement);
56 if (verify_result != QuoteVerificationResult::Verified)
57 {
58 const auto [code, message] = quote_verification_error(verify_result);
60 "Self-healing-open message from {} has an invalid quote: {} ({})",
61 info.identity.intrinsic_id,
62 code,
63 message);
64 return make_error(code, ccf::errors::InvalidQuote, message);
65 }
66
68 "Self-healing-open message from intrinsic id {} has a valid quote",
69 info.identity.intrinsic_id);
70
71 // Validating that we haven't heard from this node before, of if we have
72 // that the cert hasn't changed
73 auto* node_info_handle = args.tx.rw<self_healing_open::NodeInfoMap>(
74 Tables::SELF_HEALING_OPEN_NODES);
75 auto existing_node_info =
76 node_info_handle->get(info.identity.intrinsic_id);
77
78 if (existing_node_info.has_value())
79 {
80 // If we have seen this node before, check that the cert is the same
81 if (existing_node_info->cert_der != cert_der)
82 {
83 auto message = fmt::format(
84 "Self-healing-open message from intrinsic id {} is invalid: "
85 "certificate has changed",
86 info.identity.intrinsic_id);
87 LOG_FAIL_FMT("{}", message);
88 return make_error(
89 HTTP_STATUS_BAD_REQUEST, ccf::errors::NodeAlreadyExists, message);
90 }
91 }
92 else
93 {
95 .quote_info = info.quote_info,
96 .identity = info.identity,
97 .cert_der = cert_der,
98 .service_identity = info.service_identity};
99 node_info_handle->put(info.identity.intrinsic_id, src_info);
100 }
101
102 // ---- Run callback ----
103
104 auto ret = cb(args, in);
105 if (ret.has_value())
106 {
107 jsonhandler::JsonAdapterResponse res = ret.value();
108 return res;
109 }
110
111 // ---- Advance state machine ----
112
113 try
114 {
115 node_operation->self_healing_open().advance(args.tx, false);
116 }
117 catch (const std::logic_error& e)
118 {
119 LOG_FAIL_FMT("Self-healing-open failed to advance state: {}", e.what());
120 return make_error(
121 HTTP_STATUS_INTERNAL_SERVER_ERROR,
122 ccf::errors::InternalError,
123 fmt::format(
124 "Failed to advance self-healing-open state: {}", e.what()));
125 }
126
127 return make_success();
128 };
129 }
130
131 static void init_self_healing_open_handlers(
133 ccf::AbstractNodeContext& node_context)
134 {
135 auto self_healing_open_gossip =
136 [](
137 auto& args,
138 self_healing_open::GossipRequest in) -> std::optional<ErrorDetails> {
140 "Self-healing-open: receive gossip from {}",
141 in.info.identity.intrinsic_id);
142
143 // Stop accepting gossips once a node has voted
144 auto chosen_replica = args.tx.template ro<self_healing_open::ChosenNode>(
145 Tables::SELF_HEALING_OPEN_CHOSEN_NODE);
146 if (chosen_replica->get().has_value())
147 {
148 return ErrorDetails{
149 .status = HTTP_STATUS_INTERNAL_SERVER_ERROR,
150 .code = ccf::errors::InternalError,
151 .msg = fmt::format(
152 "This node has already voted for {}",
153 chosen_replica->get().value())};
154 }
155
156 auto gossip_handle = args.tx.template rw<self_healing_open::Gossips>(
157 Tables::SELF_HEALING_OPEN_GOSSIPS);
158 if (gossip_handle->get(in.info.identity.intrinsic_id).has_value())
159 {
161 "Node {} already gossiped, skipping", in.info.identity.intrinsic_id);
162 return std::nullopt;
163 }
164 gossip_handle->put(in.info.identity.intrinsic_id, in.txid);
165 return std::nullopt;
166 };
167 registry
169 "/self_healing_open/gossip",
170 HTTP_PUT,
171 json_adapter(wrap_self_healing_open<self_healing_open::GossipRequest>(
172 self_healing_open_gossip, node_context)),
173 no_auth_required)
175 .set_openapi_hidden(true)
176 .install();
177
178 auto self_healing_open_vote =
179 [](auto& args, self_healing_open::TaggedWithNodeInfo in)
180 -> std::optional<ErrorDetails> {
182 "Self-healing-open: receive vote from {}",
183 in.info.identity.intrinsic_id);
184
185 args.tx
186 .template rw<self_healing_open::Votes>(Tables::SELF_HEALING_OPEN_VOTES)
187 ->insert(in.info.identity.intrinsic_id);
188
189 return std::nullopt;
190 };
191 registry
193 "/self_healing_open/vote",
194 HTTP_PUT,
196 wrap_self_healing_open<self_healing_open::TaggedWithNodeInfo>(
197 self_healing_open_vote, node_context)),
198 no_auth_required)
200 .set_openapi_hidden(true)
201 .install();
202
203 auto self_healing_open_iamopen =
204 [](auto& args, self_healing_open::TaggedWithNodeInfo in)
205 -> std::optional<ErrorDetails> {
207 "Self-healing-open: receive IAmOpen from {}",
208 in.info.identity.intrinsic_id);
209 args.tx
210 .template rw<self_healing_open::SMState>(
211 Tables::SELF_HEALING_OPEN_SM_STATE)
213 args.tx
214 .template rw<self_healing_open::ChosenNode>(
215 Tables::SELF_HEALING_OPEN_CHOSEN_NODE)
216 ->put(in.info.identity.intrinsic_id);
217 return std::nullopt;
218 };
219 registry
221 "/self_healing_open/iamopen",
222 HTTP_PUT,
224 wrap_self_healing_open<self_healing_open::TaggedWithNodeInfo>(
225 self_healing_open_iamopen, node_context)),
226 no_auth_required)
228 .set_openapi_hidden(true)
229 .install();
230
231 auto self_healing_open_timeout =
232 [&](auto& args, const nlohmann::json& params) {
233 (void)params;
234 auto config = node_context.get_subsystem<NodeConfigurationSubsystem>();
235 auto node_operation =
236 node_context.get_subsystem<AbstractNodeOperation>();
237 if (config == nullptr || node_operation == nullptr)
238 {
239 return make_error(
240 HTTP_STATUS_BAD_REQUEST,
241 ccf::errors::InvalidNodeState,
242 "Unable to open self-healing-open subsystems");
243 }
244
245 if (!config->get().node_config.recover.self_healing_open.has_value())
246 {
247 return make_error(
248 HTTP_STATUS_BAD_REQUEST,
249 ccf::errors::InvalidNodeState,
250 "This node cannot do self-healing-open");
251 }
252
253 LOG_TRACE_FMT("Self-healing-open timeout received");
254
255 // Must ensure that the request originates from the primary
256 auto primary_id = node_operation->get_primary();
257 if (!primary_id.has_value())
258 {
259 LOG_FAIL_FMT("self-healing-open timeout: primary unknown");
260 return make_error(
261 HTTP_STATUS_INTERNAL_SERVER_ERROR,
262 ccf::errors::InternalError,
263 "Primary is unknown");
264 }
265 const auto& sig_auth_ident =
266 args.template get_caller<ccf::NodeCertAuthnIdentity>();
267 if (primary_id.value() != sig_auth_ident.node_id)
268 {
270 "self-healing-open timeout: request does not originate from "
271 "primary");
272 return make_error(
273 HTTP_STATUS_INTERNAL_SERVER_ERROR,
274 ccf::errors::InternalError,
275 "Request does not originate from primary.");
276 }
277
278 try
279 {
280 node_operation->self_healing_open().advance(args.tx, true);
281 }
282 catch (const std::logic_error& e)
283 {
285 "Self-healing-open gossip failed to advance state: {}", e.what());
286 return make_error(
287 HTTP_STATUS_INTERNAL_SERVER_ERROR,
288 ccf::errors::InternalError,
289 fmt::format(
290 "Failed to advance self-healing-open state: {}", e.what()));
291 }
292 return make_success("Self-healing-open timeout processed successfully");
293 };
294 registry
296 "/self_healing_open/timeout",
297 HTTP_PUT,
298 json_adapter(self_healing_open_timeout),
299 {std::make_shared<NodeCertAuthnPolicy>()})
300 .set_forwarding_required(endpoints::ForwardingRequired::Never)
301 .set_openapi_hidden(true)
302 .install();
303 }
304}
Definition node_operation_interface.h:24
Definition node_configuration_subsystem.h:13
Definition endpoint_registry.h:117
virtual Endpoint make_endpoint(const std::string &method, RESTVerb verb, const EndpointFunction &f, const AuthnPolicies &ap)
Definition endpoint_registry.cpp:204
M::Handle * rw(M &m)
Definition tx.h:211
Definition map.h:30
#define LOG_INFO_FMT
Definition internal_logger.h:15
#define LOG_TRACE_FMT
Definition internal_logger.h:13
#define LOG_FAIL_FMT
Definition internal_logger.h:16
std::vector< uint8_t > public_key_der_from_cert(const std::vector< uint8_t > &der)
Definition verifier.cpp:43
std::variant< ErrorDetails, RedirectDetails, AlreadyPopulatedResponse, nlohmann::json > JsonAdapterResponse
Definition json_handler.h:62
Definition file_serving_handlers.h:10
std::function< std::optional< ErrorDetails >(endpoints::EndpointContext &args, Input &in)> SelfHealingOpenHandler
Definition self_healing_open_handlers.h:19
jsonhandler::JsonAdapterResponse make_success()
Definition json_handler.cpp:108
std::function< jsonhandler::JsonAdapterResponse(endpoints::EndpointContext &ctx, nlohmann::json &&params)> HandlerJsonParamsAndForward
Definition json_handler.h:85
QuoteVerificationResult
Definition quote.h:18
endpoints::EndpointFunction json_adapter(const HandlerJsonParamsAndForward &f)
Definition json_handler.cpp:142
jsonhandler::JsonAdapterResponse make_error(ccf::http_status status, const std::string &code, const std::string &msg)
Definition json_handler.cpp:124
Definition node_context.h:12
std::shared_ptr< T > get_subsystem(const std::string &name) const
Definition node_context.h:37
Definition odata_error.h:58
http_status status
Definition odata_error.h:59
std::shared_ptr< ccf::RpcContext > rpc_ctx
Definition endpoint_context.h:31
Definition endpoint_context.h:55
ccf::kv::Tx & tx
Definition endpoint_context.h:61
Endpoint & set_openapi_hidden(bool hidden)
Definition endpoint.cpp:10
void install()
Definition endpoint.cpp:135
Endpoint & set_forwarding_required(ForwardingRequired fr)
Definition endpoint.cpp:74
Definition measurement.h:123
Definition self_healing_open_impl.h:32
Definition self_healing_open.h:30
ccf::QuoteInfo quote_info
Definition self_healing_open.h:31
Definition self_healing_open_impl.h:14