Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions bazel/foreign_cc/luajit.patch
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ index 0000000..9c71271
+
+ os.environ["MACOSX_DEPLOYMENT_TARGET"] = "10.6"
+ os.environ["DEFAULT_CC"] = os.environ.get("CC", "")
+ os.environ["TARGET_CFLAGS"] = os.environ.get("CFLAGS", "")
+ os.environ["TARGET_LDFLAGS"] = os.environ.get("CFLAGS", "")
+ os.environ["TARGET_CFLAGS"] = os.environ.get("CFLAGS", "") + " -fno-function-sections -fno-data-sections"
+ os.environ["TARGET_LDFLAGS"] = os.environ.get("CFLAGS", "") + " -fno-function-sections -fno-data-sections"
+ os.environ["CFLAGS"] = ""
+ # LuaJIT compile process build a tool `buildvm` and use it, building `buildvm` with ASAN
+ # will cause LSAN detect its leak and fail the build, set exitcode to 0 to make LSAN doesn't
Expand Down
26 changes: 26 additions & 0 deletions docs/root/configuration/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,32 @@ Returns the current request's underlying :repo:`connection <include/envoy/networ

Returns a :ref:`connection object <config_http_filters_lua_connection_wrapper>`.

importPublicKey()
^^^^^^^^^^^^^^^^^

.. code-block:: lua

pubkey = handle:importPublicKey(keyder, keyderLength)

Returns public key which is used by :ref:`verifySignature <verify_signature>` to verify digital signature.

.. _verify_signature:

verifySignature()
^^^^^^^^^^^^^^^^^

.. code-block:: lua

ok, error = verifySignature(hashFunction, pubkey, signature, signatureLength, data, dataLength)

