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

Geoip filter #24318

Merged
merged 29 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from 11 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
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ extensions/filters/http/oauth2 @derekargueta @snowp
/*/extensions/load_balancing_policies/round_robin @wbpcode @UNOWNED
# Early header mutation
/*/extensions/http/early_header_mutation/header_mutation @wbpcode @UNOWNED
# IP Geolocation
/*/extensions/filters/http/geoip @nezdolik @mattklein123
Copy link
Member Author

Choose a reason for hiding this comment

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

@mattklein123 i will update the ownership once it's clear who is sponsoring the change

Copy link
Member Author

Choose a reason for hiding this comment

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

@ravenblackx should i put you instead of Matt as an owner?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess. It's not really my field, but it would make sense since having reviewed some of it I now have the best context to review the rest of it.


# Intentionally exempt (treated as core code)
/*/extensions/filters/common @UNOWNED @UNOWNED
Expand Down
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ proto_library(
"//envoy/extensions/filters/http/fault/v3:pkg",
"//envoy/extensions/filters/http/file_system_buffer/v3:pkg",
"//envoy/extensions/filters/http/gcp_authn/v3:pkg",
"//envoy/extensions/filters/http/geoip/v3:pkg",
"//envoy/extensions/filters/http/grpc_http1_bridge/v3:pkg",
"//envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3:pkg",
"//envoy/extensions/filters/http/grpc_json_transcoder/v3:pkg",
Expand Down
13 changes: 13 additions & 0 deletions api/envoy/extensions/filters/http/geoip/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
"@com_github_cncf_udpa//xds/annotations/v3:pkg",
],
)
78 changes: 78 additions & 0 deletions api/envoy/extensions/filters/http/geoip/v3/geoip.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
syntax = "proto3";

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

import "google/protobuf/any.proto";

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

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

option java_package = "io.envoyproxy.envoy.extensions.filters.http.geoip.v3";
option java_outer_classname = "GeoipProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/geoip/v3;geoipv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;
option (xds.annotations.v3.file_status).work_in_progress = true;

// [#protodoc-title: Geoip]
// Geoip :ref:`configuration overview <config_http_filters_geoip>`.
ravenblackx marked this conversation as resolved.
Show resolved Hide resolved
// [#extension: envoy.filters.http.geoip]

message Geoip {

// If set to true, the `xff_num_trusted_hops` field will be used to determine
// trusted client address from `x-forwarded-for` header.
// Otherwise, the immediate downstream connection source address will be used.
bool use_xff = 1;

// The number of additional ingress proxy hops from the right side of the
// :ref:`config_http_conn_man_headers_x-forwarded-for` HTTP header to trust when
// determining the origin client's IP address. The default is zero if this option
// is not specified. See the documentation for
// :ref:`config_http_conn_man_headers_x-forwarded-for` for more information.
uint32 xff_num_trusted_hops = 2;

// Configuration for geolocation headers to add to request.
GeolocationHeadersToAdd geo_headers_to_add = 3 [(validate.rules).message = {required: true}];

// Geolocation provider specific configuration.
GeolocationProvider provider = 4 [(validate.rules).message = {required: true}];

// The set of geolocation headers to add to request. If any of the configured headers is present
// in the incoming request, it will be overridden by Geoip filter.
message GeolocationHeadersToAdd {
// If set, the header will be used to populate the country ISO code associated with the IP address.
string country = 1 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the header will be used to populate the city associated with the IP address.
string city = 2 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the header will be used to populate the region ISO code associated with the IP address.
string region = 3 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the header will be used to populate the ASN associated with the IP address.
string asn = 4 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the IP address will be checked if it belongs to any type of anonymization network (e.g. VPN, public proxy etc)
// and header will be populated with the check result.
Copy link
Member

Choose a reason for hiding this comment

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

Can you specify the concrete values? Is it true/false? Something else? Same for all of the ones that aren't obviously strings.

string is_anon = 5 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the IP address will be checked if it belongs to a VPN and header will be populated with the check result.
string anon_vpn = 6 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the IP address will be checked if it belongs to a hosting provider and header will be populated with the check result.
string anon_hosting = 7 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the IP address will be checked if it belongs to a TOR exit node and header will be populated with the check result.
string anon_tor = 8 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
// If set, the IP address will be checked if it belongs to a public proxy and header will be populated with the check result.
string anon_proxy = 9 [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME ignore_empty: true}];
}

