diff --git a/docs/root/configuration/http_filters/lua_filter.rst b/docs/root/configuration/http_filters/lua_filter.rst index 64b02794b5a9..815e5aef9632 100644 --- a/docs/root/configuration/http_filters/lua_filter.rst +++ b/docs/root/configuration/http_filters/lua_filter.rst @@ -312,57 +312,6 @@ Returns the current request's underlying :repo:`connection `. -importPublicKey() -^^^^^^^^^^^^^^^^^ - -.. code-block:: lua - - pkey = handle:importPublicKey(keyder, keyderLenght) - -Returns public key which is used by :ref:`verifySignature ` to verify digital signature. - -.. attention:: - - Remember to call :ref:`releasePublicKey ` if *pkey* is not needed to avoid memory leak. - -.. _release_public_key: - -releasePublicKey() -^^^^^^^^^^^^^^^^^^ - -.. code-block:: lua - - handle:releasePublicKey(pkey) - -Free the resource of *pkey* - -decodeBase64() -^^^^^^^^^^^^^^ - -.. code-block:: lua - - raw = request_handle:decodeBase64(str) - -Decodes base64 encoded string. Returns *nil* if input string is invalid. - -.. _verify_signature: - -verifySignature() -^^^^^^^^^^^^^^^^^ - -.. code-block:: lua - - ok, error = verifySignature(pkey, hashFunction, signature, signatureLength, data, dataLength) - -Verify signature using provided parameters. *pkey* is the public key, *hashFunction* is the variable -for hash function which be used for verifying signature. *MD4*, *MD5*, *SHA1*, *SHA224*, *SHA256*, -*SHA384*, *SHA512* and *MD5_SHA1* are supported. *signature* is the signature to be verified. -*signatureLength* is the length of the signature. *data* is the content which will be hashed. -*dataLength* is the length of data. - -The function returns a pair. If the first element is *true*, the second element will be empty -which means signature is verified; otherwise, the second element will store the error message. - .. _config_http_filters_lua_header_wrapper: Header object API diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index 4e163f5884db..98a5bbc05314 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -3,64 +3,6 @@ Version history 1.11.0 (Pending) ================ -* access log: added a new field for downstream TLS session ID to file and gRPC access logger. -* access log: added a new field for route name to file and gRPC access logger. -* access log: added a new field for response code details in :ref:`file access logger` and :ref:`gRPC access logger`. -* admin: the administration interface now includes a :ref:`/ready endpoint ` for easier readiness checks. -* admin: extend :ref:`/runtime_modify endpoint ` to support parameters within the request body. -* api: track and report requests issued since last load report. -* build: releases are built with Clang and linked with LLD. -* dubbo_proxy: support the :ref:`Dubbo proxy filter `. -* eds: added support to specify max time for which endpoints can be used :ref:`gRPC filter `. -* event: added :ref:`loop duration and poll delay statistics `. -* ext_authz: added a `x-envoy-auth-partial-body` metadata header set to `false|true` indicating if there is a partial body sent in the authorization request message. -* ext_authz: added configurable status code that allows customizing HTTP responses on filter check status errors. -* ext_authz: added option to `ext_authz` that allows the filter clearing route cache. -* filter: exposed functions to Lua to verify digital signature. -* grpc-json: added support for :ref:`auto mapping - `. -* health check: added :ref:`initial jitter ` to add jitter to the first health check in order to prevent thundering herd on Envoy startup. -* hot restart: stats are no longer shared between hot restart parent/child via shared memory, but rather by RPC. Hot restart version incremented to 11. -* http: fixed a bug where large unbufferable responses were not tracked in stats and logs correctly. -* http: fixed a crashing bug where gRPC local replies would cause segfaults when upstream access logging was on. -* http: mitigated a race condition with the :ref:`delayed_close_timeout` where it could trigger while actively flushing a pending write buffer for a downstream connection. -* http: changed `sendLocalReply` to send percent-encoded `GrpcMessage`. -* jwt_authn: make filter's parsing of JWT more flexible, allowing syntax like ``jwt=eyJhbGciOiJS...ZFnFIw,extra=7,realm=123`` -* original_src filter: added the :ref:`filter`. -* rbac: migrated from v2alpha to v2. -* redis: add support for Redis cluster custom cluster type. -* redis: added :ref:`prefix routing ` to enable routing commands based on their key's prefix to different upstream. -* redis: add support for zpopmax and zpopmin commands. -* redis: added - :ref:`max_buffer_size_before_flush ` to batch commands together until the encoder buffer hits a certain size, and - :ref:`buffer_flush_timeout ` to control how quickly the buffer is flushed if it is not full. -* redis: added auth support :ref:`downstream_auth_password ` for downstream client authentication, and :ref:`auth_password ` to configure authentication passwords for upstream server clusters. -* router: add support for configuring a :ref:`grpc timeout offset ` on incoming requests. -* router: added ability to control retry back-off intervals via :ref:`retry policy `. -* router: added ability to issue a hedged retry in response to a per try timeout via a :ref:`hedge policy `. -* router: added a route name field to each http route in route.Route list -* router: per try timeouts will no longer start before the downstream request has been received - in full by the router. This ensures that the per try timeout does not account for slow - downstreams and that will not start before the global timeout. -* runtime: added support for :ref:`flexible layering configuration - `. -* runtime: added support for statically :ref:`specifying the runtime in the bootstrap configuration - `. -* sandbox: added :ref:`CSRF sandbox `. -* server: ``--define manual_stamp=manual_stamp`` was added to allow server stamping outside of binary rules. - more info in the `bazel docs `_. -* tool: added :repo:`proto ` support for :ref:`router check tool ` tests. -* tracing: add trace sampling configuration to the route, to override the route level. -* upstream: added :ref:`upstream_cx_pool_overflow ` for the connection pool circuit breaker. -* upstream: an EDS management server can now force removal of a host that is still passing active - health checking by first marking the host as failed via EDS health check and subsequently removing - it in a future update. This is a mechanism to work around a race condition in which an EDS - implementation may remove a host before it has stopped passing active HC, thus causing the host - to become stranded until a future update. -* upstream: added :ref:`an option ` - that allows ignoring new hosts for the purpose of load balancing calculations until they have - been health checked for the first time. -* upstream: added runtime error checking to prevent setting dns type to STRICT_DNS or LOGICAL_DNS when custom resolver name is specified. 1.10.0 (Apr 5, 2019) ==================== diff --git a/source/extensions/filters/common/lua/BUILD b/source/extensions/filters/common/lua/BUILD index 11de8307b2f4..8edd21d87ec5 100644 --- a/source/extensions/filters/common/lua/BUILD +++ b/source/extensions/filters/common/lua/BUILD @@ -14,7 +14,6 @@ envoy_cc_library( hdrs = ["lua.h"], external_deps = [ "luajit", - "ssl", ], deps = [ "//include/envoy/thread_local:thread_local_interface", diff --git a/source/extensions/filters/http/lua/lua_filter.cc b/source/extensions/filters/http/lua/lua_filter.cc index 0935a26cf0bc..11404f110b77 100644 --- a/source/extensions/filters/http/lua/lua_filter.cc +++ b/source/extensions/filters/http/lua/lua_filter.cc @@ -9,13 +9,6 @@ #include "common/common/enum_to_int.h" #include "common/http/message_impl.h" -#include "absl/strings/ascii.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_cat.h" -#include "openssl/base64.h" -#include "openssl/bytestring.h" -#include "openssl/evp.h" - namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -433,129 +426,6 @@ int StreamHandleWrapper::luaLogCritical(lua_State* state) { return 0; } -const EVP_MD* StreamHandleWrapper::getDigest(const absl::string_view& name) { - std::string hash = absl::AsciiStrToLower(name); - - // Hash Hash algorithms set refers - // https://github.com/google/boringssl/blob/ff62b38b4b5a0e7926034b5f93d0c276e55b571d/include/openssl/digest.h - if (hash == "md4") { - return EVP_md4(); - } else if (hash == "md5") { - return EVP_md5(); - } else if (hash == "sha1") { - return EVP_sha1(); - } else if (hash == "sha224") { - return EVP_sha224(); - } else if (hash == "sha256") { - return EVP_sha256(); - } else if (hash == "sha384") { - return EVP_sha384(); - } else if (hash == "sha512") { - return EVP_sha512(); - } else if (hash == "md5_sha1") { - return EVP_md5_sha1(); - } else { - return nullptr; - } -} - -int StreamHandleWrapper::luaVerifySignature(lua_State* state) { - // Step 1: get public key - auto ptr = lua_touserdata(state, 2); - auto key = reinterpret_cast(ptr); - - // Step 2: initialize EVP_MD_CTX - EVP_MD_CTX* ctx = EVP_MD_CTX_new(); - - // Step 3: initialize EVP_MD - absl::string_view hash_func = luaL_checkstring(state, 3); - const EVP_MD* md = getDigest(hash_func); - - if (md == nullptr) { - lua_pushboolean(state, false); - std::string err_msg = absl::StrCat(hash_func, " is not supported."); - lua_pushlstring(state, err_msg.data(), err_msg.length()); - EVP_MD_CTX_free(ctx); - return 2; - } - - // Step 4: initialize EVP_DigestVerify - int ok = EVP_DigestVerifyInit(ctx, nullptr, md, nullptr, key); - if (!ok) { - lua_pushboolean(state, false); - absl::string_view err_msg = "Failed to initialize digest verify."; - lua_pushlstring(state, err_msg.data(), err_msg.length()); - EVP_MD_CTX_free(ctx); - return 2; - } - - // Step 5: verify signature - const char* signature = luaL_checkstring(state, 4); - int sigLen = luaL_checknumber(state, 5); - absl::string_view sig(signature, sigLen); - - const char* clearText = luaL_checkstring(state, 6); - int textLen = luaL_checknumber(state, 7); - absl::string_view text(clearText, textLen); - - ok = EVP_DigestVerify(ctx, reinterpret_cast(sig.data()), sig.length(), - reinterpret_cast(text.data()), text.length()); - - // Step 6: check result - if (ok == 1) { - lua_pushboolean(state, true); - lua_pushnil(state); - EVP_MD_CTX_free(ctx); - return 2; - } - - lua_pushboolean(state, false); - std::string err_msg = absl::StrCat("Failed to verify digest. Error code: ", ok); - lua_pushlstring(state, err_msg.data(), err_msg.length()); - EVP_MD_CTX_free(ctx); - return 2; -} - -int StreamHandleWrapper::luaDecodeBase64(lua_State* state) { - // Get input string - absl::string_view str = luaL_checkstring(state, 2); - std::string output; - - bool ok = absl::Base64Unescape(str, &output); - - if (ok) { - lua_pushlstring(state, output.data(), output.length()); - } else { - // If decode failed, push a nil into stack. - lua_pushnil(state); - } - - return 1; -} - -int StreamHandleWrapper::luaImportPublicKey(lua_State* state) { - // Get byte array and the length. - const char* str = luaL_checkstring(state, 2); - int n = luaL_checknumber(state, 3); - - absl::string_view keyder(str, n); - CBS cbs({reinterpret_cast(keyder.data()), keyder.length()}); - EVP_PKEY* key = EVP_parse_public_key(&cbs); - if (key == nullptr) { - lua_pushnil(state); - } else { - lua_pushlightuserdata(state, key); - } - return 1; -} - -int StreamHandleWrapper::luaReleasePublicKey(lua_State* state) { - auto ptr = lua_touserdata(state, 2); - auto key = reinterpret_cast(ptr); - EVP_PKEY_free(key); - return 0; -} - FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocator& tls, Upstream::ClusterManager& cluster_manager) : cluster_manager_(cluster_manager), lua_state_(lua_code, tls) { diff --git a/source/extensions/filters/http/lua/lua_filter.h b/source/extensions/filters/http/lua/lua_filter.h index 629b229b0cc5..c46d90a803d7 100644 --- a/source/extensions/filters/http/lua/lua_filter.h +++ b/source/extensions/filters/http/lua/lua_filter.h @@ -7,8 +7,6 @@ #include "extensions/filters/http/lua/wrappers.h" #include "extensions/filters/http/well_known_names.h" -#include "openssl/evp.h" - namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -129,25 +127,14 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObjectdecodeHeaders(request_headers, true)); } -// Check base64 decode. -TEST_F(LuaHttpFilterTest, CheckBase64Decode) { - const std::string SCRIPT{R"EOF( - function envoy_on_request(request_handle) - str = request_handle:decodeBase64("aGVsbG8=") - if str == nil then - request_handle:logTrace("fail") - else - request_handle:logTrace(str) - end - end - )EOF"}; - - InSequence s; - setup(SCRIPT); - - Http::TestHeaderMapImpl request_headers{{":path", "/"}}; - - EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("hello"))); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); -} - -// Check base64 decode. -TEST_F(LuaHttpFilterTest, CheckBase64DecodeFailure) { - const std::string SCRIPT{R"EOF( - function envoy_on_request(request_handle) - str = request_handle:decodeBase64(".") - if str == nil then - request_handle:logTrace("fail") - else - request_handle:logTrace(str) - end - end - )EOF"}; - - InSequence s; - setup(SCRIPT); - - Http::TestHeaderMapImpl request_headers{{":path", "/"}}; - - EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("fail"))); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); -} - -TEST_F(LuaHttpFilterTest, ImportandReleasePublicKey) { - const std::string SCRIPT{R"EOF( - function string.fromhex(str) - return (str:gsub('..', function (cc) - return string.char(tonumber(cc, 16)) - end)) - end - function envoy_on_request(request_handle) - key = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100a7471266d01d160308d73409c06f2e8d35c531c458d3e480e9f3191847d062ec5ccff7bc51e949d5f2c3540c189a4eca1e8633a62cf2d0923101c27e38013e71de9ae91a704849bff7fbe2ce5bf4bd666fd9731102a53193fe5a9a5a50644ff8b1183fa897646598caad22a37f9544510836372b44c58c98586fb7144629cd8c9479592d996d32ff6d395c0b8442ec5aa1ef8051529ea0e375883cefc72c04e360b4ef8f5760650589ca814918f678eee39b884d5af8136a9630a6cc0cde157dc8e00f39540628d5f335b2c36c54c7c8bc3738a6b21acff815405afa28e5183f550dac19abcf1145a7f9ced987db680e4a229cac75dee347ec9ebce1fc3dbbbb0203010001" - raw = key:fromhex() - key = request_handle:importPublicKey(raw, string.len(raw)) - - if key == nil then - request_handle:logTrace("failed to import public key") - else - request_handle:releasePublicKey(key) - request_handle:logTrace("succeeded to import public key") - end - end - )EOF"}; - - InSequence s; - setup(SCRIPT); - - Http::TestHeaderMapImpl request_headers{{":path", "/"}}; - - EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("succeeded to import public key"))); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); -} - -TEST_F(LuaHttpFilterTest, InvalidPublicKey) { - const std::string SCRIPT{R"EOF( - function string.fromhex(str) - return (str:gsub('..', function (cc) - return string.char(tonumber(cc, 16)) - end)) - end - function envoy_on_request(request_handle) - key = "0000" - raw = key:fromhex() - key = request_handle:importPublicKey(raw, string.len(raw)) - - if key == nil then - request_handle:logTrace("failed to import public key") - else - request_handle:releasePublicKey(key) - request_handle:logTrace("succeeded to import public key") - end - end - )EOF"}; - - InSequence s; - setup(SCRIPT); - - Http::TestHeaderMapImpl request_headers{{":path", "/"}}; - - EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("failed to import public key"))); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); -} - -TEST_F(LuaHttpFilterTest, SignatureVerify) { - const std::string SCRIPT{R"EOF( - function string.fromhex(str) - return (str:gsub('..', function (cc) - return string.char(tonumber(cc, 16)) - end)) - end - function envoy_on_request(request_handle) - key = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100a7471266d01d160308d73409c06f2e8d35c531c458d3e480e9f3191847d062ec5ccff7bc51e949d5f2c3540c189a4eca1e8633a62cf2d0923101c27e38013e71de9ae91a704849bff7fbe2ce5bf4bd666fd9731102a53193fe5a9a5a50644ff8b1183fa897646598caad22a37f9544510836372b44c58c98586fb7144629cd8c9479592d996d32ff6d395c0b8442ec5aa1ef8051529ea0e375883cefc72c04e360b4ef8f5760650589ca814918f678eee39b884d5af8136a9630a6cc0cde157dc8e00f39540628d5f335b2c36c54c7c8bc3738a6b21acff815405afa28e5183f550dac19abcf1145a7f9ced987db680e4a229cac75dee347ec9ebce1fc3dbbbb0203010001" - hashFunc = "sha256" - signature = "345ac3a167558f4f387a81c2d64234d901a7ceaa544db779d2f797b0ea4ef851b740905a63e2f4d5af42cee093a29c7155db9a63d3d483e0ef948f5ac51ce4e10a3a6606fd93ef68ee47b30c37491103039459122f78e1c7ea71a1a5ea24bb6519bca02c8c9915fe8be24927c91812a13db72dbcb500103a79e8f67ff8cb9e2a631974e0668ab3977bf570a91b67d1b6bcd5dce84055f21427d64f4256a042ab1dc8e925d53a769f6681a873f5859693a7728fcbe95beace1563b5ffbcd7c93b898aeba31421dafbfadeea50229c49fd6c445449314460f3d19150bd29a91333beaced557ed6295234f7c14fa46303b7e977d2c89ba8a39a46a35f33eb07a332" - data = "hello" - - rawkey = key:fromhex() - pkey = request_handle:importPublicKey(rawkey, string.len(rawkey)) - - if pkey == nil then - request_handle:logTrace("failed to import public key") - return - end - - rawsig = signature:fromhex() - - ok, error = request_handle:verifySignature(pkey, hashFunc, rawsig, string.len(rawsig), data, string.len(data)) - if ok then - request_handle:logTrace("signature is valid") - else - request_handle:logTrace(error) - end - - ok, error = request_handle:verifySignature(pkey, "unknown", rawsig, string.len(rawsig), data, string.len(data)) - if ok then - request_handle:logTrace("signature is valid") - else - request_handle:logTrace(error) - end - - ok, error = request_handle:verifySignature(pkey, hashFunc, "0000", 4, data, string.len(data)) - if ok then - request_handle:logTrace("signature is valid") - else - request_handle:logTrace(error) - end - - ok, error = request_handle:verifySignature(pkey, hashFunc, rawsig, string.len(rawsig), "xxxx", 4) - if ok then - request_handle:logTrace("signature is valid") - else - request_handle:logTrace(error) - end - - request_handle:releasePublicKey(pkey) - end - )EOF"}; - - InSequence s; - setup(SCRIPT); - - Http::TestHeaderMapImpl request_headers{{":path", "/"}}; - - EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("signature is valid"))); - EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("unknown is not supported."))); - EXPECT_CALL(*filter_, - scriptLog(spdlog::level::trace, StrEq("Failed to verify digest. Error code: 0"))); - EXPECT_CALL(*filter_, - scriptLog(spdlog::level::trace, StrEq("Failed to verify digest. Error code: 0"))); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); -} - } // namespace } // namespace Lua } // namespace HttpFilters diff --git a/test/extensions/filters/http/lua/lua_integration_test.cc b/test/extensions/filters/http/lua/lua_integration_test.cc index 845434c7e453..3b7d9b5adc9e 100644 --- a/test/extensions/filters/http/lua/lua_integration_test.cc +++ b/test/extensions/filters/http/lua/lua_integration_test.cc @@ -53,8 +53,6 @@ class LuaIntegrationTest : public testing::TestWithParammakeHeaderOnlyRequest(request_headers); - waitForNextUpstreamRequest(); - - EXPECT_EQ("approved", upstream_request_->headers() - .get(Http::LowerCaseString("signature_verification")) - ->value() - .getStringView()); - - upstream_request_->encodeHeaders(default_response_headers_, true); - response->waitForEndStream(); - - EXPECT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().Status()->value().getStringView()); - - cleanup(); -} - } // namespace } // namespace Envoy