Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
10 changes: 10 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def _envoy_api_deps():
"rate_limit",
"router",
"transcoder",
"ext_authz",
]
for t in http_filter_bind_targets:
native.bind(
Expand All @@ -183,6 +184,7 @@ def _envoy_api_deps():
"redis_proxy",
"rate_limit",
"client_ssl_auth",
"ext_authz",
]
for t in network_filter_bind_targets:
native.bind(
Expand All @@ -197,6 +199,14 @@ def _envoy_api_deps():
name = "http_api_protos",
actual = "@googleapis//:http_api_protos",
)
auth_bind_targets = [
"auth"
]
for t in auth_bind_targets:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, the recent merges of envoyproxy/data-plane-api#421 and #2430 will break this (and other parts of your PR). Be prepared to update when you merge master or pull in an updated data-plane-api SHA.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the heads up. I'll do another merge with master to pull in the latest data-plane-api and rework the impacted code.

native.bind(
name = "envoy_api_auth_" + t,
actual = "@envoy_api//api/auth:" + t + "_cc",
)

def envoy_dependencies(path = "@envoy_deps//", skip_targets = []):
envoy_repository = repository_rule(
Expand Down
23 changes: 23 additions & 0 deletions include/envoy/ext_authz/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
licenses(["notice"]) # Apache 2

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "ext_authz_interface",
hdrs = ["ext_authz.h"],
external_deps = ["envoy_api_auth_auth"],
deps = [
"//include/envoy/common:optional",
"//include/envoy/http:filter_interface",
"//include/envoy/http:header_map_interface",
"//include/envoy/network:filter_interface",
"//include/envoy/tracing:http_tracer_interface",
],
)

102 changes: 102 additions & 0 deletions include/envoy/ext_authz/ext_authz.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#pragma once

#include <chrono>
#include <memory>
#include <string>
#include <vector>

#include "envoy/common/optional.h"
#include "envoy/common/pure.h"
#include "envoy/http/filter.h"
#include "envoy/http/header_map.h"
#include "envoy/network/filter.h"
#include "envoy/tracing/http_tracer.h"

#include "api/auth/external_auth.pb.h"

namespace Envoy {
namespace ExtAuthz {

/**
* Possible async results for a check call.
*/
enum class CheckStatus {
// The request is authorized.
OK,
// The authz service could not be queried.
Error,
// The request is denied.
Denied
};

/**
* Async callbacks used during check() calls.
*/
class RequestCallbacks {
public:
virtual ~RequestCallbacks() {}

/**
* Called when a check request is complete. The resulting status is supplied.
*/
virtual void complete(CheckStatus status) PURE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think onComplete is a clearer name.

};


class Client {
public:
// Destructor
virtual ~Client() {}

/**
* Cancel an inflight Check request.
*/
virtual void cancel() PURE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: can there only ever be a single inflight request per client with this interface? Does this imply that each request must have its own Client?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this a bit more, maybe we don't need to do the TLS thing at all here. This is unary RPC, so we don't have a significant cost in rebuilding the client each time, it should be a fairly small construction/destruction cost. I think what you have in this PR is fine, I would be more concerned if we had a bidi streaming auth check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that the per-thread gRPC would save us setup time and hence would help even in the unary gRPC case. But if the cluster is going to do that for us then I agree changing it would not buy us much.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, for Envoy gRPC, this will come for free from the cluster pool management. In theory, Google gRPC also does this, based on good authority, but I haven't validated. Let's work on your other PRs first and we can circle back to this later.


// A check call.
virtual void check(RequestCallbacks &callback, const envoy::api::v2::auth::CheckRequest& request,
Tracing::Span& parent_span) PURE;

};

typedef std::unique_ptr<Client> ClientPtr;

/**
* An interface for creating a external authorization client.
*/
class ClientFactory {
public:
virtual ~ClientFactory() {}

/**
* Return a new authz client.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param docs for all parameters on include/envoy interface headers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also @return for the return type & text.

*/
virtual ClientPtr create(const Optional<std::chrono::milliseconds>& timeout) PURE;
};

typedef std::unique_ptr<ClientFactory> ClientFactoryPtr;


/**
* An interface for creating ext_authz.proto (authorization) request.
* CheckRequestGenerator is used to extract attributes from the TCP/HTTP request
* and fill out the details in the authorization protobuf that is sent to authorization
* service.
*/
class CheckRequestGenerator {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really a helper utility class. Generally we don't put these in include/envoy. A litmus test is "will I need to mock this class?" and "will this class have potentially more than one implementation?". If no to both, put it in the source/common subtree.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I had it in the souce/common subtree. When I started writing the UT's for the filters I had to mock it. That's when I moved it here.
The usage in http filter is here

I think that the mock is needed. @htuch Should I just keep it here or move to source/common and have the mock reference it from there? please confirm. thx.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need to mock the proto creation? These are basically straight line pure functions. Personally, I would mock the input parameters, since it's just as easy to do and results in easier to understand code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i know what you mean here. Let me try to redo the ut's without using the mock.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@htuch last commit removes the mock for proto creation.

public:
// Destructor
virtual ~CheckRequestGenerator() {}

virtual void createHttpCheck(const Envoy::Http::StreamDecoderFilterCallbacks* callbacks,
const Envoy::Http::HeaderMap &headers,
envoy::api::v2::auth::CheckRequest& request) PURE;
virtual void createTcpCheck(const Network::ReadFilterCallbacks* callbacks,
envoy::api::v2::auth::CheckRequest& request) PURE;
};

typedef std::unique_ptr<CheckRequestGenerator> CheckRequestGeneratorPtr;

} // namespace ExtAuthz
} // namespace Envoy