message GeolocationProvider {
// The name of the geoip driver to instantiate. The name must match a
// supported geoip driver.
string name = 1 [(validate.rules).string = {min_bytes: 1}];

// Geoip driver specific configuration which depends on the driver being instantiated.
oneof config_type {
ravenblackx marked this conversation as resolved.
Show resolved Hide resolved
google.protobuf.Any typed_config = 3;

This comment was marked as resolved.

}
}

}
2 changes: 2 additions & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ proto_library(
"//envoy/extensions/filters/http/fault/v3:pkg",
"//envoy/extensions/filters/http/file_system_buffer/v3:pkg",
"//envoy/extensions/filters/http/gcp_authn/v3:pkg",
"//envoy/extensions/filters/http/geoip/v3:pkg",
"//envoy/extensions/filters/http/grpc_http1_bridge/v3:pkg",
"//envoy/extensions/filters/http/grpc_http1_reverse_bridge/v3:pkg",
"//envoy/extensions/filters/http/grpc_json_transcoder/v3:pkg",
Expand Down Expand Up @@ -167,6 +168,7 @@ proto_library(
"//envoy/extensions/filters/udp/udp_proxy/v3:pkg",
"//envoy/extensions/formatter/metadata/v3:pkg",
"//envoy/extensions/formatter/req_without_query/v3:pkg",
"//envoy/extensions/geoip_providers:pkg",
"//envoy/extensions/health_checkers/redis/v3:pkg",
"//envoy/extensions/health_checkers/thrift/v3:pkg",
"//envoy/extensions/http/cache/file_system_http_cache/v3:pkg",
Expand Down
46 changes: 46 additions & 0 deletions docs/root/configuration/http/http_filters/geoip_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.. _config_http_filters_geoip:

IP Geolocation Filter
=========================
This filter decorates HTTP requests with the geolocation data.
Filter uses client address to lookup information (eg client's city, country) in the geolocation provider database.
Upon a successful lookup request will be enriched with the configured geolocation header and value from the database.
In case the configured geolocation headers are present in the incoming request, they will be overriden by the filter.
Geolocation filter emits stats for the number of successful lookups and the number of total lookups.

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

Statistics
----------
Geolocation filter outputs statistics in the
*http.<stat_prefix>.geoip.<geo-header-name>.* namespace. The :ref:`stat prefix
<envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stat_prefix>`
comes from the owning HTTP connection manager.

.. csv-table::
:header: Name, Type, Description
:widths: auto

.hit, Counter, Number of successful lookups within geolocation database for a configured geolocation header.
.total, Counter, Number of total lookups within geolocation database for a configured geolocation header.


Configuration example
---------------------

.. code-block:: yaml

name: envoy.filters.http.geoip
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.geoip.v3.Geoip
use_xff: true
xff_num_trusted_hops: 1
geo_headers_to_add:
country: "x-geo-country"
region: "x-geo-region"
provider:
name: "envoy.geoip_providers.maxmind"
Comment on lines +44 to +45
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't actually exist. Is this going to be added in some future PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's correct. I have initially suggested to split this work into implementing filter itself and then implementing the provider.


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 @@ -27,6 +27,7 @@ HTTP filters
fault_filter
file_system_buffer_filter
gcp_authn_filter
geoip_filter
golang_filter
grpc_http1_bridge_filter
grpc_http1_reverse_bridge_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 @@ -107,6 +107,7 @@ EXTENSIONS = {
"envoy.filters.http.fault": "//source/extensions/filters/http/fault:config",
"envoy.filters.http.file_system_buffer": "//source/extensions/filters/http/file_system_buffer:config",
"envoy.filters.http.gcp_authn": "//source/extensions/filters/http/gcp_authn:config",
"envoy.filters.http.geoip": "//source/extensions/filters/http/geoip:config",
"envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config",
"envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config",
"envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config",
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 @@ -238,6 +238,13 @@ envoy.filters.http.cdn_loop:
status: alpha
type_urls:
- envoy.extensions.filters.http.cdn_loop.v3.CdnLoopConfig
envoy.filters.http.geoip:
categories:
- envoy.filters.http
security_posture: unknown
status: wip
type_urls:
- envoy.extensions.filters.http.geoip.v3.Geoip
envoy.filters.http.upstream_codec:
categories:
- envoy.filters.http.upstream
Expand Down
59 changes: 59 additions & 0 deletions source/extensions/filters/http/geoip/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_extension",
"envoy_cc_library",
"envoy_extension_package",
)

licenses(["notice"]) # Apache 2

# HTTP L7 filter that decorates request with geolocation data
# Public docs: https://envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/geoip_filter

envoy_extension_package()

envoy_cc_library(
name = "geoip_filter_lib",
srcs = ["geoip_filter.cc"],
hdrs = ["geoip_filter.h"],
deps = [
":provider_config",
"//envoy/http:filter_interface",
"//envoy/runtime:runtime_interface",
"//source/common/common:assert_lib",
"//source/common/http:header_map_lib",
"//source/common/http:headers_lib",
"//source/common/http:utility_lib",
"//source/common/stats:symbol_table_lib",
"@envoy_api//envoy/extensions/filters/http/geoip/v3:pkg_cc_proto",
],
)

#todo(nezdolik) may need to split into interface and impl
envoy_cc_extension(
name = "provider_config",
hdrs = [
"geoip_provider_config.h",
"geoip_provider_config_impl.h",
],
deps = [
"//envoy/config:typed_config_interface",
"//envoy/network:address_interface",
"//envoy/protobuf:message_validator_interface",
"@envoy_api//envoy/extensions/filters/http/geoip/v3:pkg_cc_proto",
],
)

envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
":provider_config",
"//source/common/config:utility_lib",
"//source/common/protobuf:utility_lib",
"//source/extensions/filters/http/common:factory_base_lib",
"//source/extensions/filters/http/geoip:geoip_filter_lib",
"@envoy_api//envoy/extensions/filters/http/geoip/v3:pkg_cc_proto",
],
)
46 changes: 46 additions & 0 deletions source/extensions/filters/http/geoip/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "source/extensions/filters/http/geoip/config.h"

#include "envoy/registry/registry.h"

#include "source/common/config/utility.h"
#include "source/common/protobuf/utility.h"
#include "source/extensions/filters/http/geoip/geoip_filter.h"
#include "source/extensions/filters/http/geoip/geoip_provider_config_impl.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Geoip {

Http::FilterFactoryCb GeoipFilterFactory::createFilterFactoryFromProtoTyped(
const envoy::extensions::filters::http::geoip::v3::Geoip& proto_config,
const std::string& stat_prefix, Server::Configuration::FactoryContext& context) {
if (!provider_context_) {
provider_context_ =
std::make_unique<GeoipProviderFactoryContextImpl>(context.messageValidationVisitor());
}
GeoipFilterConfigSharedPtr filter_config(std::make_shared<GeoipFilterConfig>(
proto_config, stat_prefix, context.scope(), context.runtime()));

auto provider_config = proto_config.provider();
auto& geo_provider_factory =
Envoy::Config::Utility::getAndCheckFactory<GeoipProviderFactory>(provider_config);
ProtobufTypes::MessagePtr message = Envoy::Config::Utility::translateToFactoryConfig(
provider_config, context.messageValidationVisitor(), geo_provider_factory);
auto driver = geo_provider_factory.createGeoipProviderDriver(*message, provider_context_);

return [filter_config, driver](Http::FilterChainFactoryCallbacks& callbacks) -> void {
callbacks.addStreamDecoderFilter(std::make_shared<GeoipFilter>(filter_config, driver));
};
}

/**
* Static registration for geoip filter. @see RegisterFactory.
*/
REGISTER_FACTORY(GeoipFilterFactory,
Server::Configuration::NamedHttpFilterConfigFactory){"envoy.geoip"};

} // namespace Geoip
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
32 changes: 32 additions & 0 deletions source/extensions/filters/http/geoip/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include "envoy/extensions/filters/http/geoip/v3/geoip.pb.h"
#include "envoy/extensions/filters/http/geoip/v3/geoip.pb.validate.h"

#include "source/extensions/filters/http/common/factory_base.h"
#include "source/extensions/filters/http/geoip/geoip_provider_config.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Geoip {

/**
* Config registration for the geoip filter. @see NamedHttpFilterConfigFactory.
*/
class GeoipFilterFactory
: public Common::FactoryBase<envoy::extensions::filters::http::geoip::v3::Geoip> {
public:
GeoipFilterFactory() : FactoryBase("envoy.filters.http.geoip") {}

private:
Http::FilterFactoryCb createFilterFactoryFromProtoTyped(
const envoy::extensions::filters::http::geoip::v3::Geoip& proto_config,
const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override;
GeoipProviderFactoryContextPtr provider_context_;
};

} // namespace Geoip
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
Loading