diff --git a/api/envoy/extensions/wasm/v3/wasm.proto b/api/envoy/extensions/wasm/v3/wasm.proto index b4566c826ed08..cbb64aeb11342 100644 --- a/api/envoy/extensions/wasm/v3/wasm.proto +++ b/api/envoy/extensions/wasm/v3/wasm.proto @@ -40,7 +40,7 @@ message SanitizationConfig { } // Configuration for a Wasm VM. -// [#next-free-field: 8] +// [#next-free-field: 9] message VmConfig { // An ID which will be used along with a hash of the wasm code (or the name of the registered Null // VM plugin) to determine which VM will be used for the plugin. All plugins which use the same @@ -83,6 +83,13 @@ message VmConfig { // The Wasm code that Envoy will execute. config.core.v3.AsyncDataSource code = 3; + // A hex-encoded Ed 25519 public key to use for verification. + // The runtime must verify an embedded signature over the + // Wasm code if specified before executing. + // TODO: May want to expand this field to a "VerificationOptions" message containing + // repeated public keys, options for all or none, and signature types. + string public_key = 8 [(validate.rules).string = {len: 64}]; + // The Wasm configuration used in initialization of a new VM // (proxy_on_start). `google.protobuf.Struct` is serialized as JSON before // passing it to the plugin. `google.protobuf.BytesValue` and diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 9967b86504382..0312d73eb8d0b 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -858,6 +858,11 @@ def _proxy_wasm_cpp_sdk(): external_http_archive(name = "proxy_wasm_cpp_sdk") def _proxy_wasm_cpp_host(): + # Use a local branch to pull in https://github.com/proxy-wasm/proxy-wasm-cpp-host/pull/177 + # native.local_repository( + # name = "proxy_wasm_cpp_host", + # path = "/usr/local/google/home/asraa/git/proxy-wasm-cpp-host", + # ) external_http_archive(name = "proxy_wasm_cpp_host") def _emscripten_toolchain(): diff --git a/generated_api_shadow/envoy/extensions/wasm/v3/wasm.proto b/generated_api_shadow/envoy/extensions/wasm/v3/wasm.proto index b4566c826ed08..c6512d9a7bbf8 100644 --- a/generated_api_shadow/envoy/extensions/wasm/v3/wasm.proto +++ b/generated_api_shadow/envoy/extensions/wasm/v3/wasm.proto @@ -40,7 +40,7 @@ message SanitizationConfig { } // Configuration for a Wasm VM. -// [#next-free-field: 8] +// [#next-free-field: 9] message VmConfig { // An ID which will be used along with a hash of the wasm code (or the name of the registered Null // VM plugin) to determine which VM will be used for the plugin. All plugins which use the same @@ -83,6 +83,12 @@ message VmConfig { // The Wasm code that Envoy will execute. config.core.v3.AsyncDataSource code = 3; + // A hex-encoded Ed 25519 public key to use for verification. + // The runtime must verify an embedded signature over the + // Wasm code if specified before executing. + // TODO: May want to be repeated + string public_key = 8 [(validate.rules).string = {len: 64}]; + // The Wasm configuration used in initialization of a new VM // (proxy_on_start). `google.protobuf.Struct` is serialized as JSON before // passing it to the plugin. `google.protobuf.BytesValue` and diff --git a/source/extensions/common/wasm/wasm.cc b/source/extensions/common/wasm/wasm.cc index 22274d07acfdd..9bdf7ed53e077 100644 --- a/source/extensions/common/wasm/wasm.cc +++ b/source/extensions/common/wasm/wasm.cc @@ -392,8 +392,9 @@ bool createWasm(const PluginSharedPtr& plugin, const Stats::ScopeSharedPtr& scop return wasm_factory(config, scope, cluster_manager, dispatcher, lifecycle_notifier, toAbslStringView(vm_key)); }; + // TODO: May want to replace the public key with verification options defined in proxy_wasm. auto wasm = proxy_wasm::createWasm( - vm_key, code, plugin, proxy_wasm_factory, + vm_key, code, config.config().vm_config().public_key(), plugin, proxy_wasm_factory, getCloneFactory(wasm_extension, dispatcher, create_root_context_for_testing), config.config().vm_config().allow_precompiled()); Stats::ScopeSharedPtr create_wasm_stats_scope = diff --git a/test/extensions/common/wasm/BUILD b/test/extensions/common/wasm/BUILD index 54e6b7a24fcd6..c16b5c5ad3585 100644 --- a/test/extensions/common/wasm/BUILD +++ b/test/extensions/common/wasm/BUILD @@ -37,6 +37,7 @@ envoy_cc_test( "//test/extensions/common/wasm/test_data:bad_signature_cpp.wasm", "//test/extensions/common/wasm/test_data:test_context_cpp.wasm", "//test/extensions/common/wasm/test_data:test_cpp.wasm", + "//test/extensions/common/wasm/test_data:test_cpp.signed.wasm", "//test/extensions/common/wasm/test_data:test_restriction_cpp.wasm", ]), external_deps = ["abseil_optional"], diff --git a/test/extensions/common/wasm/test_data/BUILD b/test/extensions/common/wasm/test_data/BUILD index 95db303869db9..de60baf208e99 100644 --- a/test/extensions/common/wasm/test_data/BUILD +++ b/test/extensions/common/wasm/test_data/BUILD @@ -9,6 +9,8 @@ licenses(["notice"]) # Apache 2 envoy_package() +exports_files(["test_cpp.signed.wasm"]) + wasm_rust_binary( name = "test_rust.wasm", srcs = ["test_rust.rs"], diff --git a/test/extensions/common/wasm/test_data/test_cpp.signed.wasm b/test/extensions/common/wasm/test_data/test_cpp.signed.wasm new file mode 100644 index 0000000000000..feb288ba7728b Binary files /dev/null and b/test/extensions/common/wasm/test_data/test_cpp.signed.wasm differ diff --git a/test/extensions/common/wasm/wasm_test.cc b/test/extensions/common/wasm/wasm_test.cc index 0d9d4e1b030cd..3bb801138985b 100644 --- a/test/extensions/common/wasm/wasm_test.cc +++ b/test/extensions/common/wasm/wasm_test.cc @@ -1403,6 +1403,41 @@ TEST_P(WasmCommonTest, ThreadLocalCopyRetainsEnforcement) { thread_local_wasm->start(plugin); } +TEST_P(WasmCommonTest, SignedModule) { +#if defined(__aarch64__) + // TODO(PiotrSikora): There are no Emscripten releases for arm64. + if (GetParam() != "null") { + return; + } +#endif + if (GetParam() != "v8") { + return; + } + Stats::IsolatedStoreImpl stats_store; + Api::ApiPtr api = Api::createApiForTest(stats_store); + Upstream::MockClusterManager cluster_manager; + Event::DispatcherPtr dispatcher(api->allocateDispatcher("wasm_test")); + auto scope = Stats::ScopeSharedPtr(stats_store.createScope("wasm.")); + NiceMock local_info; + const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/common/wasm/test_data/test_cpp.signed.wasm")); + EXPECT_FALSE(code.empty()); + + envoy::extensions::wasm::v3::PluginConfig plugin_config; + *plugin_config.mutable_vm_config()->mutable_runtime() = + absl::StrCat("envoy.wasm.runtime.", GetParam()); + plugin_config.mutable_vm_config()->set_public_key( + "95cf8acf16e0002805bcf303cf204d02d34537d435f20b1a53fa4c2253d35448"); + auto plugin = std::make_shared( + plugin_config, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); + auto vm_key = proxy_wasm::makeVmKey("", "", code); + auto wasm = std::make_unique(plugin->wasmConfig(), vm_key, scope, + cluster_manager, *dispatcher); + + EXPECT_TRUE(wasm->load(code, false)); + EXPECT_TRUE(wasm->initialize()); +} + class WasmCommonContextTest : public Common::Wasm::WasmTestBase> { public: