Skip to content

Commit bef80da

Browse files
eddyashtonCopilotachamayou
authored
Disable snapshot read endpoints by-default, require a per-interface opt-in to enable new OperatorFeature (#7440)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Amaury Chamayou <amchamay@microsoft.com>
1 parent 4d55e14 commit bef80da

35 files changed

+878
-694
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
3333

3434
- CheckQuorum now requires a quorum in every configuration (#7375)
3535

36+
### Changed
37+
38+
- The snapshot-serving endpoints required for `fetch_recent_snapshot` behaviour are now disabled-by-default to avoid public DoS requests. They should be enabled on a per-interface basis by adding `"enabled_operator_features": ["SnapshotRead"]` to the interface's configuration, on an interface with local visibility used for node-to-node join requests.
39+
3640
## [7.0.0-dev4]
3741

3842
[7.0.0-dev4]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.0-dev4

doc/host_config_schema/cchost_config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@
134134
}
135135
},
136136
"additionalProperties": false
137+
},
138+
"enabled_operator_features": {
139+
"type": "array",
140+
"items": {
141+
"enum": ["SnapshotRead"],
142+
"type": "string"
143+
},
144+
"description": "An array of features which should be enabled on this interface, providing access to endpoints with specific security or performance constraints. The only feature currently supported is 'SnapshotRead', which gates access to the /snapshot/* endpoints used to fetch snapshots directly from nodes. Since these require disk IO and produce large responses, this feature should not be enabled on interfaces with public access, and instead restricted to interfaces with local connectivity for node-to-node and operator access."
137145
}
138146
},
139147
"required": ["bind_address"]

doc/schemas/node_openapi.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,9 @@
526526
"bind_address": {
527527
"$ref": "#/components/schemas/string"
528528
},
529+
"enabled_operator_features": {
530+
"$ref": "#/components/schemas/OperatorFeature_set"
531+
},
529532
"endorsement": {
530533
"$ref": "#/components/schemas/Endorsement"
531534
},
@@ -586,6 +589,18 @@
586589
],
587590
"type": "string"
588591
},
592+
"OperatorFeature": {
593+
"enum": [
594+
"SnapshotRead"
595+
],
596+
"type": "string"
597+
},
598+
"OperatorFeature_set": {
599+
"items": {
600+
"$ref": "#/components/schemas/OperatorFeature"
601+
},
602+
"type": "array"
603+
},
589604
"ParserConfiguration": {
590605
"properties": {
591606
"initial_window_size": {

include/ccf/endpoint.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "ccf/http_consts.h"
99
#include "ccf/rest_verb.h"
1010
#include "ccf/service/map.h"
11+
#include "ccf/service/operator_feature.h"
1112

1213
#include <string>
1314
#include <utility>
@@ -232,6 +233,8 @@ namespace ccf::endpoints
232233
* @see ccf::any_cert_auth_policy
233234
*/
234235
AuthnPolicies authn_policies;
236+
237+
std::set<OperatorFeature> required_operator_features;
235238
};
236239

237240
using EndpointDefinitionPtr = std::shared_ptr<const EndpointDefinition>;
@@ -312,6 +315,14 @@ namespace ccf::endpoints
312315
*/
313316
Endpoint& set_openapi_hidden(bool hidden);
314317

318+
/** Add an opt-in feature which this endpoint uses. The endpoint will only
319+
* be available on interfaces which have opted in to enabling all required
320+
* features.
321+
*
322+
* @return This Endpoint for further modification
323+
*/
324+
Endpoint& require_operator_feature(OperatorFeature feature);
325+
315326
/** Sets the JSON schema that the request parameters must comply with.
316327
*
317328
* @param j Request parameters JSON schema

include/ccf/service/node_info_network.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "ccf/ds/json.h"
77
#include "ccf/ds/nonstd.h"
88
#include "ccf/http_configuration.h"
9+
#include "ccf/service/operator_feature.h"
910

1011
#include <string>
1112

@@ -109,6 +110,11 @@ namespace ccf
109110
/// Timeout for forwarded RPC calls (in milliseconds)
110111
std::optional<size_t> forwarding_timeout_ms = std::nullopt;
111112

113+
/// Features enabled for this interface. Any endpoint with required
114+
/// features will be inaccessible (on this interface) if this does not
115+
/// contain those features.
116+
std::set<ccf::endpoints::OperatorFeature> enabled_operator_features;
117+
112118
struct Redirections
113119
{
114120
RedirectionResolverConfig to_primary;
@@ -130,6 +136,7 @@ namespace ccf
130136
http_configuration == other.http_configuration &&
131137
accepted_endpoints == other.accepted_endpoints &&
132138
forwarding_timeout_ms == other.forwarding_timeout_ms &&
139+
enabled_operator_features == other.enabled_operator_features &&
133140
redirections == other.redirections;
134141
}
135142
};
@@ -165,6 +172,7 @@ namespace ccf
165172
http_configuration,
166173
accepted_endpoints,
167174
forwarding_timeout_ms,
175+
enabled_operator_features,
168176
redirections);
169177
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(NodeInfoNetwork_v2);
170178
DECLARE_JSON_REQUIRED_FIELDS(
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
#pragma once
4+
5+
#include "ccf/ds/json.h"
6+
7+
namespace ccf::endpoints
8+
{
9+
enum class OperatorFeature : uint8_t
10+
{
11+
SnapshotRead,
12+
};
13+
14+
DECLARE_JSON_ENUM(
15+
OperatorFeature,
16+
{
17+
{OperatorFeature::SnapshotRead, "SnapshotRead"},
18+
});
19+
}

src/ds/pending_io.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ struct PendingIO
7979
*/
8080
static void clear_empty(std::vector<PendingIO<T>>& list)
8181
{
82-
std::remove_if(
83-
list.begin(), list.end(), [](PendingIO<T>& p) { return p.clear; });
82+
list.erase(
83+
std::remove_if(
84+
list.begin(), list.end(), [](PendingIO<T>& p) { return p.clear; }),
85+
list.end());
8486
}
8587
};

src/endpoints/endpoint.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ namespace ccf::endpoints
1313
return *this;
1414
}
1515

16+
Endpoint& Endpoint::require_operator_feature(OperatorFeature feature)
17+
{
18+
required_operator_features.insert(feature);
19+
return *this;
20+
}
21+
1622
Endpoint& Endpoint::set_params_schema(const nlohmann::json& j)
1723
{
1824
params_schema = j;

src/node/node_state.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,11 @@ namespace ccf
17671767
consensus->can_replicate());
17681768
}
17691769

1770+
std::optional<ccf::NodeId> get_primary() override
1771+
{
1772+
return consensus->primary();
1773+
}
1774+
17701775
bool is_in_initialised_state() const override
17711776
{
17721777
return sm.check(NodeStartupState::initialized);

0 commit comments

Comments
 (0)