-
Notifications
You must be signed in to change notification settings - Fork 41
feat: Shielding support #1241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: Shielding support #1241
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
85e3b22
Initial shielding work
TartanLlama 94357a6
Docs and testing
TartanLlama add9018
Correct docs
TartanLlama ef63e56
Correct docs
TartanLlama 5fca6ae
Cleanup
TartanLlama 1756685
Correct test
TartanLlama 65bdb43
Format
TartanLlama 1eb3956
Merge branch 'main' into sy/shielding
TartanLlama 426f88e
Merge branch 'main' into sy/shielding
TartanLlama bcfef66
Merge branch 'main' into sy/shielding
TartanLlama f348319
Merge branch 'main' into sy/shielding
TartanLlama 95f3779
Merge branch 'main' into sy/shielding
TartanLlama 106aa24
Remove test
TartanLlama File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| --- | ||
| hide_title: false | ||
| hide_table_of_contents: false | ||
| pagination_next: null | ||
| pagination_prev: null | ||
| --- | ||
| # `Shield()` | ||
|
|
||
| Load information about the given shield. | ||
|
|
||
| Returns an object representing the shield if it is active, or throws an exception if the string is malformed or the shield doesn’t exist. | ||
|
|
||
| Shield names are defined on [this webpage](https://www.fastly.com/documentation/guides/concepts/shielding/#shield-locations), in the “shield code” column. For example, the string “pdx-or-us” will look up our Portland, OR, USA shield site, while “paris-fr” will look up our Paris site. | ||
|
|
||
| If you are using a major cloud provider for your primary origin site, consider looking at the “Recommended for” column, to find the Fastly POP most closely located to the given cloud provider. | ||
|
|
||
| ## Syntax | ||
|
|
||
| ```js | ||
| new Shield(name) | ||
| ``` | ||
|
|
||
| > **Note:** `Shield()` can only be constructed with `new`. Attempting to call it without `new` throws a [`TypeError`](../../globals/TypeError/TypeError.mdx). | ||
|
|
||
| ### Exceptions | ||
|
|
||
| - `TypeError` | ||
| - Thrown if no Shield exists with the provided name | ||
|
|
||
| ## Examples | ||
|
|
||
| In this example, we create a Shield instance for the Sydney, Australia shield POP. If the code is running on that shield POP, it fetches directly from origin. Otherwise, it routes the request through the shield using an encrypted connection. | ||
|
|
||
| ```js | ||
| /// <reference types="@fastly/js-compute" /> | ||
|
|
||
| import { Shield } from "fastly:shielding"; | ||
|
|
||
| async function app(event) { | ||
| const shield = new Shield('wsi-australia-au'); | ||
| // If running on the shield POP, fetch from the origin directly | ||
| if (shield.runningOn()) { | ||
| return await fetch('https://http-me.fastly.com/anything', { backend: 'httpme' }); | ||
| } | ||
| // Otherwise, route the request through the shield using an encrypted connection | ||
| return await fetch(event.request, { backend: shield.encryptedBackend() }); | ||
| } | ||
|
|
||
| addEventListener("fetch", (event) => event.respondWith(app(event))) | ||
| ``` |
13 changes: 13 additions & 0 deletions
13
documentation/docs/shielding/Shield/prototype/encryptedBackend.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| hide_title: false | ||
| hide_table_of_contents: false | ||
| pagination_next: null | ||
| pagination_prev: null | ||
| --- | ||
| # Shield.prototype.encryptedBackend | ||
|
|
||
| ▸ **encryptedBackend**(): `Backend` | ||
|
|
||
| Returns a `Backend` representing an encrypted connection to the POP. | ||
|
|
||
| For reference, this is almost always the backend that you want to use. Only use [`Shield::unencryptedBackend`](./unencryptedBackend.mdx) in situations in which you are 100% sure that all the data you will send and receive over the backend is already encrypted. |
15 changes: 15 additions & 0 deletions
15
documentation/docs/shielding/Shield/prototype/runningOn.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| hide_title: false | ||
| hide_table_of_contents: false | ||
| pagination_next: null | ||
| pagination_prev: null | ||
| --- | ||
| # Shield.prototype.runningOn | ||
|
|
||
| ▸ **runningOn**(): `boolean` | ||
|
|
||
| Returns whether we are currently operating on the given shield. | ||
|
|
||
| Technically, this may also return true in very isolated incidents in which Fastly is routing traffic from the target shield POP to the POP that this code is running on, but in these situations the results should be approximately identical. | ||
|
|
||
| (For example, it may be the case that you are asking to shield to ‘pdx-or-us’. But, for load balancing, performance, or other reasons, Fastly is temporarily shifting shielding traffic from Portland to Seattle. In that case, this function may return true for hosts running on ‘bfi-wa-us’, our Seattle site, because effectively the shield has moved to that location. This should give you a slightly faster experience than the alternative, in which this function would return false, you would try to forward your traffic to the Portland site, and then that traffic would be caught and redirected back to Seattle.) |
13 changes: 13 additions & 0 deletions
13
documentation/docs/shielding/Shield/prototype/unencryptedBackend.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| hide_title: false | ||
| hide_table_of_contents: false | ||
| pagination_next: null | ||
| pagination_prev: null | ||
| --- | ||
| # Shield.prototype.unencryptedBackend | ||
|
|
||
| ▸ **unencryptedBackend**(): `Backend` | ||
|
|
||
| Returns a `Backend` representing an unencrypted connection to the POP. | ||
|
|
||
| Generally speaking, we encourage users to use [`Shield::encryptedBackend`](./encryptedBackend.mdx) instead of this function. Data sent over this backend – the unencrypted version – will be sent over the open internet, with no protections. In most cases, this is not what you want. However, in some cases – such as when you want to ship large data blobs that you know are already encrypted — using these backends can prevent a double-encryption performance penalty. |
7 changes: 7 additions & 0 deletions
7
integration-tests/js-compute/fixtures/app/__input_bundled.js.map
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { assert, assertThrows } from './assertions.js'; | ||
| import { routes } from './routes.js'; | ||
| import { Shield } from 'fastly:shielding'; | ||
|
|
||
| routes.set('/shielding/invalid-shield', () => { | ||
| assertThrows(() => new Shield('i-am-not-a-real-shield')); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| #include "shielding.h" | ||
| #include "../../../StarlingMonkey/runtime/encode.h" | ||
| #include "../common/validations.h" | ||
| #include "../host-api/host_api_fastly.h" | ||
| #include "backend.h" | ||
| #include "fastly.h" | ||
|
|
||
| namespace fastly::shielding { | ||
| const JSFunctionSpec Shield::static_methods[] = { | ||
| JS_FS_END, | ||
| }; | ||
|
|
||
| const JSPropertySpec Shield::static_properties[] = { | ||
| JS_PS_END, | ||
| }; | ||
|
|
||
| const JSFunctionSpec Shield::methods[] = { | ||
| JS_FN("runningOn", runningOn, 0, JSPROP_ENUMERATE), | ||
| JS_FN("unencryptedBackend", unencryptedBackend, 0, JSPROP_ENUMERATE), | ||
| JS_FN("encryptedBackend", encryptedBackend, 0, JSPROP_ENUMERATE), JS_FS_END}; | ||
|
|
||
| const JSPropertySpec Shield::properties[] = {JS_PS_END}; | ||
|
|
||
| bool Shield::runningOn(JSContext *cx, unsigned argc, JS::Value *vp) { | ||
| METHOD_HEADER(0); | ||
| bool is_me = JS::GetReservedSlot(self, static_cast<uint32_t>(Slots::IsMe)).toBoolean(); | ||
| args.rval().setBoolean(is_me); | ||
| return true; | ||
| } | ||
| bool Shield::unencryptedBackend(JSContext *cx, unsigned argc, JS::Value *vp) { | ||
| METHOD_HEADER(0) | ||
| JS::RootedString target( | ||
| cx, JS::GetReservedSlot(self, static_cast<uint32_t>(Slots::PlainTarget)).toString()); | ||
| return backend_for_shield(cx, target, args.rval()); | ||
| } | ||
| bool Shield::encryptedBackend(JSContext *cx, unsigned argc, JS::Value *vp) { | ||
| METHOD_HEADER(0) | ||
| JS::RootedString target( | ||
| cx, JS::GetReservedSlot(self, static_cast<uint32_t>(Slots::SSLTarget)).toString()); | ||
| return backend_for_shield(cx, target, args.rval()); | ||
| } | ||
| bool Shield::backend_for_shield(JSContext *cx, JS::HandleString target, | ||
| JS::MutableHandleValue rval) { | ||
| auto name = core::encode(cx, target); | ||
| fastly_shielding_shield_backend_config config{nullptr, 0, 0}; | ||
| auto options_mask = 0; | ||
| std::uint32_t backend_name_size_out = 0; | ||
| constexpr std::size_t max_backend_name_size = 1024; | ||
| std::string backend_name_out(max_backend_name_size, 0); | ||
| auto status = fastly_shielding_backend_for_shield(name.ptr.get(), name.len, options_mask, &config, | ||
| backend_name_out.data(), max_backend_name_size, | ||
| &backend_name_size_out); | ||
| if (status != 0) { | ||
| HANDLE_ERROR(cx, status); | ||
| return false; | ||
| } | ||
| backend_name_out.resize(backend_name_size_out); | ||
| host_api::HostString backend_name(backend_name_out); | ||
| return backend::Backend::get_from_valid_name(cx, std::move(backend_name), rval); | ||
| } | ||
|
|
||
| bool Shield::constructor(JSContext *cx, unsigned argc, JS::Value *vp) { | ||
| REQUEST_HANDLER_ONLY("The Shield builtin"); | ||
| CTOR_HEADER("Shield", 1); | ||
|
|
||
| JS::HandleValue name_arg = args.get(0); | ||
| auto name = core::encode(cx, name_arg); | ||
|
|
||
| // Keep calling fastly_shielding_shield_info with a increasingly large buffer until it returns OK | ||
| std::uint32_t buf_size = 1024; | ||
| std::vector<char> out_buf(buf_size); | ||
| while (true) { | ||
| std::uint32_t used_amount = 0; | ||
| auto status = fastly_shielding_shield_info(name.ptr.get(), name.len, out_buf.data(), buf_size, | ||
| &used_amount); | ||
| if (status == 0) { | ||
| out_buf.resize(used_amount); | ||
| break; | ||
| } else if (status == FASTLY_HOST_ERROR_BUFFER_LEN) { | ||
| buf_size *= 2; | ||
| out_buf = std::vector<char>(1024); | ||
| } else { | ||
| HANDLE_ERROR(cx, status); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| if (out_buf.size() < 3) { | ||
| return false; | ||
| } | ||
|
|
||
| JS::RootedObject self(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj)); | ||
|
|
||
| bool is_me = out_buf[0] != 0; | ||
| JS_SetReservedSlot(self, static_cast<uint32_t>(Slots::IsMe), JS::BooleanValue(is_me)); | ||
| JS_SetReservedSlot(self, static_cast<uint32_t>(Slots::PlainTarget), | ||
| JS::StringValue(JS_NewStringCopyZ(cx, out_buf.data() + 1))); | ||
| auto plain_bytes_end = std::find(begin(out_buf) + 1, end(out_buf), 0); | ||
| JS_SetReservedSlot(self, static_cast<uint32_t>(Slots::SSLTarget), | ||
| JS::StringValue(JS_NewStringCopyZ(cx, &*plain_bytes_end + 1))); | ||
|
|
||
| args.rval().setObject(*self); | ||
| return true; | ||
| } | ||
|
|
||
| bool install(api::Engine *engine) { | ||
| RootedObject shielding_ns(engine->cx(), JS_NewObject(engine->cx(), nullptr)); | ||
| if (!Shield::init_class_impl(engine->cx(), shielding_ns)) { | ||
| return false; | ||
| } | ||
| RootedObject shield_obj(engine->cx(), JS_GetConstructor(engine->cx(), Shield::proto_obj)); | ||
| RootedValue shield_val(engine->cx(), ObjectValue(*shield_obj)); | ||
| if (!JS_SetProperty(engine->cx(), shielding_ns, "Shield", shield_val)) { | ||
| return false; | ||
| } | ||
|
|
||
| RootedValue shielding_ns_val(engine->cx(), JS::ObjectValue(*shielding_ns)); | ||
| if (!engine->define_builtin_module("fastly:shielding", shielding_ns_val)) { | ||
| return false; | ||
| } | ||
|
|
||
| RootedObject fastly(engine->cx()); | ||
| if (!fastly::get_fastly_object(engine, &fastly)) { | ||
| return false; | ||
| } | ||
| if (!JS_SetProperty(engine->cx(), fastly, "shielding", shielding_ns_val)) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| } // namespace fastly::shielding | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #ifndef FASTLY_SHIELDING_H | ||
| #define FASTLY_SHIELDING_H | ||
|
|
||
| #include "../host-api/host_api_fastly.h" | ||
| #include "builtin.h" | ||
| #include "extension-api.h" | ||
|
|
||
| namespace fastly::shielding { | ||
|
|
||
| class Shield : public builtins::BuiltinImpl<Shield> { | ||
| private: | ||
| static bool backend_for_shield(JSContext *cx, JS::HandleString target, | ||
| JS::MutableHandleValue rval); | ||
|
|
||
| public: | ||
| static constexpr const char *class_name = "Shield"; | ||
| static const int ctor_length = 0; | ||
| enum Slots { IsMe, PlainTarget, SSLTarget, Count }; | ||
| static const JSFunctionSpec static_methods[]; | ||
| static const JSPropertySpec static_properties[]; | ||
| static const JSFunctionSpec methods[]; | ||
| static const JSPropertySpec properties[]; | ||
|
|
||
| static bool runningOn(JSContext *cx, unsigned argc, JS::Value *vp); | ||
| static bool unencryptedBackend(JSContext *cx, unsigned argc, JS::Value *vp); | ||
| static bool encryptedBackend(JSContext *cx, unsigned argc, JS::Value *vp); | ||
|
|
||
| static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp); | ||
| }; | ||
|
|
||
| } // namespace fastly::shielding | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { Backend } from 'fastly:backend'; | ||
|
|
||
| declare module 'fastly:shielding' { | ||
| export class Shield { | ||
| constructor(name: string); | ||
| runningOn(): boolean; | ||
| unencryptedBackend(): Backend; | ||
| encryptedBackend(): Backend; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't
FASTLY_HOST_ERROR_BUFFER_LENusually setbuf_sizeto the expectedbuf_size, so you don't have to keep retrying like this?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually, yes, not in the case of this host call it seems. I'm following the Rust SDK behaviour fwiw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/fastly/fst-compute-sdk-rs/blob/fe621bae505f843d55fa99afae186f453c847aee/fastly/src/shielding.rs#L127
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apparently it doesn't. Absolutely rude.