Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Credential Injector Filter: OAuth2 client credential extension #33702

Merged
merged 55 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
0b65ec0
Credential Injector Filter: Oauth2 client credential extension
vikaschoudhary16 Apr 18, 2024
b024b40
Add tests
vikaschoudhary16 Apr 23, 2024
dfdab41
format
vikaschoudhary16 Apr 24, 2024
5d4f9be
proto update
vikaschoudhary16 Apr 24, 2024
a77ede1
doc and gcc fix
vikaschoudhary16 Apr 24, 2024
e9d10ce
docs
vikaschoudhary16 Apr 24, 2024
a8c6b64
more tests and configuration knob for retry interval
vikaschoudhary16 Apr 26, 2024
844d7f4
format
vikaschoudhary16 Apr 26, 2024
2fdd985
format
vikaschoudhary16 Apr 26, 2024
cc9d30f
retrigger ci
vikaschoudhary16 Apr 26, 2024
9999d38
test
vikaschoudhary16 Apr 27, 2024
d8d2470
test
vikaschoudhary16 Apr 27, 2024
537c671
docs update
vikaschoudhary16 Apr 29, 2024
4859865
enable/disable instead recreating timer
vikaschoudhary16 Apr 29, 2024
25d7bf3
retrigger ci
vikaschoudhary16 Apr 29, 2024
89c03c3
config test
vikaschoudhary16 Apr 29, 2024
4630b1d
yamllint
vikaschoudhary16 Apr 29, 2024
aaa3755
path update
vikaschoudhary16 Apr 29, 2024
c9d822f
try adding credentials file
vikaschoudhary16 Apr 29, 2024
55a1aa4
access log
vikaschoudhary16 Apr 29, 2024
b79f55b
update path
vikaschoudhary16 Apr 29, 2024
89b499f
more updates
vikaschoudhary16 Apr 29, 2024
611a682
add secrets for test env
vikaschoudhary16 Apr 30, 2024
834b995
add secrets dir
vikaschoudhary16 Apr 30, 2024
7e6158c
fix another rule
vikaschoudhary16 Apr 30, 2024
56077bc
fix file count and address timer feedback
vikaschoudhary16 Apr 30, 2024
54eea71
remove debugging changes
vikaschoudhary16 Apr 30, 2024
d05d2ad
fix
vikaschoudhary16 Apr 30, 2024
7efd2fd
dont use statelessMockServerContext
vikaschoudhary16 Apr 30, 2024
a6e730b
revert mockServerContext and docs changes to see if tests pass
vikaschoudhary16 Apr 30, 2024
f267d62
revert references to example yamls
vikaschoudhary16 Apr 30, 2024
d169f97
revert the reverts
vikaschoudhary16 Apr 30, 2024
2e8eec8
retrigger ci
vikaschoudhary16 Apr 30, 2024
a4da20f
Merge branch 'main' into client-cred-oauth2
vikaschoudhary16 Apr 30, 2024
df3af64
Use static secret in docs and more tests
vikaschoudhary16 May 1, 2024
e9dd790
format
vikaschoudhary16 May 1, 2024
8c57894
format
vikaschoudhary16 May 1, 2024
876bb97
fix
vikaschoudhary16 May 1, 2024
7cab926
feedback
vikaschoudhary16 May 1, 2024
a158867
retrigger ci
vikaschoudhary16 May 1, 2024
32a4ba7
more tests
vikaschoudhary16 May 2, 2024
71a73dd
format
vikaschoudhary16 May 2, 2024
f684155
fix tsan
vikaschoudhary16 May 2, 2024
7f298cf
Address feedback: docs and request lifetime
vikaschoudhary16 May 2, 2024
99af0c5
title line
vikaschoudhary16 May 2, 2024
9c36381
fix doc
vikaschoudhary16 May 2, 2024
da8fb23
More tests
vikaschoudhary16 May 3, 2024
c8c50fe
format
vikaschoudhary16 May 3, 2024
9a09099
Address feedback
vikaschoudhary16 May 7, 2024
958268c
revert accidental commit
vikaschoudhary16 May 7, 2024
fb25af4
Merge branch 'main' into client-cred-oauth2
vikaschoudhary16 May 7, 2024
faacc74
Move extension tests to under http/extensions
vikaschoudhary16 May 8, 2024
a7dd4cf
add teardown
vikaschoudhary16 May 9, 2024
3cc083f
tear down
vikaschoudhary16 May 9, 2024
eb73004
asan
vikaschoudhary16 May 9, 2024
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
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123
/*/extensions/filters/http/credential_injector @zhaohuabing @kyessenov
/*/extensions/http/injected_credentials/common @zhaohuabing @kyessenov
/*/extensions/http/injected_credentials/generic @zhaohuabing @kyessenov
/*/extensions/http/injected_credentials/oauth2 @vikaschoudhary16 @wbpcode

