Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
f7676b3
WIP: Adding redirection strategies. per-operation
eddyashton Jan 15, 2024
c83b2e9
Missing includes
eddyashton Jan 15, 2024
1150e42
Match Python's default interface name
eddyashton Jan 15, 2024
aecc9e8
Sketch initial implementation of location lookup
eddyashton Jan 15, 2024
db0e851
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Jan 15, 2024
3375ba7
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Jan 19, 2024
d423290
Sketch in the startup config
eddyashton Jan 19, 2024
d15e39f
By default, commands don't need to be redirected
eddyashton Jan 19, 2024
836defc
Maybe temporary - turn off forwarding, and derive redirection from fo…
eddyashton Jan 19, 2024
e4d499b
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Jan 19, 2024
9446364
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Jan 24, 2024
35454e5
Skip redirecting when no consensus, with a comment
eddyashton Jan 24, 2024
dd3b6f9
Skip forwarding tests, to be replaced later
eddyashton Jan 24, 2024
ddb5cdf
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 5, 2024
6cfc7bc
Rebase
eddyashton Mar 5, 2024
10e2fd7
Document new config options in host schema
eddyashton Mar 5, 2024
ee77aa6
Nicer logging of redirect responses
eddyashton Mar 5, 2024
167079f
Basic e2e test
eddyashton Mar 5, 2024
2d49289
Plumbing for basic test of static redirects
eddyashton Mar 6, 2024
3bd010f
Allow temporary redirects on join
eddyashton Mar 6, 2024
6e9905c
Add a new temporary job
eddyashton Mar 6, 2024
479aa04
Clarify TODO, remove other tests for now
eddyashton Mar 6, 2024
14ae89e
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 6, 2024
04fefe8
Formatting
eddyashton Mar 6, 2024
c0bed97
Follow redirects in submit_recovery_share.sh
eddyashton Mar 6, 2024
1b2b030
Schema update
eddyashton Mar 6, 2024
b99eb42
Disabling forwarding tests with test_reqs
eddyashton Mar 6, 2024
33e9032
Work around lost session consistency
eddyashton Mar 6, 2024
8247287
Bootstrap redirection from forwarding, for the sake of external_executor
eddyashton Mar 6, 2024
a75bbef
Updated error code
eddyashton Mar 7, 2024
c6c6ebe
Handle redirects manually, in our Python layer
eddyashton Mar 7, 2024
445f6fc
Formatting
eddyashton Mar 7, 2024
3809e1b
Do gov via the primary
eddyashton Mar 8, 2024
87e7641
Copy cose_header_builder, update comment
eddyashton Mar 8, 2024
a17e560
Format
eddyashton Mar 8, 2024
fad4d99
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 8, 2024
f192c7f
Redirect in Python, so we also affect old versions of the script
eddyashton Mar 8, 2024
f27da90
Upstream branch fix
eddyashton Mar 11, 2024
a3b24b4
Restore build, reduce redirect test scope
eddyashton Mar 11, 2024
5c69b93
Debug redirects build, to avoid LONG_TESTS
eddyashton Mar 11, 2024
cd77853
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 11, 2024
7ade333
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 13, 2024
793b7dc
Explicit role-redirect test
eddyashton Mar 13, 2024
beba9c5
Add minimal sample configs showing redirection config
eddyashton Mar 13, 2024
2782153
Make redirects purely runtime configured
eddyashton Mar 13, 2024
486bbee
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 14, 2024
6ba30c8
Lint fixes
eddyashton Mar 15, 2024
94e5571
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 15, 2024
9fb9233
Merge branch 'main' into add_redirection_strategies
achamayou Mar 18, 2024
a68c144
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 18, 2024
dbe32ce
Remove redirects job
eddyashton Mar 19, 2024
6ad87d5
First pass at docs + CHANGELOG
eddyashton Mar 20, 2024
48c84a8
Link to docs from CHANGELOG
eddyashton Mar 20, 2024
4597ad8
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 20, 2024
c1e74f0
Merge branch 'main' into add_redirection_strategies
achamayou Mar 22, 2024
c6c1e73
Weird
eddyashton Mar 25, 2024
4c50104
Merge branch 'main' of github.com:microsoft/CCF into add_redirection_…
eddyashton Mar 25, 2024
be06c21
Merge branch 'add_redirection_strategies' of github.com:eddyashton/CC…
eddyashton Mar 25, 2024
b68fd43
Merge branch 'main' into add_redirection_strategies
eddyashton Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [5.0.0-dev15]

[5.0.0-dev15]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev15

### Added

- CCF now supports a mode where HTTP redirect responses are returned, rather than relying on internal forwarding. This can be used to ensure that write requests are executed on a primary, even if they are initially sent to a backup. This behaviour is enabled by setting the `redirections` field in an `rpc_interface` within cchost's launch config. This can be configured to redirect either directly to a node (if each node has a distinct, accessible name), or to a static load balancer address, depending on the current deployment. See docs for description of [redirection behaviour](https://microsoft.github.io/CCF/main/architecture/request_flow.html#redirection-flow) and [configuration](https://microsoft.github.io/CCF/main/operations/configuration.html#redirections).

