forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
grpc: Add AWS IAM grpc credentials extension (envoyproxy#7532)
Signed-off-by: Scott LaVigne <[email protected]>
- Loading branch information
1 parent
04477ca
commit c92b8ba
Showing
16 changed files
with
439 additions
and
1 deletion.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
syntax = "proto3"; | ||
|
||
// [#protodoc-title: Grpc Credentials AWS IAM] | ||
// Configuration for AWS IAM Grpc Credentials Plugin | ||
|
||
package envoy.config.grpc_credential.v2alpha; | ||
|
||
option java_outer_classname = "AwsIamProto"; | ||
option java_package = "io.envoyproxy.envoy.config.grpc_credential.v2alpha"; | ||
option java_multiple_files = true; | ||
option go_package = "v2alpha"; | ||
|
||
import "validate/validate.proto"; | ||
|
||
message AwsIamConfig { | ||
// The `service namespace | ||
// <https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#genref-aws-service-namespaces>`_ | ||
// of the Grpc endpoint. | ||
// | ||
// Example: appmesh | ||
string service_name = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
||
// The `region <https://docs.aws.amazon.com/general/latest/gr/rande.html>`_ hosting the Grpc | ||
// endpoint. If unspecified, the extension will use the value in the ``AWS_REGION`` environment | ||
// variable. | ||
// | ||
// Example: us-west-2 | ||
string region = 2; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,4 @@ Extensions | |
resource_monitor/resource_monitor | ||
common/common | ||
cluster/cluster | ||
grpc_credential/grpc_credential |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Grpc Credentials | ||
================ | ||
|
||
.. toctree:: | ||
:glob: | ||
:maxdepth: 1 | ||
|
||
v2alpha/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
licenses(["notice"]) # Apache 2 | ||
|
||
# AWS IAM gRPC Credentials | ||
|
||
load( | ||
"//bazel:envoy_build_system.bzl", | ||
"envoy_cc_library", | ||
"envoy_package", | ||
) | ||
|
||
envoy_package() | ||
|
||
envoy_cc_library( | ||
name = "config", | ||
srcs = ["config.cc"], | ||
hdrs = ["config.h"], | ||
external_deps = ["grpc"], | ||
deps = [ | ||
"//include/envoy/grpc:google_grpc_creds_interface", | ||
"//include/envoy/registry", | ||
"//source/common/common:assert_lib", | ||
"//source/common/config:utility_lib", | ||
"//source/common/grpc:google_grpc_creds_lib", | ||
"//source/common/http:message_lib", | ||
"//source/common/http:utility_lib", | ||
"//source/extensions/filters/http/common/aws:credentials_provider_impl_lib", | ||
"//source/extensions/filters/http/common/aws:region_provider_impl_lib", | ||
"//source/extensions/filters/http/common/aws:signer_impl_lib", | ||
"//source/extensions/filters/http/common/aws:utility_lib", | ||
"//source/extensions/grpc_credentials:well_known_names", | ||
"@envoy_api//envoy/config/grpc_credential/v2alpha:aws_iam_cc", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
#include "extensions/grpc_credentials/aws_iam/config.h" | ||
|
||
#include "envoy/api/v2/core/grpc_service.pb.h" | ||
#include "envoy/common/exception.h" | ||
#include "envoy/config/grpc_credential/v2alpha/aws_iam.pb.validate.h" | ||
#include "envoy/grpc/google_grpc_creds.h" | ||
#include "envoy/registry/registry.h" | ||
|
||
#include "common/config/utility.h" | ||
#include "common/grpc/google_grpc_creds_impl.h" | ||
#include "common/http/utility.h" | ||
#include "common/protobuf/message_validator_impl.h" | ||
|
||
#include "extensions/filters/http/common/aws/credentials_provider_impl.h" | ||
#include "extensions/filters/http/common/aws/region_provider_impl.h" | ||
#include "extensions/filters/http/common/aws/signer_impl.h" | ||
#include "extensions/filters/http/common/aws/utility.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace GrpcCredentials { | ||
namespace AwsIam { | ||
|
||
std::shared_ptr<grpc::ChannelCredentials> AwsIamGrpcCredentialsFactory::getChannelCredentials( | ||
const envoy::api::v2::core::GrpcService& grpc_service_config, Api::Api& api) { | ||
|
||
const auto& google_grpc = grpc_service_config.google_grpc(); | ||
std::shared_ptr<grpc::ChannelCredentials> creds = | ||
Grpc::CredsUtility::defaultSslChannelCredentials(grpc_service_config, api); | ||
|
||
std::shared_ptr<grpc::CallCredentials> call_creds; | ||
for (const auto& credential : google_grpc.call_credentials()) { | ||
switch (credential.credential_specifier_case()) { | ||
case envoy::api::v2::core::GrpcService::GoogleGrpc::CallCredentials::kFromPlugin: { | ||
if (credential.from_plugin().name() == GrpcCredentialsNames::get().AwsIam) { | ||
AwsIamGrpcCredentialsFactory credentials_factory; | ||
const Envoy::ProtobufTypes::MessagePtr config_message = | ||
Envoy::Config::Utility::translateToFactoryConfig( | ||
credential.from_plugin(), ProtobufMessage::getNullValidationVisitor(), | ||
credentials_factory); | ||
const auto& config = Envoy::MessageUtil::downcastAndValidate< | ||
const envoy::config::grpc_credential::v2alpha::AwsIamConfig&>(*config_message); | ||
auto credentials_provider = | ||
std::make_shared<HttpFilters::Common::Aws::DefaultCredentialsProviderChain>( | ||
api, HttpFilters::Common::Aws::Utility::metadataFetcher); | ||
auto signer = std::make_unique<HttpFilters::Common::Aws::SignerImpl>( | ||
config.service_name(), getRegion(config), credentials_provider, api.timeSource()); | ||
std::shared_ptr<grpc::CallCredentials> new_call_creds = grpc::MetadataCredentialsFromPlugin( | ||
std::make_unique<AwsIamHeaderAuthenticator>(std::move(signer))); | ||
if (call_creds == nullptr) { | ||
call_creds = new_call_creds; | ||
} else { | ||
call_creds = grpc::CompositeCallCredentials(call_creds, new_call_creds); | ||
} | ||
} | ||
break; | ||
} | ||
default: | ||
// unused credential types | ||
continue; | ||
} | ||
} | ||
|
||
if (call_creds != nullptr) { | ||
return grpc::CompositeChannelCredentials(creds, call_creds); | ||
} | ||
|
||
return creds; | ||
} | ||
|
||
std::string AwsIamGrpcCredentialsFactory::getRegion( | ||
const envoy::config::grpc_credential::v2alpha::AwsIamConfig& config) { | ||
std::unique_ptr<HttpFilters::Common::Aws::RegionProvider> region_provider; | ||
if (!config.region().empty()) { | ||
region_provider = | ||
std::make_unique<HttpFilters::Common::Aws::StaticRegionProvider>(config.region()); | ||
} else { | ||
region_provider = std::make_unique<HttpFilters::Common::Aws::EnvironmentRegionProvider>(); | ||
} | ||
|
||
if (!region_provider->getRegion().has_value()) { | ||
throw EnvoyException("Could not determine AWS region. " | ||
"If you are not running Envoy in EC2 or ECS, " | ||
"provide the region in the plugin configuration."); | ||
} | ||
|
||
return *region_provider->getRegion(); | ||
} | ||
|
||
grpc::Status | ||
AwsIamHeaderAuthenticator::GetMetadata(grpc::string_ref service_url, grpc::string_ref method_name, | ||
const grpc::AuthContext&, | ||
std::multimap<grpc::string, grpc::string>* metadata) { | ||
|
||
auto message = buildMessageToSign(absl::string_view(service_url.data(), service_url.length()), | ||
absl::string_view(method_name.data(), method_name.length())); | ||
|
||
try { | ||
signer_->sign(message, false); | ||
} catch (const EnvoyException& e) { | ||
return grpc::Status(grpc::StatusCode::INTERNAL, e.what()); | ||
} | ||
|
||
signedHeadersToMetadata(message.headers(), *metadata); | ||
|
||
return grpc::Status::OK; | ||
} | ||
|
||
Http::RequestMessageImpl | ||
AwsIamHeaderAuthenticator::buildMessageToSign(absl::string_view service_url, | ||
absl::string_view method_name) { | ||
|
||
const auto uri = fmt::format("{}/{}", service_url, method_name); | ||
absl::string_view host; | ||
absl::string_view path; | ||
Http::Utility::extractHostPathFromUri(uri, host, path); | ||
|
||
Http::RequestMessageImpl message; | ||
message.headers().insertMethod().value().setReference(Http::Headers::get().MethodValues.Post); | ||
message.headers().insertHost().value(host); | ||
message.headers().insertPath().value(path); | ||
|
||
return message; | ||
} | ||
|
||
void AwsIamHeaderAuthenticator::signedHeadersToMetadata( | ||
const Http::HeaderMap& headers, std::multimap<grpc::string, grpc::string>& metadata) { | ||
|
||
headers.iterate( | ||
[](const Http::HeaderEntry& entry, void* context) -> Http::HeaderMap::Iterate { | ||
auto* md = static_cast<std::multimap<grpc::string, grpc::string>*>(context); | ||
const auto& key = entry.key().getStringView(); | ||
// Skip pseudo-headers | ||
if (key.empty() || key[0] == ':') { | ||
return Http::HeaderMap::Iterate::Continue; | ||
} | ||
md->emplace(key, entry.value().getStringView()); | ||
return Http::HeaderMap::Iterate::Continue; | ||
}, | ||
&metadata); | ||
} | ||
|
||
REGISTER_FACTORY(AwsIamGrpcCredentialsFactory, Grpc::GoogleGrpcCredentialsFactory); | ||
|
||
} // namespace AwsIam | ||
} // namespace GrpcCredentials | ||
} // namespace Extensions | ||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#pragma once | ||
|
||
#include "envoy/config/grpc_credential/v2alpha/aws_iam.pb.validate.h" | ||
#include "envoy/grpc/google_grpc_creds.h" | ||
#include "envoy/http/header_map.h" | ||
|
||
#include "common/http/message_impl.h" | ||
|
||
#include "extensions/filters/http/common/aws/signer.h" | ||
#include "extensions/grpc_credentials/well_known_names.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace GrpcCredentials { | ||
namespace AwsIam { | ||
|
||
/** | ||
* AWS IAM based gRPC channel credentials factory. | ||
*/ | ||
class AwsIamGrpcCredentialsFactory : public Grpc::GoogleGrpcCredentialsFactory { | ||
public: | ||
std::shared_ptr<grpc::ChannelCredentials> | ||
getChannelCredentials(const envoy::api::v2::core::GrpcService& grpc_service_config, | ||
Api::Api& api) override; | ||
|
||
Envoy::ProtobufTypes::MessagePtr createEmptyConfigProto() { | ||
return std::make_unique<envoy::config::grpc_credential::v2alpha::AwsIamConfig>(); | ||
} | ||
|
||
std::string name() const override { return GrpcCredentialsNames::get().AwsIam; } | ||
|
||
private: | ||
static std::string getRegion(const envoy::config::grpc_credential::v2alpha::AwsIamConfig& config); | ||
}; | ||
|
||
/** | ||
* Produce AWS IAM signature metadata for a gRPC call. | ||
*/ | ||
class AwsIamHeaderAuthenticator : public grpc::MetadataCredentialsPlugin { | ||
public: | ||
AwsIamHeaderAuthenticator(HttpFilters::Common::Aws::SignerPtr signer) | ||
: signer_(std::move(signer)) {} | ||
|
||
grpc::Status GetMetadata(grpc::string_ref, grpc::string_ref, const grpc::AuthContext&, | ||
std::multimap<grpc::string, grpc::string>* metadata) override; | ||
|
||
bool IsBlocking() const override { return true; } | ||
|
||
private: | ||
static Http::RequestMessageImpl buildMessageToSign(absl::string_view service_url, | ||
absl::string_view method_name); | ||
|
||
static void signedHeadersToMetadata(const Http::HeaderMap& headers, | ||
std::multimap<grpc::string, grpc::string>& metadata); | ||
|
||
const HttpFilters::Common::Aws::SignerPtr signer_; | ||
}; | ||
|
||
} // namespace AwsIam | ||
} // namespace GrpcCredentials | ||
} // namespace Extensions | ||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_test", | ||
"envoy_package", | ||
"envoy_select_google_grpc", | ||
) | ||
|
||
envoy_package() | ||
|
||
envoy_cc_test( | ||
name = "aws_iam_grpc_credentials_test", | ||
srcs = envoy_select_google_grpc(["aws_iam_grpc_credentials_test.cc"]), | ||
data = ["//test/config/integration/certs"], | ||
deps = [ | ||
"//source/extensions/grpc_credentials:well_known_names", | ||
"//source/extensions/grpc_credentials/aws_iam:config", | ||
"//test/common/grpc:grpc_client_integration_test_harness_lib", | ||
"//test/integration:integration_lib", | ||
"@envoy_api//envoy/config/grpc_credential/v2alpha:aws_iam_cc", | ||
] + envoy_select_google_grpc(["//source/common/grpc:google_async_client_lib"]), | ||
) |
Oops, something went wrong.