# Lua cluster specifier
/*/extensions/router/cluster_specifiers/lua @StarryVae @wbpcode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package envoy.extensions.http.injected_credentials.oauth2.v3;
import "envoy/config/core/v3/http_uri.proto";
import "envoy/extensions/transport_sockets/tls/v3/secret.proto";

import "google/protobuf/duration.proto";

import "xds/annotations/v3/status.proto";

import "udpa/annotations/status.proto";
Expand All @@ -18,7 +20,6 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
option (xds.annotations.v3.file_status).work_in_progress = true;

// [#protodoc-title: OAuth2 Credential]
// [#not-implemented-hide:]
// [#extension: envoy.http.injected_credentials.oauth2]

// OAuth2 extension can be used to retrieve an OAuth2 access token from an authorization server and inject it into the
Expand Down Expand Up @@ -67,4 +68,9 @@ message OAuth2 {
// Refer to [RFC 6749: The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749#section-4.4) for details.
ClientCredentials client_credentials = 3;
}

// The interval between two successive retries to fetch token from Identity Provider. Default is 2 secs.
// The interval must be at least 1 second.
google.protobuf.Duration token_fetch_retry_interval = 4
[(validate.rules).duration = {gte {seconds: 1}}];
}
3 changes: 0 additions & 3 deletions docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ filegroup(
"root/**/envoy-dynamic-filesystem-demo.yaml",
"root/operations/_include/traffic_tapping_*.yaml",
"root/configuration/http/http_filters/_include/checksum_filter.yaml",
# TODO(phlax/windows-dev): figure out how to get this working on windows
# "Error: unable to read file: /etc/ssl/certs/ca-certificates.crt"
"root/configuration/http/http_filters/_include/credential-injector-filter.yaml",
phlax marked this conversation as resolved.
Show resolved Hide resolved
"root/configuration/http/http_filters/_include/dns-cache-circuit-breaker.yaml",
"root/configuration/other_features/_include/dlb.yaml",
"root/configuration/other_features/_include/hyperscan_matcher.yaml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ static_resources:
"@type": type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic
credential:
name: credential
sds_config:
path_config_source:
path: /home/ubuntu/credential.yaml
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Expand All @@ -55,3 +52,13 @@ static_resources:
socket_address:
address: 127.0.0.1
port_value: 8080

secrets:
phlax marked this conversation as resolved.
Show resolved Hide resolved
- name: credential
generic_secret:
secret:
inline_string: "Basic base64EncodedUsernamePassword"
- name: credential-bearer
generic_secret:
secret:
inline_string: "Bearer myToken"
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: local-backend-service
http_filters:
- name: envoy.filters.http.credential_injector
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector
credential:
name: envoy.http.injected_credentials.oauth2
typed_config:
"@type": type.googleapis.com/envoy.extensions.http.injected_credentials.oauth2.v3.OAuth2
token_endpoint:
cluster: okta.ad
timeout: 3s
uri: "https://dev-1178504991.okta.com/oauth2/default/v1/token"
client_credentials:
client_id: some-client-id
client_secret:
name: client-secret
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
phlax marked this conversation as resolved.
Show resolved Hide resolved

clusters:
- lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: okta.ad
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: dev-1178504991.okta.com
port_value: 443
name: okta.ad
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: dev-1178504991.okta.com
type: LOGICAL_DNS
- name: local-backend-service
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: local-backend-service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8000

secrets:
phlax marked this conversation as resolved.
Show resolved Hide resolved
- name: client-secret
generic_secret:
secret:
inline_string: "lL0FWQUMnWizwG0JPop3ccEaC1pNZn5uYYnTbVQM"
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
.. _config_http_filters_credential_injector:

Credential Injector
Credential injector
===================

The credential injector HTTP filter serves the purpose of injecting credentials into outgoing HTTP requests.

The filter configuration is used to retrieve the credentials, or they can be fetched from a remote source
such as an OAuth2 authorization server. The credentials obtained are then injected into the ``Authorization``
header of the proxied HTTP requests, utilizing either the ``Basic`` or ``Bearer`` scheme.

