Skip to content

Commit

Permalink
filter: AWS Lambda filter (initial version) (envoyproxy#10260)
Browse files Browse the repository at this point in the history
This filter transform HTTP requests to AWS Lambda invocations.

The filter supports pass-through only. Meaning, the request body
is passed to Lambda as is. Note: Lambda requires the request to be in
JSON format.

In a later iteration, we'll wrap the headers the body in a JSON string
before passing it to Lambda.

The filter requires the ARN of the Lambda function and supports
per-filter-config. When the per-filter configuration is used, the target
cluster must be tagged with specific metadata. This indicates to the
filter whether to process the request or to skip it.

Lambda supports two invocation modes:
- Synchronous (Request-Response)
- Asynchronous (Event)

This initial version of the filter supports the synchronous mode only.
In a later iteration I'll add support for the asynchronous (Event-based)
version.

Signed-off-by: Marco Magdy <[email protected]>
  • Loading branch information
marcomagdy authored Mar 10, 2020
1 parent 5dde5d9 commit 8074010
Show file tree
Hide file tree
Showing 31 changed files with 1,033 additions and 12 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,4 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/filters/http/on_demand @dmitri-d @htuch @lambdai
/*/extensions/filters/network/local_ratelimit @mattklein123 @junr03
/*/extensions/filters/http/aws_request_signing @rgs1 @derekargueta @mattklein123 @marcomagdy
/*/extensions/filters/http/aws_lambda @mattklein123 @marcomagdy @lavignes
2 changes: 2 additions & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ proto_library(
"//envoy/config/filter/dubbo/router/v2alpha1:pkg",
"//envoy/config/filter/fault/v2:pkg",
"//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg",
"//envoy/config/filter/http/aws_lambda/v2alpha:pkg",
"//envoy/config/filter/http/aws_request_signing/v2alpha:pkg",
"//envoy/config/filter/http/buffer/v2:pkg",
"//envoy/config/filter/http/cache/v2alpha:pkg",
Expand Down Expand Up @@ -160,6 +161,7 @@ proto_library(
"//envoy/extensions/common/tap/v3:pkg",
"//envoy/extensions/filters/common/fault/v3:pkg",
"//envoy/extensions/filters/http/adaptive_concurrency/v3:pkg",
"//envoy/extensions/filters/http/aws_lambda/v3:pkg",
"//envoy/extensions/filters/http/aws_request_signing/v3:pkg",
"//envoy/extensions/filters/http/buffer/v3:pkg",
"//envoy/extensions/filters/http/cache/v3alpha:pkg",
Expand Down
1 change: 1 addition & 0 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ proto_library(
"//envoy/config/filter/dubbo/router/v2alpha1:pkg",
"//envoy/config/filter/fault/v2:pkg",
"//envoy/config/filter/http/adaptive_concurrency/v2alpha:pkg",
"//envoy/config/filter/http/aws_lambda/v2alpha:pkg",
"//envoy/config/filter/http/aws_request_signing/v2alpha:pkg",
"//envoy/config/filter/http/buffer/v2:pkg",
"//envoy/config/filter/http/cache/v2alpha:pkg",
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/config/filter/http/aws_lambda/v2alpha/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# DO NOT EDIT. This file is generated by tools/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"],
)
36 changes: 36 additions & 0 deletions api/envoy/config/filter/http/aws_lambda/v2alpha/aws_lambda.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
syntax = "proto3";

package envoy.config.filter.http.aws_lambda.v2alpha;

import "udpa/annotations/status.proto";

import "udpa/annotations/migrate.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.config.filter.http.aws_lambda.v2alpha";
option java_outer_classname = "AwsLambdaProto";
option java_multiple_files = true;
option (udpa.annotations.file_migrate).move_to_package =
"envoy.extensions.filters.http.aws_lambda.v3";
option (udpa.annotations.file_status).work_in_progress = true;

// [#protodoc-title: AWS Lambda]
// AWS Lambda :ref:`configuration overview <config_http_filters_aws_lambda>`.
// [#extension: envoy.filters.http.aws_lambda]

// AWS Lambda filter config
message Config {
// The ARN of the AWS Lambda to invoke when the filter is engaged
// Must be in the following format:
// arn:<partition>:lambda:<region>:<account-number>:function:<function-name>
string arn = 1 [(validate.rules).string = {min_len: 1}];

// Whether to transform the request (headers and body) to a JSON payload or pass it as is.
bool payload_passthrough = 2;
}

// Per-route configuration for AWS Lambda. This can be useful when invoking a different Lambda function or a different
// version of the same Lambda depending on the route.
message PerRouteConfig {
Config invoke_config = 1;
}
12 changes: 12 additions & 0 deletions api/envoy/extensions/filters/http/aws_lambda/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DO NOT EDIT. This file is generated by tools/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/filter/http/aws_lambda/v2alpha:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
38 changes: 38 additions & 0 deletions api/envoy/extensions/filters/http/aws_lambda/v3/aws_lambda.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
syntax = "proto3";

package envoy.extensions.filters.http.aws_lambda.v3;

import "udpa/annotations/versioning.proto";

import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.http.aws_lambda.v3";
option java_outer_classname = "AwsLambdaProto";
option java_multiple_files = true;

// [#protodoc-title: AWS Lambda]
// AWS Lambda :ref:`configuration overview <config_http_filters_aws_lambda>`.
// [#extension: envoy.filters.http.aws_lambda]

// AWS Lambda filter config
message Config {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.http.aws_lambda.v2alpha.Config";

// The ARN of the AWS Lambda to invoke when the filter is engaged
// Must be in the following format:
// arn:<partition>:lambda:<region>:<account-number>:function:<function-name>
string arn = 1 [(validate.rules).string = {min_len: 1}];

// Whether to transform the request (headers and body) to a JSON payload or pass it as is.
bool payload_passthrough = 2;
}

// Per-route configuration for AWS Lambda. This can be useful when invoking a different Lambda function or a different
// version of the same Lambda depending on the route.
message PerRouteConfig {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.http.aws_lambda.v2alpha.PerRouteConfig";

Config invoke_config = 1;
}
116 changes: 116 additions & 0 deletions docs/root/configuration/http/http_filters/aws_lambda_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

.. _config_http_filters_aws_lambda:

AWS Lambda
==========

* :ref:`v2 API reference <envoy_api_msg_config.filter.http.aws_lambda.v2alpha.config>`
* This filter should be configured with the name *envoy.filters.http.aws_lambda*.

.. attention::

The AWS Lambda filter is currently under active development.

The HTTP AWS Lambda filter is used to trigger an AWS Lambda function from a standard HTTP/1.x or HTTP/2 request.
It supports a few options to control whether to pass through the HTTP request payload as is or to wrap it in a JSON
schema.

If :ref:`payload_passthrough <envoy_api_msg_config.filter.http.aws_lambda.v2alpha.config>` is set to
``true``, then the payload is sent to Lambda without any transformations.
*Note*: This means you lose access to all the HTTP headers in the Lambda function.

However, if :ref:`payload_passthrough <envoy_api_msg_config.filter.http.aws_lambda.v2alpha.config>`
is set to ``false``, then the HTTP request is transformed to a JSON (the details of the JSON transformation will be
documented once that feature is implemented).

The filter supports :ref:`per-filter configuration
<envoy_api_msg_config.filter.http.aws_lambda.v2alpha.PerRouteConfig>`.
Below are some examples the show how the filter can be used in different deployment scenarios.

Example configuration
---------------------

In this configuration, the filter applies to all routes in the filter chain of the http connection manager:

.. code-block:: yaml
http_filters:
- name: envoy.filters.http.aws_lambda
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.aws_lambda.v3.Config
arn: "arn:aws:lambda:us-west-2:987654321:function:hello_envoy"
payload_passthrough: true
The corresponding regional endpoint must be specified in the target cluster. So, for example if the Lambda function is
in us-west-2:

.. code-block:: yaml
clusters:
- name: lambda_egress_gateway
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: lambda_egress_gateway
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: lambda.us-west-2.amazonaws.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
sni: "*.amazonaws.com"
The filter can also be configured per virtual-host, route or weighted-cluster. In that case, the target cluster *must*
have specific Lambda metadata.

.. code-block:: yaml
weighted_clusters:
clusters:
- name: lambda_egress_gateway
weight: 42
typed_per_filter_config:
envoy.filters.http.aws_lambda:
"@type": type.googleapis.com/envoy.extensions.filters.http.aws_lambda.v3.PerRouteConfig
invoke_config:
arn: "arn:aws:lambda:us-west-2:987654321:function:hello_envoy"
payload_passthrough: false
An example with the Lambda metadata applied to a weighted-cluster:

.. code-block:: yaml
clusters:
- name: lambda_egress_gateway
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
metadata:
filter_metadata:
com.amazonaws.lambda:
egress_gateway: true
load_assignment:
cluster_name: lambda_egress_gateway # does this have to match? seems redundant
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: lambda.us-west-2.amazonaws.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
sni: "*.amazonaws.com"
1 change: 1 addition & 0 deletions docs/root/configuration/http/http_filters/http_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ HTTP filters
:maxdepth: 2

adaptive_concurrency_filter
aws_lambda_filter
aws_request_signing_filter
buffer_filter
cors_filter
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions source/extensions/common/aws/signer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ class Signer {
* @throws EnvoyException if the request cannot be signed.
*/
virtual void sign(Http::RequestHeaderMap& headers) PURE;

/**
* Sign an AWS request.
* @param headers AWS API request headers.
* @param content_hash The Hex encoded SHA-256 of the body of the AWS API request.
* @throws EnvoyException if the request cannot be signed.
*/
virtual void sign(Http::RequestHeaderMap& headers, const std::string& content_hash) PURE;
};

