-
Notifications
You must be signed in to change notification settings - Fork 71
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
Implement a capability restriction system. #89
Changes from 23 commits
7f0b181
0b28799
a3e393a
056b80a
bdc82eb
86eed01
cd44da9
5971335
8d24cf5
18a6367
a180877
26de5b9
ceaef93
a94857a
0ad3d42
254328b
57b6abb
a8d5b6c
18cf449
f37f375
d8ae296
228350f
bd262ef
ed8cbca
68a6cf0
980be06
7ef7473
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
#include <memory> | ||
|
||
#include "include/proxy-wasm/word.h" | ||
#include "include/proxy-wasm/wasm_vm.h" | ||
|
||
namespace proxy_wasm { | ||
|
||
|
@@ -57,7 +58,7 @@ template <typename Pairs> void marshalPairs(const Pairs &result, char *buffer) { | |
} | ||
} | ||
|
||
// ABI functions exported from envoy to wasm. | ||
// ABI functions exported from host to wasm. | ||
|
||
Word get_configuration(void *raw_context, Word address, Word size); | ||
Word get_status(void *raw_context, Word status_code, Word address, Word size); | ||
|
@@ -153,5 +154,51 @@ Word pthread_equal(void *, Word left, Word right); | |
// Any currently executing Wasm call context. | ||
::proxy_wasm::ContextBase *ContextOrEffectiveContext(::proxy_wasm::ContextBase *context); | ||
|
||
#define FOR_ALL_HOST_FUNCTIONS(_f) \ | ||
_f(log) _f(get_status) _f(set_property) _f(get_property) _f(send_local_response) \ | ||
_f(get_shared_data) _f(set_shared_data) _f(register_shared_queue) _f(resolve_shared_queue) \ | ||
_f(dequeue_shared_queue) _f(enqueue_shared_queue) _f(get_header_map_value) \ | ||
_f(add_header_map_value) _f(replace_header_map_value) _f(remove_header_map_value) \ | ||
_f(get_header_map_pairs) _f(set_header_map_pairs) _f(get_header_map_size) \ | ||
_f(get_buffer_status) _f(get_buffer_bytes) _f(set_buffer_bytes) \ | ||
_f(http_call) _f(grpc_call) _f(grpc_stream) _f(grpc_close) \ | ||
_f(grpc_cancel) _f(grpc_send) _f(set_tick_period_milliseconds) \ | ||
_f(get_current_time_nanoseconds) _f(define_metric) \ | ||
_f(increment_metric) _f(record_metric) _f(get_metric) \ | ||
_f(set_effective_context) _f(done) \ | ||
_f(call_foreign_function) | ||
|
||
#define FOR_ALL_HOST_FUNCTIONS_ABI_SPECIFIC(_f) \ | ||
_f(get_configuration) _f(continue_request) _f(continue_response) _f(clear_route_cache) \ | ||
_f(continue_stream) _f(close_stream) _f(get_log_level) | ||
|
||
#define FOR_ALL_WASI_FUNCTIONS(_f) \ | ||
_f(fd_write) _f(fd_read) _f(fd_seek) _f(fd_close) _f(fd_fdstat_get) _f(environ_get) \ | ||
_f(environ_sizes_get) _f(args_get) _f(args_sizes_get) _f(clock_time_get) _f(random_get) \ | ||
_f(proc_exit) | ||
|
||
// Helpers to generate a stub to pass to VM, in place of a restricted export. | ||
#define _CREATE_EXPORT_STUB(_fn) \ | ||
template <typename F> struct _fn##Stub; \ | ||
template <typename... Args> struct _fn##Stub<Word(void *, Args...)> { \ | ||
static Word stub(void *raw_context, Args...) { \ | ||
auto context = exports::ContextOrEffectiveContext( \ | ||
static_cast<ContextBase *>((void)raw_context, current_context_)); \ | ||
context->wasmVm()->integration()->error("Attempted call to restricted capability: " #_fn); \ | ||
return WasmResult::InternalFailure; \ | ||
} \ | ||
}; \ | ||
template <typename... Args> struct _fn##Stub<void(void *, Args...)> { \ | ||
static void stub(void *raw_context, Args...) { \ | ||
auto context = exports::ContextOrEffectiveContext( \ | ||
static_cast<ContextBase *>((void)raw_context, current_context_)); \ | ||
context->wasmVm()->integration()->error("Attempted call to restricted capability: " #_fn); \ | ||
} \ | ||
}; | ||
FOR_ALL_HOST_FUNCTIONS(_CREATE_EXPORT_STUB) | ||
FOR_ALL_HOST_FUNCTIONS_ABI_SPECIFIC(_CREATE_EXPORT_STUB) | ||
FOR_ALL_WASI_FUNCTIONS(_CREATE_EXPORT_STUB) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For WASI, I think it would be better if we had a dedicated stub that returns |
||
#undef _CREATE_EXPORT_STUB | ||
|
||
} // namespace exports | ||
} // namespace proxy_wasm |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,11 +42,19 @@ using WasmForeignFunction = | |
using WasmVmFactory = std::function<std::unique_ptr<WasmVm>()>; | ||
using CallOnThreadFunction = std::function<void(std::function<void()>)>; | ||
|
||
struct SanitizationConfig { | ||
std::vector<std::string> argument_list; | ||
enum class ListType : int { Allowlist = 0, Denylist = 1 }; | ||
ListType list_type; | ||
PiotrSikora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
using AllowedCapabilitiesMap = std::unordered_map<std::string, SanitizationConfig>; | ||
|
||
// Wasm execution instance. Manages the host side of the Wasm interface. | ||
class WasmBase : public std::enable_shared_from_this<WasmBase> { | ||
public: | ||
WasmBase(std::unique_ptr<WasmVm> wasm_vm, std::string_view vm_id, | ||
std::string_view vm_configuration, std::string_view vm_key); | ||
std::string_view vm_configuration, std::string_view vm_key, | ||
AllowedCapabilitiesMap allowed_capabilities); | ||
WasmBase(const std::shared_ptr<WasmHandleBase> &other, WasmVmFactory factory); | ||
virtual ~WasmBase(); | ||
|
||
|
@@ -92,6 +100,12 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> { | |
return nullptr; | ||
} | ||
|
||
// Capability restriction (restricting/exposing the ABI). | ||
bool capabilityAllowed(std::string capability_name) { | ||
return allowed_capabilities_.empty() || | ||
allowed_capabilities_.find(capability_name) != allowed_capabilities_.end(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so basically if the configuration is null, all functions are allowed, and if it is the empty list (or the meaningless list like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But, as long as there would be no use cases for disabling all exported functions (and I don't come up with anything), this is just fine as it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's empty then all functions are allowed. It does feel a little unintuitive, but yeah I don't think there's any case where you would want to have all capabilities restricted. |
||
} | ||
|
||
virtual ContextBase *createVmContext() { return new ContextBase(this); } | ||
virtual ContextBase *createRootContext(const std::shared_ptr<PluginBase> &plugin) { | ||
return new ContextBase(this, plugin); | ||
|
@@ -225,6 +239,20 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> { | |
WasmCallVoid<1> on_log_; | ||
WasmCallVoid<1> on_delete_; | ||
|
||
#define FOR_ALL_MODULE_FUNCTIONS(_f) \ | ||
_f(validate_configuration) _f(on_vm_start) _f(on_configure) _f(on_tick) _f(on_context_create) \ | ||
_f(on_new_connection) _f(on_downstream_data) _f(on_upstream_data) \ | ||
_f(on_downstream_connection_close) _f(on_upstream_connection_close) _f(on_request_body) \ | ||
_f(on_request_trailers) _f(on_request_metadata) _f(on_response_body) \ | ||
_f(on_response_trailers) _f(on_response_metadata) _f(on_http_call_response) \ | ||
_f(on_grpc_receive) _f(on_grpc_close) _f(on_grpc_receive_initial_metadata) \ | ||
_f(on_grpc_receive_trailing_metadata) _f(on_queue_ready) _f(on_done) \ | ||
_f(on_log) _f(on_delete) | ||
|
||
// Capabilities which are allowed to be linked to the module. If this is empty, restriction | ||
// is not enforced. | ||
AllowedCapabilitiesMap allowed_capabilities_; | ||
|
||
std::shared_ptr<WasmHandleBase> base_wasm_handle_; | ||
|
||
// Used by the base_wasm to enable non-clonable thread local Wasm(s) to be constructed. | ||
|
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.
nit: @PiotrSikora we should have a new Wasm result like
Unauthorized
as WASI'sacces Permission denied.
(hopefully in the next ABI ). https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#variants-1