## [5.0.0-dev14]

[5.0.0-dev14]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev14
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1378,12 +1378,17 @@ if(BUILD_TESTS)
${CMAKE_SOURCE_DIR}/samples/apps/logging/js
)

# This test uses large requests (so too slow for SAN)
if(NOT SAN)
add_e2e_test(
NAME e2e_limits PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/limits.py
)
endif()

add_e2e_test(
NAME e2e_redirects PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/redirects.py
)

add_e2e_test(
NAME e2e_logging_http2
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/e2e_logging.py
Expand Down
59 changes: 59 additions & 0 deletions doc/architecture/request_flow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,65 @@ When follower A receives the forwarded response, it writes this to the TLS sessi

NetStackA-->>User: 200 OK "Copied {a} from {A} to {B}"

Redirection flow
----------------

CCF supports HTTP redirections as an alternative to forwarding. When a request arrives that cannot be executed locally, rather than forwarding it to an appropriate node over the node-to-node channels, the node can return a HTTP redirect response advising the caller to resubmit the request directly to that node. This uses standard HTTP semantics, reporting the redirect target in a ``Location`` header. Most HTTP clients will have an option to follow this redirect automatically, and all should have an option to enable this behaviour if desired. Alternatively, client applications may choose to intercept this redirect response and manually interpret it, perhaps to alter the resubmitted request or to update the target node for future requests.

.. warning:: Many HTTP clients will strip out ``Authorization`` headers when following Cross-Origin redirects. This means that if your client is automatically following redirects, and you submit a request with a JWT token as authorization, if you are redirected you may see a surprising authorization failure. In this scenario we recommend intercepting the redirect responses manually, so that the request can be resubmitted without stripping headers.

Similar to forwarding, the redirect behaviour is partly controlled by per-endpoint metadata, so the initially receiving node must parse the request and go through endpoint dispatch before making a forwarding decision.

There are currently 2 supported modes for redirections. In the first, the response sends the user directly to the suggested node. This will only work if that node has an accessible name, which can be included in the ``Location`` header and accessed by the user.

.. mermaid::

sequenceDiagram
autonumber
participant U as User
participant B as Backup (nodeA.ccf.com)
participant P as Primary (nodeB.ccf.com)

U->>B: POST /copy/A/B
B->>B: Lookup endpoint
B->>B: Decide request should be redirected
B->>B: Build redirect response
B-->>U: 307 REDIRECT Location: nodeB.ccf.com/copy/A/B

U->>P: POST /copy/A/B
P->>P: Lookup endpoint
P->>P: Decide request can be executed
P->>P: Execute request
P-->>U: 200 OK "Copied {a} from {A} to {B}"

For deployments where nodes are not directly accessible, redirections can still be supported via multiple load balancers. All that is required is `a` public name for each redirect purpose, with up-to-date balancing to the correct nodes. More simply, that currently means maintaining a `write` load balancer which can direct external traffic to a primary.

.. mermaid::

sequenceDiagram
autonumber
participant U as User
participant LB as General LB (service.ccf.com)
participant B as Backup
participant WLB as Write LB (write.service.ccf.com)
participant P as Primary

U->>LB: POST /copy/A/B
LB->>B: POST /copy/A/B
B->>B: Lookup endpoint
B->>B: Decide request should be redirected
B->>B: Build redirect response
B-->>U: 307 REDIRECT Location: write.service.ccf.com/copy/A/B

U->>WLB: POST /copy/A/B
WLB->>P: POST /copy/A/B
P->>P: Lookup endpoint
P->>P: Decide request can be executed
P->>P: Execute request
P-->>U: 200 OK "Copied {a} from {A} to {B}"

To use redirection behaviour, and choose whether to redirect to a node or a load balancer, set the ``redirections`` field in the :doc:`cchost launch configuration </operations/configuration>`.

External executor flow
----------------------

Expand Down
62 changes: 61 additions & 1 deletion doc/host_config_schema/cchost_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@
"type": "integer",
"default": 3000,
"description": "Timeout for forwarded RPC calls (in milliseconds)"
},
"redirections": {
"type": "object",
"description": "Configure how redirect responses should be produced on this interface. If this is omitted, then forwarding will be used instead",
"properties": {
"to_primary": {
"$ref": "#/$defs/RedirectionResolver",
"description": "Configures how the Location header should be populated, when requests arrive on this interface that must be served by a primary while the receiving node is not a primary"
}
},
"additionalProperties": false
}
},
"required": ["bind_address"]
Expand Down Expand Up @@ -687,5 +698,54 @@
}
},
"required": ["enclave", "network", "command"],
"additionalProperties": false
"additionalProperties": false,
"$defs": {
"RedirectionResolver": {
"type": "object",
"properties": {
"kind": {
"enum": ["NodeByRole", "StaticAddress"]
},
"target": {}
},
"required": ["kind"],
"if": {
"properties": {
"kind": {
"const": "NodeByRole"
}
}
},
"then": {
"properties": {
"target": {
"type": "object",
"properties": {
"role": {
"enum": ["primary"],
"default": "primary"
}
},
"additionalProperties": false
}
}
},
"else": {
"properties": {
"target": {
"type": "object",
"properties": {
"address": {
"type": "string"
}
},
"required": ["address"],
"additionalProperties": false
}
},
"required": ["target"]
},
"additionalProperties": false
}
}
}
44 changes: 43 additions & 1 deletion doc/schemas/gov_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,27 @@
},
"published_address": {
"type": "string"
},
"redirections": {
"properties": {
"to_primary": {
"properties": {
"kind": {
"enum": [
"NodeByRole",
"StaticAddress"
],
"type": "string"
},
"target": {}
},
"required": [
"kind"
],
"type": "object"
}
},
"type": "object"
}
},
"required": [
Expand Down Expand Up @@ -788,6 +809,27 @@
},
"published_address": {
"type": "string"
},
"redirections": {
"properties": {
"to_primary": {
"properties": {
"kind": {
"enum": [
"NodeByRole",
"StaticAddress"
],
"type": "string"
},
"target": {}
},
"required": [
"kind"
],
"type": "object"
}
},
"type": "object"
}
},
"required": [
Expand Down Expand Up @@ -1283,7 +1325,7 @@
"info": {
"description": "This API is used to submit and query proposals which affect CCF's public governance tables.",
"title": "CCF Governance API",
"version": "4.1.3"
"version": "4.1.4"
},
"openapi": "3.0.0",
"paths": {
Expand Down
34 changes: 33 additions & 1 deletion doc/schemas/node_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -584,13 +584,24 @@
},
"published_address": {
"$ref": "#/components/schemas/string"
},
"redirections": {
"$ref": "#/components/schemas/NodeInfoNetwork_v2__NetInterface__Redirections"
}
},
"required": [
"bind_address"
],
"type": "object"
},
"NodeInfoNetwork_v2__NetInterface__Redirections": {
"properties": {
"to_primary": {
"$ref": "#/components/schemas/RedirectionResolverConfig"
}
},
"type": "object"
},
"NodeMetrics": {
"properties": {
"sessions": {
Expand Down Expand Up @@ -686,6 +697,27 @@
],
"type": "string"
},
"RedirectionResolutionKind": {
"enum": [
"NodeByRole",
"StaticAddress"
],
"type": "string"
},
"RedirectionResolverConfig": {
"properties": {
"kind": {
"$ref": "#/components/schemas/RedirectionResolutionKind"
},
"target": {
"$ref": "#/components/schemas/json"
}
},
"required": [
"kind"
],
"type": "object"
},
"RetirementPhase": {
"enum": [
"Ordered",
Expand Down Expand Up @@ -905,7 +937,7 @@
"info": {
"description": "This API provides public, uncredentialed access to service and node state.",
"title": "CCF Public Node API",
"version": "4.9.0"
"version": "4.9.1"
},
"openapi": "3.0.0",
"paths": {
Expand Down
23 changes: 23 additions & 0 deletions include/ccf/endpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ namespace ccf::endpoints
Never
};

enum class RedirectionStrategy
{
/** This operation does not need to be redirected, and can be executed on
the receiving node. Most read-only operations can be executed on any
node, so should be marked as None. */
None,

/** This operation must be executed on a primary. If the current node is not
a primary, it should attempt to redirect to the primary, or else return
an error. Any write operations must be executed on a primary, so should
be marked as ToPrimary. */
ToPrimary
};

enum class Mode
{
ReadWrite,
Expand All @@ -77,6 +91,11 @@ namespace ccf::endpoints
{ForwardingRequired::Always, "always"},
{ForwardingRequired::Never, "never"}});

DECLARE_JSON_ENUM(
RedirectionStrategy,
{{RedirectionStrategy::None, "none"},
{RedirectionStrategy::ToPrimary, "to_primary"}});

DECLARE_JSON_ENUM(
Mode,
{{Mode::ReadWrite, "readwrite"},
Expand Down Expand Up @@ -106,6 +125,8 @@ namespace ccf::endpoints
Mode mode = Mode::ReadWrite;
/// Endpoint forwarding policy
ForwardingRequired forwarding_required = ForwardingRequired::Always;
/// Endpoint redirection policy
RedirectionStrategy redirection_strategy = RedirectionStrategy::None;
/// Authentication policies
std::vector<std::string> authn_policies = {};
/// OpenAPI schema for endpoint
Expand Down Expand Up @@ -397,6 +418,8 @@ namespace ccf::endpoints
*/
Endpoint& set_forwarding_required(ForwardingRequired fr);

Endpoint& set_redirection_strategy(RedirectionStrategy rs);

void install();
};

Expand Down
Loading