using SignerPtr = std::unique_ptr<Signer>;
Expand Down
2 changes: 1 addition & 1 deletion source/extensions/common/aws/signer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ void SignerImpl::sign(Http::RequestHeaderMap& headers) {
}

void SignerImpl::sign(Http::RequestHeaderMap& headers, const std::string& content_hash) {
headers.setReferenceKey(SignatureHeaders::get().ContentSha256, content_hash);
const auto& credentials = credentials_provider_->getCredentials();
if (!credentials.accessKeyId() || !credentials.secretAccessKey()) {
// Empty or "anonymous" credentials are a valid use-case for non-production environments.
Expand Down Expand Up @@ -85,7 +86,6 @@ std::string SignerImpl::createContentHash(Http::RequestMessage& message, bool si
const auto content_hash = message.body()
? Hex::encode(crypto_util.getSha256Digest(*message.body()))
: SignatureConstants::get().HashedEmptyString;
message.headers().addCopy(SignatureHeaders::get().ContentSha256, content_hash);
return content_hash;
}

Expand Down
2 changes: 1 addition & 1 deletion source/extensions/common/aws/signer_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class SignerImpl : public Signer, public Logger::Loggable<Logger::Id::http> {
const std::map<std::string, std::string>& canonical_headers,
absl::string_view signature) const;

void sign(Http::RequestHeaderMap& headers, const std::string& content_hash);
void sign(Http::RequestHeaderMap& headers, const std::string& content_hash) override;

const std::string service_name_;
const std::string region_;
Expand Down
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ EXTENSIONS = {
#

"envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config",
"envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config",
"envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config",
"envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config",
"envoy.filters.http.cache": "//source/extensions/filters/http/cache:config",
Expand Down
Loading

0 comments on commit 8074010

Please sign in to comment.