Notice: This filter is intended to be used for workload authentication, which means that the identity associated
with the inserted credential is considered as the identity of the workload behind the Envoy proxy (in this case,
Envoy is typically deployed as a sidecar alongside that workload).
Expand All @@ -24,40 +20,58 @@ Configuration
* This filter should be configured with the type URL ``type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector``.
* :ref:`v3 API reference <envoy_v3_api_msg_extensions.filters.http.credential_injector.v3.CredentialInjector>`

Currently the filter supports :ref:`generic <envoy_v3_api_msg_extensions.http.injected_credentials.generic.v3.Generic>` only.
Other credential types can be supported as extensions.
The filter is configured with one of the following supported ``credential_injector`` extensions. Extensions are responsible for fetching the credentials
from the source. The credentials obtained are then injected into the ``Authorization`` header of the proxied HTTP requests, utilizing either the ``Basic``
or ``Bearer`` scheme.

Generic credential injector
---------------------------
* This extension should be configured with the type URL ``type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic``.
* :ref:`generic <envoy_v3_api_msg_extensions.http.injected_credentials.generic.v3.Generic>`

Here is an example configuration with Generic credential, which injects an HTTP Basic Auth credential into the proxied requests.

.. literalinclude:: _include/credential-injector-filter.yaml
.. literalinclude:: _include/credential-injector-generic-filter.yaml
:language: yaml
:lines: 28-41
:caption: :download:`credential-injector-filter.yaml <_include/credential-injector-filter.yaml>`
:lines: 29-39
:linenos:
:lineno-start: 29
:caption: :download:`credential-injector-filter.yaml <_include/credential-injector-generic-filter.yaml>`


credential.yaml for Basic Auth:
Credential which is being used to inject a ``Basic Auth`` credential into the proxied requests:

.. code-block:: yaml
.. literalinclude:: _include/credential-injector-generic-filter.yaml
:language: yaml
:lines: 57-60
:linenos:
:lineno-start: 57
:caption: :download:`credential-injector-filter.yaml <_include/credential-injector-generic-filter.yaml>`

resources:
- "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
name: credential
generic_secret:
secret:
inline_string: "Basic base64EncodedUsernamePassword"
It can also be configured to inject a ``Bearer`` token into the proxied requests.

It can also be configured to inject a Bearer token into the proxied requests.
Credential for ``Bearer`` token:

credential.yaml for Bearer Token:
.. literalinclude:: _include/credential-injector-generic-filter.yaml
:language: yaml
:lines: 61-64
:linenos:
:lineno-start: 61
:caption: :download:`credential-injector-filter.yaml <_include/credential-injector-generic-filter.yaml>`

.. code-block:: yaml
OAuth2 credential injector (client credential grant)
----------------------------------------------------
* This extension should be configured with the type URL ``type.googleapis.com/envoy.extensions.http.injected_credentials.oauth2.v3.OAuth2``.
* :ref:`oauth2 client credentials grant <envoy_v3_api_msg_extensions.http.injected_credentials.oauth2.v3.OAuth2>`

resources:
- "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
name: credential
generic_secret:
secret:
inline_string: "Bearer myToken"
Here is an example configuration with OAuth2 client credential injector, which injects an OAuth2 token into the proxied requests.

.. literalinclude:: _include/credential-injector-oauth2-filter.yaml
:language: yaml
:lines: 25-39
:linenos:
:lineno-start: 25
:caption: :download:`credential-injector-filter.yaml <_include/credential-injector-oauth2-filter.yaml>`

Statistics
----------
Expand All @@ -71,3 +85,18 @@ The HTTP credential injector filter outputs statistics in the ``http.<stat_prefi
``injected``, Counter, Total number of requests with injected credentials
``failed``, Counter, Total number of requests that failed to inject credentials
``already_exists``, Counter, Total number of requests that already had credentials and overwrite is false

OAuth2 client credential injector extension specific statistics are also emitted in the ``http.<stat_prefix>.credential_injector.oauth2.`` namespace.

.. csv-table::
:header: Name, Type, Description
:widths: 1, 1, 2

``token_requested``, Counter, Total number of token requests sent to the OAuth2 server
``token_fetched``, Counter, Total number of successful token fetches from the OAuth2 server
``token_fetch_failed_on_client_secret``, Counter, Total number of times token request not sent due to missing client secret
``token_fetch_failed_on_cluster_not_found``, Counter, Total number of times token request not sent due to missing OAuth2 server cluster
``token_fetch_failed_on_bad_response_code``, Counter, Total number of times OAuth2 server responded with non-200 response code
``token_fetch_failed_on_bad_token``, Counter, Total number of times OAuth2 server responded with bad token
``token_fetch_failed_on_stream_reset``, Counter, Total number of times http stream with OAuth2 server got reset