4 changes: 3 additions & 1 deletion include/envoy/request_info/request_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ enum ResponseFlag {
FaultInjected = 0x400,
// Request was ratelimited locally by rate limit filter.
RateLimited = 0x800,
// Request was unauthorized.
Unauthorized = 0x1000,
// ATTENTION: MAKE SURE THIS REMAINS EQUAL TO THE LAST FLAG.
LastFlag = RateLimited
LastFlag = Unauthorized
};

/**
Expand Down
9 changes: 8 additions & 1 deletion source/common/access_log/grpc_access_log_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void HttpGrpcAccessLog::responseFlagsToAccessLogResponseFlags(
envoy::api::v2::filter::accesslog::AccessLogCommon& common_access_log,
const RequestInfo::RequestInfo& request_info) {

static_assert(RequestInfo::ResponseFlag::LastFlag == 0x800,
static_assert(RequestInfo::ResponseFlag::LastFlag == 0x1000,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like gRPC access logging protos should be enhanced for unauthorized? Can you add a TODO and take care of this in one of your follow ups?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. I'll do it in the follow up pr; thanks.

"A flag has been added. Fix this code.");

if (request_info.getResponseFlag(RequestInfo::ResponseFlag::FailedLocalHealthCheck)) {
Expand Down Expand Up @@ -131,6 +131,13 @@ void HttpGrpcAccessLog::responseFlagsToAccessLogResponseFlags(
if (request_info.getResponseFlag(RequestInfo::ResponseFlag::RateLimited)) {
common_access_log.mutable_response_flags()->set_rate_limited(true);
}

// TODO(saumoh): Uncomment once unauthorization has been added to accesslog.proto
//
// if (request_info.getResponseFlag(RequestInfo::ResponseFlag::Unauthorized)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove all of this for now and keep it locally. It will bit rot otherwise.

// common_access_log.mutable_response_flags()->set_unauthorized(true);
// }

}

void HttpGrpcAccessLog::log(const Http::HeaderMap* request_headers,
Expand Down
9 changes: 7 additions & 2 deletions source/common/config/well_known_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ class NetworkFilterNameValues {
const std::string REDIS_PROXY = "envoy.redis_proxy";
// IP tagging filter
const std::string TCP_PROXY = "envoy.tcp_proxy";
// Authorization filter
const std::string EXT_AUTHORIZATION = "envoy.ext_authz";

// Converts names from v1 to v2
const V1Converter v1_converter_;

NetworkFilterNameValues()
: v1_converter_({CLIENT_SSL_AUTH, ECHO, HTTP_CONNECTION_MANAGER, MONGO_PROXY, RATE_LIMIT,
REDIS_PROXY, TCP_PROXY}) {}
REDIS_PROXY, TCP_PROXY, EXT_AUTHORIZATION}) {}
};

typedef ConstSingleton<NetworkFilterNameValues> NetworkFilterNames;
Expand Down Expand Up @@ -117,13 +119,16 @@ class HttpFilterNameValues {
const std::string HEALTH_CHECK = "envoy.health_check";
// Lua filter
const std::string LUA = "envoy.lua";
// External Authorization filter
const std::string EXT_AUTHORIZATION = "envoy.ext_authz";


// Converts names from v1 to v2
const V1Converter v1_converter_;

HttpFilterNameValues()
: v1_converter_({BUFFER, CORS, DYNAMO, FAULT, GRPC_HTTP1_BRIDGE, GRPC_JSON_TRANSCODER,
GRPC_WEB, HEALTH_CHECK, IP_TAGGING, RATE_LIMIT, ROUTER, LUA}) {}
GRPC_WEB, HEALTH_CHECK, IP_TAGGING, RATE_LIMIT, ROUTER, LUA, EXT_AUTHORIZATION}) {}
};

typedef ConstSingleton<HttpFilterNameValues> HttpFilterNames;
Expand Down
30 changes: 30 additions & 0 deletions source/common/ext_authz/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
licenses(["notice"]) # Apache 2

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "ext_authz_lib",
srcs = ["ext_authz_impl.cc"],
hdrs = ["ext_authz_impl.h"],
deps = [
"//include/envoy/ext_authz:ext_authz_interface",
"//include/envoy/grpc:async_client_interface",
"//include/envoy/grpc:async_client_manager_interface",
"//include/envoy/http:protocol_interface",
"//include/envoy/network:address_interface",
"//include/envoy/network:connection_interface",
"//include/envoy/upstream:cluster_manager_interface",
"//include/envoy/ssl:connection_interface",
"//source/common/common:assert_lib",
"//source/common/grpc:async_client_lib",
"//source/common/http:headers_lib",
"//source/common/tracing:http_tracer_lib",
],
)

Loading