Verify signature using provided parameters. *hashFunction* is the variable for hash function which be used
for verifying signature. *SHA1*, *SHA224*, *SHA256*, *SHA384* and *SHA512* are supported.
*pubkey* is the public key. *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
Expand Down
6 changes: 6 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ Version history
* 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<envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.delayed_close_timeout>` where it could trigger while actively flushing a pending write buffer for a downstream connection.
* jwt_authn: make filter's parsing of JWT more flexible, allowing syntax like ``jwt=eyJhbGciOiJS...ZFnFIw,extra=7,realm=123``
* listener: added :ref:`source IP <envoy_api_field_listener.FilterChainMatch.source_prefix_ranges>`
and :ref:`source port <envoy_api_field_listener.FilterChainMatch.source_ports>` filter
chain matching.
* lua: exposed functions to Lua to verify digital signature.
* original_src filter: added the :ref:`filter<config_http_filters_original_src>`.
* rbac: migrated from v2alpha to v2.
* redis: add support for Redis cluster custom cluster type.
* redis: added :ref:`prefix routing <envoy_api_field_config.filter.network.redis_proxy.v2.RedisProxy.prefix_routes>` to enable routing commands based on their key's prefix to different upstream.
* redis: add support for zpopmax and zpopmin commands.
Expand Down
59 changes: 58 additions & 1 deletion source/common/crypto/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#include "common/common/assert.h"
#include "common/common/stack_array.h"

#include "openssl/evp.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "openssl/bytestring.h"
#include "openssl/hmac.h"
#include "openssl/sha.h"

Expand Down Expand Up @@ -41,6 +43,61 @@ std::vector<uint8_t> Utility::getSha256Hmac(const std::vector<uint8_t>& key,
return hmac;
}

const VerificationOutput Utility::verifySignature(absl::string_view hash, EVP_PKEY* pubKey,
const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& text) {
// Step 1: initialize EVP_MD_CTX
bssl::ScopedEVP_MD_CTX ctx;

// Step 2: initialize EVP_MD
const EVP_MD* md = Utility::getHashFunction(hash);

if (md == nullptr) {
return {false, absl::StrCat(hash, " is not supported.")};
}

// Step 3: initialize EVP_DigestVerify
int ok = EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pubKey);
if (!ok) {
return {false, "Failed to initialize digest verify."};
}

// Step 4: verify signature
ok = EVP_DigestVerify(ctx.get(), signature.data(), signature.size(), text.data(), text.size());

// Step 5: check result
if (ok == 1) {
return {true, ""};
}

return {false, absl::StrCat("Failed to verify digest. Error code: ", ok)};
}

PublicKeyPtr Utility::importPublicKey(const std::vector<uint8_t>& key) {
CBS cbs({key.data(), key.size()});
return PublicKeyPtr(EVP_parse_public_key(&cbs));
}

const EVP_MD* Utility::getHashFunction(absl::string_view name) {
const std::string hash = absl::AsciiStrToLower(name);

// Hash algorithms set refers
// https://github.com/google/boringssl/blob/master/include/openssl/digest.h
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 {
return nullptr;
}
}

} // namespace Crypto
} // namespace Common
} // namespace Envoy
39 changes: 39 additions & 0 deletions source/common/crypto/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,27 @@
#include "envoy/buffer/buffer.h"

#include "absl/strings/string_view.h"
#include "openssl/evp.h"

namespace Envoy {
namespace Common {
namespace Crypto {

struct VerificationOutput {
/**
* Verification result. If result_ is true, error_message_ is empty.
*/
bool result_;

/**
* Error message when verification failed.
* TODO(crazyxy): switch to absl::StatusOr when available
*/
std::string error_message_;
};

typedef bssl::UniquePtr<EVP_PKEY> PublicKeyPtr;

class Utility {
public:
/**
Expand All @@ -28,6 +44,29 @@ class Utility {
*/
static std::vector<uint8_t> getSha256Hmac(const std::vector<uint8_t>& key,
absl::string_view message);

/**
* Verify cryptographic signatures.
* @param hash hash function(including SHA1, SHA224, SHA256, SHA384, SHA512)
* @param key pointer to public key
* @param signature signature
* @param text clear text
* @return If the result_ is true, the error_message_ is empty; otherwise,
* the error_message_ stores the error message
*/
static const VerificationOutput verifySignature(absl::string_view hash, EVP_PKEY* key,
const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& text);

/**
* Import public key.
* @param key key string
* @return pointer to public key
*/
static PublicKeyPtr importPublicKey(const std::vector<uint8_t>& key);

private:
static const EVP_MD* getHashFunction(absl::string_view name);
};

} // namespace Crypto
Expand Down
5 changes: 5 additions & 0 deletions source/common/http/codec_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ class CodecClient : Logger::Loggable<Logger::Id::client>,
*/
uint64_t id() { return connection_->id(); }

/**
* @return the underlying codec protocol.
*/
Protocol protocol() { return codec_->protocol(); }

/**
* @return the underlying connection error.
*/
Expand Down
28 changes: 16 additions & 12 deletions source/common/http/http1/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ void ConnPoolImpl::onResponseComplete(ActiveClient& client) {
if (!client.stream_wrapper_->encode_complete_) {
ENVOY_CONN_LOG(debug, "response before request complete", *client.codec_client_);
onDownstreamReset(client);
} else if (client.stream_wrapper_->saw_close_header_ || client.codec_client_->remoteClosed()) {
ENVOY_CONN_LOG(debug, "saw upstream connection: close", *client.codec_client_);
} else if (client.stream_wrapper_->close_connection_ || client.codec_client_->remoteClosed()) {
ENVOY_CONN_LOG(debug, "saw upstream close connection", *client.codec_client_);
onDownstreamReset(client);
} else if (client.remaining_requests_ > 0 && --client.remaining_requests_ == 0) {
ENVOY_CONN_LOG(debug, "maximum requests per connection", *client.codec_client_);
Expand Down Expand Up @@ -273,17 +273,21 @@ ConnPoolImpl::StreamWrapper::~StreamWrapper() {
void ConnPoolImpl::StreamWrapper::onEncodeComplete() { encode_complete_ = true; }

void ConnPoolImpl::StreamWrapper::decodeHeaders(HeaderMapPtr&& headers, bool end_stream) {
if (headers->Connection() &&
absl::EqualsIgnoreCase(headers->Connection()->value().getStringView(),
Headers::get().ConnectionValues.Close)) {
saw_close_header_ = true;
parent_.parent_.host_->cluster().stats().upstream_cx_close_notify_.inc();
}
if (!saw_close_header_ && headers->ProxyConnection() &&
absl::EqualsIgnoreCase(headers->ProxyConnection()->value().getStringView(),
Headers::get().ConnectionValues.Close)) {
saw_close_header_ = true;
// If Connection: close OR
// Http/1.0 and not Connection: keep-alive OR
// Proxy-Connection: close
if ((headers->Connection() &&
(absl::EqualsIgnoreCase(headers->Connection()->value().getStringView(),
Headers::get().ConnectionValues.Close))) ||
(parent_.codec_client_->protocol() == Protocol::Http10 &&
(!headers->Connection() ||
!absl::EqualsIgnoreCase(headers->Connection()->value().getStringView(),
Headers::get().ConnectionValues.KeepAlive))) ||
(headers->ProxyConnection() &&
(absl::EqualsIgnoreCase(headers->ProxyConnection()->value().getStringView(),
Headers::get().ConnectionValues.Close)))) {
parent_.parent_.host_->cluster().stats().upstream_cx_close_notify_.inc();
close_connection_ = true;
}

StreamDecoderWrapper::decodeHeaders(std::move(headers), end_stream);
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/http1/conn_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class ConnPoolImpl : public ConnectionPool::Instance, public ConnPoolImplBase {

ActiveClient& parent_;
bool encode_complete_{};
bool saw_close_header_{};
bool close_connection_{};
bool decode_complete_{};
};

Expand Down
38 changes: 38 additions & 0 deletions source/common/network/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,44 @@ bool Utility::isLocalConnection(const Network::ConnectionSocket& socket) {
return false;
}

bool Utility::isLocalIpAddress(const Address::Instance& address) {
if (address.type() != Envoy::Network::Address::Type::Ip) {
return false;
}

if (isLoopbackAddress(address)) {
return false;
}

struct ifaddrs* ifaddr;
const int rc = getifaddrs(&ifaddr);
Cleanup ifaddr_cleanup([ifaddr] {
if (ifaddr) {
freeifaddrs(ifaddr);
}
});
RELEASE_ASSERT(rc == 0, "");

auto af_look_up = (address.ip()->version() == Address::IpVersion::v4) ? AF_INET : AF_INET6;

for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr) {
continue;
}

if (ifa->ifa_addr->sa_family == af_look_up) {
const auto* addr = reinterpret_cast<const struct sockaddr_storage*>(ifa->ifa_addr);
auto local_address = Address::addressFromSockAddr(
*addr, (af_look_up == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6));

if (address == *local_address) {
return true;
}
}
}
return false;
}

bool Utility::isInternalAddress(const Address::Instance& address) {
if (address.type() != Address::Type::Ip) {
return false;
Expand Down
6 changes: 6 additions & 0 deletions source/common/network/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ class Utility {
*/
static bool isLocalConnection(const Network::ConnectionSocket& socket);

/**
* Determine whether this is a local address.
* @return bool the address is a local address.
*/
static bool isLocalIpAddress(const Address::Instance& address);

/**
* Determine whether this is an internal (RFC1918) address.
* @return bool the address is an RFC1918 address.
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ envoy_cc_library(
"//include/envoy/upstream:cluster_manager_interface",
"//source/common/buffer:buffer_lib",
"//source/common/common:enum_to_int",
"//source/common/crypto:utility_lib",
"//source/common/http:message_lib",
"//source/extensions/filters/common/lua:lua_lib",
"//source/extensions/filters/common/lua:wrappers_lib",
Expand All @@ -36,6 +37,7 @@ envoy_cc_library(
deps = [
"//include/envoy/http:header_map_interface",
"//include/envoy/stream_info:stream_info_interface",
"//source/common/crypto:utility_lib",
"//source/common/http:utility_lib",
"//source/extensions/filters/common/lua:lua_lib",
"//source/extensions/filters/common/lua:wrappers_lib",
Expand Down
48 changes: 48 additions & 0 deletions source/extensions/filters/http/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "common/buffer/buffer_impl.h"
#include "common/common/assert.h"
#include "common/common/enum_to_int.h"
#include "common/crypto/utility.h"
#include "common/http/message_impl.h"

namespace Envoy {
Expand Down Expand Up @@ -428,6 +429,52 @@ int StreamHandleWrapper::luaLogCritical(lua_State* state) {
return 0;
}

int StreamHandleWrapper::luaVerifySignature(lua_State* state) {
// Step 1: get hash function
absl::string_view hash = luaL_checkstring(state, 2);

// Step 2: get key pointer
auto ptr = lua_touserdata(state, 3);

// Step 3: get signature
const char* signature = luaL_checkstring(state, 4);
int sig_len = luaL_checknumber(state, 5);
const std::vector<uint8_t> sig_vec(signature, signature + sig_len);

// Step 4: get clear text
const char* clear_text = luaL_checkstring(state, 6);
int text_len = luaL_checknumber(state, 7);
const std::vector<uint8_t> text_vec(clear_text, clear_text + text_len);

// Step 5: verify signature
auto output = Common::Crypto::Utility::verifySignature(hash, reinterpret_cast<EVP_PKEY*>(ptr),
sig_vec, text_vec);

lua_pushboolean(state, output.result_);
if (output.result_) {
lua_pushnil(state);
} else {
lua_pushlstring(state, output.error_message_.data(), output.error_message_.length());
}
return 2;
}

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);
std::vector<uint8_t> key(str, str + n);

if (public_key_wrapper_.get() != nullptr) {
public_key_wrapper_.pushStack();
} else {
public_key_wrapper_.reset(
PublicKeyWrapper::create(state, Common::Crypto::Utility::importPublicKey(key)), true);
}

return 1;
}

FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocator& tls,
Upstream::ClusterManager& cluster_manager)
: cluster_manager_(cluster_manager), lua_state_(lua_code, tls) {
Expand All @@ -442,6 +489,7 @@ FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocat
lua_state_.registerType<DynamicMetadataMapWrapper>();
lua_state_.registerType<DynamicMetadataMapIterator>();
lua_state_.registerType<StreamHandleWrapper>();
lua_state_.registerType<PublicKeyWrapper>();

request_function_slot_ = lua_state_.registerGlobal("envoy_on_request");
if (lua_state_.getGlobalRef(request_function_slot_) == LUA_REFNIL) {
Expand Down
Loading