1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ EXTENSIONS = {
#

"envoy.http.injected_credentials.generic": "//source/extensions/http/injected_credentials/generic:config",
"envoy.http.injected_credentials.oauth2": "//source/extensions/http/injected_credentials/oauth2:config",

#
# QUIC extensions
Expand Down
7 changes: 7 additions & 0 deletions source/extensions/extensions_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,13 @@ envoy.http.injected_credentials.generic:
status: alpha
type_urls:
- envoy.extensions.http.injected_credentials.generic.v3.Generic
envoy.http.injected_credentials.oauth2:
categories:
- envoy.http.injected_credentials
security_posture: unknown
status: alpha
type_urls:
- envoy.extensions.http.injected_credentials.oauth2.v3.OAuth2
envoy.internal_redirect_predicates.allow_listed_routes:
categories:
- envoy.internal_redirect_predicates
Expand Down
10 changes: 6 additions & 4 deletions source/extensions/filters/http/credential_injector/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ CredentialInjectorFilterFactory::createFilterFactoryFromProtoTyped(
proto_config.credential().typed_config(), context.messageValidationVisitor(),
*config_factory);
CredentialInjectorSharedPtr credential_injector =
config_factory->createCredentialInjectorFromProto(*message, context);
config_factory->createCredentialInjectorFromProto(
*message, stats_prefix + "credential_injector.", context);

FilterConfigSharedPtr config = std::make_shared<FilterConfig>(
std::move(credential_injector), proto_config.overwrite(),
proto_config.allow_request_without_credential(), stats_prefix, context.scope());
FilterConfigSharedPtr config =
std::make_shared<FilterConfig>(std::move(credential_injector), proto_config.overwrite(),
proto_config.allow_request_without_credential(),
stats_prefix + "credential_injector.", context.scope());
return [config](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void {
callbacks.addStreamDecoderFilter(std::make_shared<CredentialInjectorFilter>(config));
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ FilterConfig::FilterConfig(CredentialInjectorSharedPtr credential_injector, bool
Stats::Scope& scope)
: injector_(credential_injector), overwrite_(overwrite),
allow_request_without_credential_(allow_request_without_credential),
stats_(generateStats(stats_prefix + "credential_injector.", scope)) {}
stats_(generateStats(stats_prefix, scope)) {}

// Inject configured credential to the HTTP request header.
// return true if successful
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class NamedCredentialInjectorConfigFactory : public Config::TypedFactory {
public:
virtual CredentialInjectorSharedPtr
createCredentialInjectorFromProto(const Protobuf::Message& config,
const std::string& stats_prefix,
Server::Configuration::FactoryContext& context) PURE;

std::string category() const override { return "envoy.http.injected_credentials"; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ class CredentialInjectorFactoryBase : public NamedCredentialInjectorConfigFactor
public:
CredentialInjectorSharedPtr
createCredentialInjectorFromProto(const Protobuf::Message& proto_config,
const std::string& stats_prefix,
Server::Configuration::FactoryContext& context) override {
return createCredentialInjectorFromProtoTyped(
MessageUtil::downcastAndValidate<const ConfigProto&>(proto_config,
context.messageValidationVisitor()),
context);
stats_prefix, context);
}

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
Expand All @@ -34,7 +35,7 @@ class CredentialInjectorFactoryBase : public NamedCredentialInjectorConfigFactor

private:
virtual CredentialInjectorSharedPtr
createCredentialInjectorFromProtoTyped(const ConfigProto&,
createCredentialInjectorFromProtoTyped(const ConfigProto&, const std::string&,
Server::Configuration::FactoryContext&) PURE;

const std::string name_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ secretsProvider(const envoy::extensions::transport_sockets::tls::v3::SdsSecretCo

Common::CredentialInjectorSharedPtr
GenericCredentialInjectorFactory::createCredentialInjectorFromProtoTyped(
const Generic& config, Server::Configuration::FactoryContext& context) {
const Generic& config, const std::string& /*stats_prefix*/,
Server::Configuration::FactoryContext& context) {
const auto& credential_secret = config.credential();
auto& server_context = context.serverFactoryContext();
auto& cluster_manager = server_context.clusterManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class GenericCredentialInjectorFactory : public Common::CredentialInjectorFactor

private:
Common::CredentialInjectorSharedPtr
createCredentialInjectorFromProtoTyped(const Generic& config,
createCredentialInjectorFromProtoTyped(const Generic& config, const std::string& stats_prefix,
Server::Configuration::FactoryContext& context) override;
};

Expand Down
Loading