Skip to content

Commit

Permalink
auth: new api auth implementation (#36968)
Browse files Browse the repository at this point in the history
Commit Message: auth: new api auth implementation
Additional Description:

To close #34877

Risk Level: low. New extension.
Testing: unit, integration.
Docs Changes: added.
Release Notes: added.
Platform Specific Features: n/a.

---------

Signed-off-by: wangbaiping/wbpcode <[email protected]>
Signed-off-by: wangbaiping(wbpcode) <[email protected]>
Signed-off-by: code <[email protected]>
Co-authored-by: Adi (Suissa) Peleg <[email protected]>
Co-authored-by: yanavlasov <[email protected]>
  • Loading branch information
3 people authored Dec 10, 2024
1 parent b7c429f commit d2dbc05
Show file tree
Hide file tree
Showing 23 changed files with 1,748 additions and 24 deletions.
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123
/*/extensions/filters/http/bandwidth_limit @nitgoy @mattklein123 @yanavlasov @tonya11en
# HTTP Basic Auth
/*/extensions/filters/http/basic_auth @zhaohuabing @wbpcode
# HTTP API Key Auth
/*/extensions/filters/http/api_key_auth @wbpcode @sanposhiho
# Original IP detection
/*/extensions/http/original_ip_detection/custom_header @alyssawilk @mattklein123
/*/extensions/http/original_ip_detection/xff @alyssawilk @mattklein123
Expand Down
1 change: 0 additions & 1 deletion api/envoy/extensions/filters/http/api_key_auth/v3/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"@com_github_cncf_xds//udpa/annotations:pkg",
"@com_github_cncf_xds//xds/annotations/v3:pkg",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ syntax = "proto3";

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

import "envoy/config/core/v3/base.proto";

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

import "udpa/annotations/sensitive.proto";
import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.http.api_key_auth.v3";
option java_outer_classname = "ApiKeyAuthProto";
Expand All @@ -17,38 +16,88 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
option (xds.annotations.v3.file_status).work_in_progress = true;

// [#protodoc-title: APIKey Auth]
// [#not-implemented-hide:]
// APIKey Auth :ref:`configuration overview <config_http_filters_api_key_auth>`.
// [#extension: envoy.filters.http.api_key_auth]

// API Key HTTP authentication.
//
// Example:
// For example, the following configuration configures the filter to authenticate the clients using
// the API key from the header ``X-API-KEY``. And only the clients with the key ``real-key`` are
// considered as authenticated.
//
// .. code-block:: yaml
//
// authentication_header: "X-API-KEY"
// keys:
// inline_string: |-
// clientID1:apiKey1
// clientID2:apiKey2
// credentials:
// - key: real-key
// client: user
// key_sources:
// - header: "X-API-KEY"
//
message APIKeyAuth {
// keys used to authenticate the client.
// It should be a map of clientID to apiKey.
// The clientID serves solely for identification purposes and isn't used for authentication.
config.core.v3.DataSource keys = 1 [(udpa.annotations.sensitive) = true];
message ApiKeyAuth {
// The credentials that are used to authenticate the clients.
repeated Credential credentials = 1 [(udpa.annotations.sensitive) = true];

// The key sources to fetch the key from the coming request.
repeated KeySource key_sources = 2;
}

// API key auth configuration of per route or per virtual host or per route configuration.
message ApiKeyAuthPerRoute {
// The credentials that are used to authenticate the clients. If this field is non-empty, then the
// credentials in the filter level configuration will be ignored and the credentials in this
// configuration will be used.
repeated Credential credentials = 1 [(udpa.annotations.sensitive) = true];

// The key sources to fetch the key from the coming request. If this field is non-empty, then the
// key sources in the filter level configuration will be ignored and the key sources in this
// configuration will be used.
repeated KeySource key_sources = 2;

// A list of clients that are allowed to access the route or vhost. The clients listed here
// should be subset of the clients listed in the ``credentials`` to provide authorization control
// after the authentication is successful. If the list is empty, then all authenticated clients
// are allowed. This provides very limited but simple authorization. If more complex authorization
// is required, then use the :ref:`HTTP RBAC filter <config_http_filters_rbac>` instead.
//
// .. note::
// Setting this field and ``credentials`` at the same configuration entry is not an error but
// also makes no much sense because they provide similar functionality. Please only use
// one of them at same configuration entry except for the case that you want to share the same
// credentials list across multiple routes but still use different allowed clients for each
// route.
//
repeated string allowed_clients = 3;
}

// Single credential entry that contains the API key and the related client id.
message Credential {
// The value of the unique API key.
string key = 1 [(validate.rules).string = {min_len: 1}];

// The unique id or identity that used to identify the client or consumer.
string client = 2 [(validate.rules).string = {min_len: 1}];
}

// The header name to fetch the key.
// If multiple values are present in the given header, the filter rejects the request.
// Only one of authentication_header, authentication_query, or authentication_cookie should be set.
string authentication_header = 2;
message KeySource {
// The header name to fetch the key. If multiple header values are present, the first one will be
// used. If the header value starts with 'Bearer ', this prefix will be stripped to get the
// key value.
//
// If set, takes precedence over ``query`` and ``cookie``.
string header = 1
[(validate.rules).string =
{max_len: 1024 well_known_regex: HTTP_HEADER_NAME strict: false ignore_empty: true}];

// The query parameter name to fetch the key.
// Only one of authentication_header, authentication_query, or authentication_cookie should be set.
string authentication_query = 3;
// The query parameter name to fetch the key. If multiple query values are present, the first one
// will be used.
//
// The field will be used if ``header`` is not set. If set, takes precedence over ``cookie``.
string query = 2 [(validate.rules).string = {max_len: 1024}];

// The cookie name to fetch the key.
// Only one of authentication_header, authentication_query, or authentication_cookie should be set.
string authentication_cookie = 4;
//
// The field will be used if the ``header`` and ``query`` are not set.
string cookie = 3
[(validate.rules).string =
{max_len: 1024 well_known_regex: HTTP_HEADER_NAME strict: false ignore_empty: true}];
}
4 changes: 4 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ new_features:
change: |
Added support for dynamic cluster selection in UDP proxy. The cluster can be set by one of the session filters
by setting a per-session state object under the key ``envoy.udp_proxy.cluster``.
- area: filters
change: |
Added :ref:`the Api Key Auth filter <envoy_v3_api_msg_extensions.filters.http.api_key_auth.v3.ApiKeyAuth>`, which
can be used to authenticate requests using an API key.
- area: CEL-attributes
change: |
Added :ref:`attribute <arch_overview_attributes>` ``upstream.request_attempt_count``
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
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:
path: "/admin"
route:
cluster: upstream_com
typed_per_filter_config:
api_key_auth:
"@type": type.googleapis.com/envoy.extensions.filters.http.api_key_auth.v3.ApiKeyAuthPerRoute
key_sources:
- query: api_key
allowed_clients:
- another_client
- match:
path: "/special"
route:
cluster: upstream_com
typed_per_filter_config:
api_key_auth:
"@type": type.googleapis.com/envoy.extensions.filters.http.api_key_auth.v3.ApiKeyAuthPerRoute
credentials:
- key: special_key
client: special_client
key_sources:
- header: X-Special-Key
- match:
prefix: "/static"
route:
cluster: upstream_com
typed_per_filter_config:
api_key_auth:
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
disabled: true
- match:
prefix: "/"
route:
cluster: upstream_com
http_filters:
- name: api_key_auth
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.api_key_auth.v3.ApiKeyAuth
credentials:
- key: one_key
client: one_client
- key: another_key
client: another_client
key_sources:
- header: Authorization
- name: envoy.filters.http.router
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: upstream_com
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service_upstream_com
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: upstream.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: upstream.com
94 changes: 94 additions & 0 deletions docs/root/configuration/http/http_filters/api_key_auth_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
.. _config_http_filters_api_key_auth:

API key auth
============

This HTTP filter can be used to authenticate users based on the unique API key. The filter will
extract the API keys from either an HTTP header, a parameter query, or a cookie and verify them against
the configured credential list.

If the API key is valid and the related client is allowed, the request will be allowed to continue.
If the API key is invalid or not exists, the request will be denied with 401 status code.
If the API key is valid but the related client is not allowed, the request will be denied with
403 status code.

Configuration
-------------

* This filter should be configured with the type URL ``type.googleapis.com/envoy.extensions.filters.http.api_key_auth.v3.ApiKeyAuth``.
* :ref:`v3 API reference <envoy_v3_api_msg_extensions.filters.http.api_key_auth.v3.ApiKeyAuth>`

An example configuration of the filter may look like the following:

.. literalinclude:: _include/api-key-auth-filter.yaml
:language: yaml
:lines: 57-66
:linenos:
:caption: :download:`api-key-auth-filter.yaml <_include/api-key-auth-filter.yaml>`

Per-Route Configuration
-----------------------

It's possible to override the filter's configuration for a specific scope like a route or virtual host.
And the overriding configuration could be partial to override only credential list or to override only
the API key source.

And this filter also provides very limited authorization control. A simple ``allowed_clients`` could be
configured for specific scope like a route or virtual host to allow or deny specific clients.

An example scope specific configuration of the filter may look like the following:

.. literalinclude:: _include/api-key-auth-filter.yaml
:language: yaml
:lines: 16-55
:linenos:
:caption: :download:`api-key-auth-filter.yaml <_include/api-key-auth-filter.yaml>`

In this example we customize key source for ``/admin`` route and only allow limited clients to access
this route. We also customize the credential list for ``/special`` route and disable the filter for
``/static`` route.

Combining the per-route configuration example and the filter configuration example, given the following
requests, the filter will behave as follows:

.. code-block:: text
# The request will be allowed because the API key is valid and the client is allowed.
GET /admin?api_key=another_key HTTP/1.1
host: example.com
# The request will be denied with 403 status code because the API key is valid but the client is
# not allowed.
GET /admin?api_key=one_key HTTP/1.1
host: example.com
# The request will be denied with 401 status code because the API key is invalid.
GET /admin?api_key=invalid_key HTTP/1.1
host: example.com
# The request will be allowed because the API key is valid and no client validation is configured.
GET /special HTTP/1.1
host: example.com
X-Special-Key: "special_key"
# The request will be allowed because the filter is disabled for specific route.
GET /static HTTP/1.1
host: example.com
# The request will be allowed because the API key is valid and no client validation is configured.
GET / HTTP/1.1
host: example.com
Authorization: "Bearer one_key"
Statistics
----------

The HTTP API key auth filter outputs statistics in the ``http.<stat_prefix>.api_key_auth.`` namespace.

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

allowed, Counter, Total number of allowed requests
unauthorized, Counter, Total number of requests that have invalid API key
forbidden, Counter, Total number of requests that have valid API key but not allowed
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 @@ -9,6 +9,7 @@ HTTP filters
adaptive_concurrency_filter
admission_control_filter
aws_lambda_filter
api_key_auth_filter
aws_request_signing_filter
bandwidth_limit_filter
basic_auth_filter
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 @@ -133,6 +133,7 @@ EXTENSIONS = {
"envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config",
"envoy.filters.http.admission_control": "//source/extensions/filters/http/admission_control:config",
"envoy.filters.http.alternate_protocols_cache": "//source/extensions/filters/http/alternate_protocols_cache:config",
"envoy.filters.http.api_key_auth": "//source/extensions/filters/http/api_key_auth: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.bandwidth_limit": "//source/extensions/filters/http/bandwidth_limit:config",
Expand Down
8 changes: 8 additions & 0 deletions source/extensions/extensions_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ envoy.filters.http.basic_auth:
type_urls:
- envoy.extensions.filters.http.basic_auth.v3.BasicAuth
- envoy.extensions.filters.http.basic_auth.v3.BasicAuthPerRoute
envoy.filters.http.api_key_auth:
categories:
- envoy.filters.http
security_posture: robust_to_untrusted_downstream
status: alpha
type_urls:
- envoy.extensions.filters.http.api_key_auth.v3.ApiKeyAuth
- envoy.extensions.filters.http.api_key_auth.v3.ApiKeyAuthPerRoute
envoy.filters.http.buffer:
categories:
- envoy.filters.http
Expand Down
Loading

0 comments on commit d2dbc05

Please sign in to comment.