Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ message BufferSettings {
// The authorization request will be dispatched and no 413 HTTP error will be returned by the
// filter.
bool allow_partial_message = 2;

// If true, the body sent to the external authorization service is set with raw bytes, it sets
// the :ref:`raw_body<envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.raw_body>`
// field of HTTP request attribute context. Otherwise, :ref:`
// body<envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.body>` will be filled
// with UTF-8 string request body.
bool pack_as_bytes = 3;
}

// HttpService is used for raw HTTP communication between the filter and the authorization service.
Expand Down

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

8 changes: 7 additions & 1 deletion api/envoy/service/auth/v3/attribute_context.proto
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ message AttributeContext {

// This message defines attributes for an HTTP request.
// HTTP/1.x, HTTP/2, gRPC are all considered as HTTP requests.
// [#next-free-field: 12]
// [#next-free-field: 13]
message HttpRequest {
option (udpa.annotations.versioning).previous_message_type =
"envoy.service.auth.v2.AttributeContext.HttpRequest";
Expand Down Expand Up @@ -145,6 +145,12 @@ message AttributeContext {

// The HTTP request body.
string body = 11;

// The HTTP request body in bytes. This is used instead of
// :ref:`body <envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.body>` when
// :ref:`pack_as_bytes <envoy_api_field_extensions.filters.http.ext_authz.v3.BufferSettings.pack_as_bytes>`
// is set to true.
bytes raw_body = 12;
}

// The source of a network activity, such as starting a TCP connection.
Expand Down
8 changes: 7 additions & 1 deletion api/envoy/service/auth/v4alpha/attribute_context.proto

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

34 changes: 33 additions & 1 deletion docs/root/configuration/http/http_filters/ext_authz_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ configuration options at
:ref:`HTTP filter <envoy_v3_api_msg_extensions.filters.http.ext_authz.v3.ExtAuthz>`.

Configuration Examples
-----------------------------
----------------------

A sample filter configuration for a gRPC authorization server:

Expand Down Expand Up @@ -60,6 +60,38 @@ A sample filter configuration for a gRPC authorization server:
# entire request.
connect_timeout: 0.25s

.. note::

One of the features of this filter is to send HTTP request body to the configured gRPC
authorization server as part of the :ref:`check request
<envoy_v3_api_msg_service.auth.v3.CheckRequest>`.

A sample configuration is as follows:

.. code:: yaml

http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
grpc_service:
envoy_grpc:
cluster_name: ext-authz
with_request_body:
max_request_bytes: 1024
allow_partial_message: true
pack_as_bytes: true

Please note that by default :ref:`check request<envoy_v3_api_msg_service.auth.v3.CheckRequest>`
carries the HTTP request body as UTF-8 string and it fills the :ref:`body
<envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.body>` field. To pack the request
body as raw bytes, it is needed to set :ref:`pack_as_bytes
<envoy_v3_api_field_extensions.filters.http.ext_authz.v3.BufferSettings.pack_as_bytes>` field to
true. In effect to that, the :ref:`raw_body
<envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.raw_body>`
field will be set and :ref:`body
<envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.body>` field will be empty.

A sample filter configuration for a raw HTTP authorization server:

.. code-block:: yaml
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ New Features
* dynamic_forward_proxy: added :ref:`use_tcp_for_dns_lookups<envoy_v3_api_field_extensions.common.dynamic_forward_proxy.v3.DnsCacheConfig.use_tcp_for_dns_lookups>` option to use TCP for DNS lookups in order to match the DNS options for :ref:`Clusters<envoy_v3_api_msg_config.cluster.v3.Cluster>`.
* ext_authz filter: added support for emitting dynamic metadata for both :ref:`HTTP <config_http_filters_ext_authz_dynamic_metadata>` and :ref:`network <config_network_filters_ext_authz_dynamic_metadata>` filters.
The emitted dynamic metadata is set by :ref:`dynamic metadata <envoy_v3_api_field_service.auth.v3.CheckResponse.dynamic_metadata>` field in a returned :ref:`CheckResponse <envoy_v3_api_msg_service.auth.v3.CheckResponse>`.
* ext_authz filter: added support for sending :ref:`raw bytes as request body <envoy_v3_api_field_service.auth.v3.AttributeContext.HttpRequest.raw_body>` of a gRPC check request by setting :ref:`pack_as_bytes <envoy_v3_api_field_extensions.filters.http.ext_authz.v3.BufferSettings.pack_as_bytes>` to true.
* grpc-json: support specifying `response_body` field in for `google.api.HttpBody` message.
* hds: added :ref:`cluster_endpoints_health <envoy_v3_api_field_service.health.v3.EndpointHealthResponse.cluster_endpoints_health>` to HDS responses, keeping endpoints in the same groupings as they were configured in the HDS specifier by cluster and locality instead of as a flat list.
* hds: added :ref:`transport_socket_matches <envoy_v3_api_field_service.health.v3.ClusterHealthCheck.transport_socket_matches>` to HDS cluster health check specifier, so the existing match filter :ref:`transport_socket_match_criteria <envoy_v3_api_field_config.core.v3.HealthCheck.transport_socket_match_criteria>` in the repeated field :ref:`health_checks <envoy_v3_api_field_service.health.v3.ClusterHealthCheck.health_checks>` has context to match against. This unblocks support for health checks over HTTPS and HTTP/2.
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.

22 changes: 15 additions & 7 deletions source/extensions/filters/common/ext_authz/check_request_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void CheckRequestUtils::setRequestTime(envoy::service::auth::v3::AttributeContex
void CheckRequestUtils::setHttpRequest(
envoy::service::auth::v3::AttributeContext::HttpRequest& httpreq, uint64_t stream_id,
const StreamInfo::StreamInfo& stream_info, const Buffer::Instance* decoding_buffer,
const Envoy::Http::RequestHeaderMap& headers, uint64_t max_request_bytes) {
const Envoy::Http::RequestHeaderMap& headers, uint64_t max_request_bytes, bool pack_as_bytes) {
httpreq.set_id(std::to_string(stream_id));
httpreq.set_method(getHeaderStr(headers.Method()));
httpreq.set_path(getHeaderStr(headers.Path()));
Expand Down Expand Up @@ -130,7 +130,15 @@ void CheckRequestUtils::setHttpRequest(
const uint64_t length = std::min(decoding_buffer->length(), max_request_bytes);
std::string data(length, 0);
decoding_buffer->copyOut(0, length, &data[0]);
httpreq.set_body(std::move(data));

// This pack_as_bytes flag allows us to switch the content type (bytes or string) of "body" to
// be sent to the external authorization server without doing string encoding check (in this
// case UTF-8 check).
if (pack_as_bytes) {
httpreq.set_raw_body(std::move(data));
} else {
httpreq.set_body(std::move(data));
}

// Add in a header to detect when a partial body is used.
(*mutable_headers)[Http::Headers::get().EnvoyAuthPartialBody.get()] =
Expand All @@ -141,18 +149,18 @@ void CheckRequestUtils::setHttpRequest(
void CheckRequestUtils::setAttrContextRequest(
envoy::service::auth::v3::AttributeContext::Request& req, const uint64_t stream_id,
const StreamInfo::StreamInfo& stream_info, const Buffer::Instance* decoding_buffer,
const Envoy::Http::RequestHeaderMap& headers, uint64_t max_request_bytes) {
const Envoy::Http::RequestHeaderMap& headers, uint64_t max_request_bytes, bool pack_as_bytes) {
setRequestTime(req, stream_info);
setHttpRequest(*req.mutable_http(), stream_id, stream_info, decoding_buffer, headers,
max_request_bytes);
max_request_bytes, pack_as_bytes);
}

void CheckRequestUtils::createHttpCheck(
const Envoy::Http::StreamDecoderFilterCallbacks* callbacks,
const Envoy::Http::RequestHeaderMap& headers,
Protobuf::Map<std::string, std::string>&& context_extensions,
envoy::config::core::v3::Metadata&& metadata_context,
envoy::service::auth::v3::CheckRequest& request, uint64_t max_request_bytes,
envoy::service::auth::v3::CheckRequest& request, uint64_t max_request_bytes, bool pack_as_bytes,
bool include_peer_certificate) {

auto attrs = request.mutable_attributes();
Expand All @@ -163,10 +171,10 @@ void CheckRequestUtils::createHttpCheck(
auto* cb = const_cast<Envoy::Http::StreamDecoderFilterCallbacks*>(callbacks);
setAttrContextPeer(*attrs->mutable_source(), *cb->connection(), service, false,
include_peer_certificate);
setAttrContextPeer(*attrs->mutable_destination(), *cb->connection(), "", true,
setAttrContextPeer(*attrs->mutable_destination(), *cb->connection(), EMPTY_STRING, true,
include_peer_certificate);
setAttrContextRequest(*attrs->mutable_request(), cb->streamId(), cb->streamInfo(),
cb->decodingBuffer(), headers, max_request_bytes);
cb->decodingBuffer(), headers, max_request_bytes, pack_as_bytes);

// Fill in the context extensions and metadata context.
(*attrs->mutable_context_extensions()) = std::move(context_extensions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ class CheckRequestUtils {
* check request.
* @param request is the reference to the check request that will be filled up.
* @param with_request_body when true, will add the request body to the check request.
* @param pack_as_bytes when true, will set the check request body as bytes.
* @param include_peer_certificate whether to include the peer certificate in the check request.
*/
static void createHttpCheck(const Envoy::Http::StreamDecoderFilterCallbacks* callbacks,
const Envoy::Http::RequestHeaderMap& headers,
Protobuf::Map<std::string, std::string>&& context_extensions,
envoy::config::core::v3::Metadata&& metadata_context,
envoy::service::auth::v3::CheckRequest& request,
uint64_t max_request_bytes, bool include_peer_certificate);
uint64_t max_request_bytes, bool pack_as_bytes,
bool include_peer_certificate);

/**
* createTcpCheck is used to extract the attributes from the network layer and fill them up
Expand All @@ -76,13 +78,13 @@ class CheckRequestUtils {
const uint64_t stream_id, const StreamInfo::StreamInfo& stream_info,
const Buffer::Instance* decoding_buffer,
const Envoy::Http::RequestHeaderMap& headers,
uint64_t max_request_bytes);
uint64_t max_request_bytes, bool pack_as_bytes);
static void setAttrContextRequest(envoy::service::auth::v3::AttributeContext::Request& req,
const uint64_t stream_id,
const StreamInfo::StreamInfo& stream_info,
const Buffer::Instance* decoding_buffer,
const Envoy::Http::RequestHeaderMap& headers,
uint64_t max_request_bytes);
uint64_t max_request_bytes, bool pack_as_bytes);
static std::string getHeaderStr(const Envoy::Http::HeaderEntry* entry);
static Envoy::Http::HeaderMap::Iterate fillHttpHeaders(const Envoy::Http::HeaderEntry&, void*);
};
Expand Down
3 changes: 2 additions & 1 deletion source/extensions/filters/http/ext_authz/ext_authz.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ void Filter::initiateCall(const Http::RequestHeaderMap& headers,

Filters::Common::ExtAuthz::CheckRequestUtils::createHttpCheck(
callbacks_, headers, std::move(context_extensions), std::move(metadata_context),
check_request_, config_->maxRequestBytes(), config_->includePeerCertificate());
check_request_, config_->maxRequestBytes(), config_->packAsBytes(),
config_->includePeerCertificate());

ENVOY_STREAM_LOG(trace, "ext_authz filter calling authorization server", *callbacks_);
state_ = State::Calling;
Expand Down
4 changes: 4 additions & 0 deletions source/extensions/filters/http/ext_authz/ext_authz.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class FilterConfig {
failure_mode_allow_(config.failure_mode_allow()),
clear_route_cache_(config.clear_route_cache()),
max_request_bytes_(config.with_request_body().max_request_bytes()),
pack_as_bytes_(config.with_request_body().pack_as_bytes()),
status_on_error_(toErrorCode(config.status_on_error().code())), scope_(scope),
runtime_(runtime), http_context_(http_context),
filter_enabled_(config.has_filter_enabled()
Expand Down Expand Up @@ -90,6 +91,8 @@ class FilterConfig {

uint32_t maxRequestBytes() const { return max_request_bytes_; }

bool packAsBytes() const { return pack_as_bytes_; }

Http::Code statusOnError() const { return status_on_error_; }

bool filterEnabled() { return filter_enabled_.has_value() ? filter_enabled_->enabled() : true; }
Expand Down Expand Up @@ -132,6 +135,7 @@ class FilterConfig {
const bool failure_mode_allow_;
const bool clear_route_cache_;
const uint32_t max_request_bytes_;
const bool pack_as_bytes_;
const Http::Code status_on_error_;
Stats::Scope& scope_;
Runtime::Loader& runtime_;
Expand Down
Loading