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

Add filter state IP matcher #37637

Merged
merged 14 commits into from
Jan 14, 2025
Merged
22 changes: 22 additions & 0 deletions api/envoy/type/matcher/v3/address.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
syntax = "proto3";

package envoy.type.matcher.v3;

import "xds/core/v3/cidr.proto";

import "udpa/annotations/status.proto";

option java_package = "io.envoyproxy.envoy.type.matcher.v3";
option java_outer_classname = "AddressProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3;matcherv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Address Matcher]

// Match an IP against a repeated CIDR range. This matcher is intended to be
// used in other matchers, for example in the filter state matcher to match a
// filter state object as an IP.
message AddressMatcher {
repeated xds.core.v3.CidrRange ranges = 1;
}
4 changes: 4 additions & 0 deletions api/envoy/type/matcher/v3/filter_state.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";

package envoy.type.matcher.v3;

import "envoy/type/matcher/v3/address.proto";
import "envoy/type/matcher/v3/string.proto";

import "udpa/annotations/status.proto";
Expand All @@ -25,5 +26,8 @@ message FilterStateMatcher {

// Matches the filter state object as a string value.
StringMatcher string_match = 2;

// Matches the filter state object as a ip Instance.
AddressMatcher address_match = 3;
}
}
6 changes: 6 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ new_features:
- area: grpc-json
change: |
Added a new http filter for :ref:`gRPC to JSON transcoding <config_http_filters_grpc_json_reverse_transcoder>`.
- area: matchers
change: |
Added new filter state matcher ip_range to
:ref:`FilterStateMatcher <envoy_v3_api_msg_type.matcher.v3.FilterStateMatcher>` which attempts to
cast the filter state object to an IP and match it against a list of CidrRanges. To support
this, also added an :ref:`AddressMatcher <envoy_v3_api_msg_type.matcher.v3.AddressMatcher>`.
- area: attributes
change: |
added new ``xds.virtual_host_name`` and ``xds.virtual_host_metadata`` attributes support. See
Expand Down
1 change: 1 addition & 0 deletions docs/root/api-v3/types/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ Types
../type/v3/token_bucket.proto
../type/matcher/v3/value.proto
../type/matcher/v3/status_code_input.proto
../type/matcher/v3/address.proto
2 changes: 2 additions & 0 deletions envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ envoy_cc_library(
hdrs = ["address.h"],
deps = [
"//envoy/common:base_includes",
"//envoy/common:optref_lib",
"//envoy/common:pure_lib",
"//envoy/stream_info:filter_state_interface",
],
)

Expand Down
16 changes: 16 additions & 0 deletions envoy/network/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include <memory>
#include <string>

#include "envoy/common/optref.h"
#include "envoy/common/platform.h"
#include "envoy/common/pure.h"
#include "envoy/stream_info/filter_state.h"

#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
Expand All @@ -23,6 +25,7 @@ namespace Address {

class Instance;
using InstanceConstSharedPtr = std::shared_ptr<const Instance>;
using InstanceConstOptRef = OptRef<const Instance>;

/**
* Interface for an Ipv4 address.
Expand Down Expand Up @@ -238,6 +241,19 @@ class Instance {
virtual const Network::SocketInterface& socketInterface() const PURE;
};

/*
* Used to store Instance in filter state.
*/
class InstanceAccessor : public Envoy::StreamInfo::FilterState::Object {
public:
InstanceAccessor(InstanceConstSharedPtr ip) : ip_(std::move(ip)) {}

InstanceConstOptRef getIp() const { return makeOptRefFromPtr<const Instance>(ip_.get()); }

private:
InstanceConstSharedPtr ip_;
};

} // namespace Address
} // namespace Network
} // namespace Envoy
14 changes: 14 additions & 0 deletions source/common/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ envoy_cc_library(
deps = [
":utility_lib",
"//envoy/common:matchers_interface",
"//source/common/common:filter_state_object_matchers_lib",
"//source/common/common:regex_lib",
"//source/common/config:metadata_lib",
"//source/common/config:utility_lib",
Expand All @@ -333,6 +334,19 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "filter_state_object_matchers_lib",
srcs = ["filter_state_object_matchers.cc"],
hdrs = ["filter_state_object_matchers.h"],
deps = [
"//envoy/common:exception_lib",
"//envoy/common:matchers_interface",
"//envoy/common:pure_lib",
"//source/common/network:cidr_range_interface",
"@com_google_absl//absl/status:statusor",
],
)

envoy_cc_library(
name = "random_generator_lib",
srcs = [
Expand Down
37 changes: 37 additions & 0 deletions source/common/common/filter_state_object_matchers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "source/common/common/filter_state_object_matchers.h"

#include "envoy/common/exception.h"
#include "envoy/network/address.h"
#include "envoy/stream_info/filter_state.h"

#include "source/common/network/cidr_range.h"

#include "absl/status/statusor.h"
#include "xds/core/v3/cidr.pb.h"

namespace Envoy {
namespace Matchers {

FilterStateIpRangeMatcher::FilterStateIpRangeMatcher(
std::unique_ptr<Network::Address::IpList>&& ip_list)
: ip_list_(std::move(ip_list)) {}

bool FilterStateIpRangeMatcher::match(const StreamInfo::FilterState::Object& object) const {
const Network::Address::InstanceAccessor* ip =
dynamic_cast<const Network::Address::InstanceAccessor*>(&object);
if (ip == nullptr) {
return false;
}
return ip_list_->contains(*ip->getIp());
}

FilterStateStringMatcher::FilterStateStringMatcher(StringMatcherPtr&& string_matcher)
: string_matcher_(std::move(string_matcher)) {}

bool FilterStateStringMatcher::match(const StreamInfo::FilterState::Object& object) const {
const auto string_value = object.serializeAsString();
return string_value && string_matcher_->match(*string_value);
}

} // namespace Matchers
} // namespace Envoy
41 changes: 41 additions & 0 deletions source/common/common/filter_state_object_matchers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <memory>

#include "envoy/common/matchers.h"
#include "envoy/common/pure.h"
#include "envoy/stream_info/filter_state.h"

#include "source/common/network/cidr_range.h"

namespace Envoy {
namespace Matchers {

class FilterStateObjectMatcher {
public:
virtual bool match(const StreamInfo::FilterState::Object& object) const PURE;
virtual ~FilterStateObjectMatcher() = default;
};

using FilterStateObjectMatcherPtr = std::unique_ptr<FilterStateObjectMatcher>;

class FilterStateIpRangeMatcher : public FilterStateObjectMatcher {
public:
FilterStateIpRangeMatcher(std::unique_ptr<Network::Address::IpList>&& ip_list);
bool match(const StreamInfo::FilterState::Object& object) const override;

private:
std::unique_ptr<Envoy::Network::Address::IpList> ip_list_;
};

class FilterStateStringMatcher : public FilterStateObjectMatcher {
public:
FilterStateStringMatcher(StringMatcherPtr&& string_matcher);
bool match(const StreamInfo::FilterState::Object& object) const override;

private:
const StringMatcherPtr string_matcher_;
};

} // namespace Matchers
} // namespace Envoy
40 changes: 31 additions & 9 deletions source/common/common/matchers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
#include "envoy/type/matcher/v3/string.pb.h"
#include "envoy/type/matcher/v3/value.pb.h"

#include "source/common/common/filter_state_object_matchers.h"
#include "source/common/common/macros.h"
#include "source/common/common/regex.h"
#include "source/common/config/metadata.h"
#include "source/common/config/utility.h"
#include "source/common/http/path_utility.h"

#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "filter_state_object_matchers.h"

namespace Envoy {
namespace Matchers {
Expand Down Expand Up @@ -122,31 +125,50 @@ MetadataMatcher::MetadataMatcher(const envoy::type::matcher::v3::MetadataMatcher
}

namespace {
StringMatcherPtr valueMatcherFromProto(const envoy::type::matcher::v3::FilterStateMatcher& matcher,
Server::Configuration::CommonFactoryContext& context) {

absl::StatusOr<FilterStateObjectMatcherPtr>
filterStateObjectMatcherFromProto(const envoy::type::matcher::v3::FilterStateMatcher& matcher,
Server::Configuration::CommonFactoryContext& context) {
switch (matcher.matcher_case()) {
case envoy::type::matcher::v3::FilterStateMatcher::MatcherCase::kStringMatch:
return std::make_unique<const StringMatcherImpl<envoy::type::matcher::v3::StringMatcher>>(
matcher.string_match(), context);
return std::make_unique<FilterStateStringMatcher>(
std::make_unique<const StringMatcherImpl<envoy::type::matcher::v3::StringMatcher>>(
matcher.string_match(), context));
break;
case envoy::type::matcher::v3::FilterStateMatcher::MatcherCase::kAddressMatch: {
auto ip_list = Network::Address::IpList::create(matcher.address_match().ranges());
RETURN_IF_NOT_OK_REF(ip_list.status());
return std::make_unique<FilterStateIpRangeMatcher>(std::move(*ip_list));
break;
}
default:
PANIC_DUE_TO_PROTO_UNSET;
}
}

} // namespace

FilterStateMatcher::FilterStateMatcher(const envoy::type::matcher::v3::FilterStateMatcher& matcher,
Server::Configuration::CommonFactoryContext& context)
: key_(matcher.key()), value_matcher_(valueMatcherFromProto(matcher, context)) {}
absl::StatusOr<FilterStateMatcherPtr>
FilterStateMatcher::create(const envoy::type::matcher::v3::FilterStateMatcher& config,
Server::Configuration::CommonFactoryContext& context) {
absl::StatusOr<FilterStateObjectMatcherPtr> matcher =
filterStateObjectMatcherFromProto(config, context);
if (!matcher.ok()) {
return matcher.status();
}
return std::make_unique<FilterStateMatcher>(config.key(), std::move(*matcher));
}

FilterStateMatcher::FilterStateMatcher(std::string key,
FilterStateObjectMatcherPtr&& object_matcher)
: key_(key), object_matcher_(std::move(object_matcher)) {}

bool FilterStateMatcher::match(const StreamInfo::FilterState& filter_state) const {
const auto* object = filter_state.getDataReadOnlyGeneric(key_);
if (object == nullptr) {
return false;
}
const auto string_value = object->serializeAsString();
return string_value && value_matcher_->match(*string_value);
return object_matcher_->match(*object);
}

PathMatcherConstSharedPtr
Expand Down
12 changes: 9 additions & 3 deletions source/common/common/matchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "envoy/type/matcher/v3/string.pb.h"
#include "envoy/type/matcher/v3/value.pb.h"

#include "source/common/common/filter_state_object_matchers.h"
#include "source/common/common/regex.h"
#include "source/common/common/utility.h"
#include "source/common/protobuf/protobuf.h"
Expand Down Expand Up @@ -239,8 +240,7 @@ class MetadataMatcher {

class FilterStateMatcher {
public:
FilterStateMatcher(const envoy::type::matcher::v3::FilterStateMatcher& matcher,
Server::Configuration::CommonFactoryContext& context);
FilterStateMatcher(std::string key, FilterStateObjectMatcherPtr&& object_matcher);

/**
* Check whether the filter state object is matched to the matcher.
Expand All @@ -249,11 +249,17 @@ class FilterStateMatcher {
*/
bool match(const StreamInfo::FilterState& filter_state) const;

static absl::StatusOr<std::unique_ptr<FilterStateMatcher>>
create(const envoy::type::matcher::v3::FilterStateMatcher& matcher,
Server::Configuration::CommonFactoryContext& context);

private:
const std::string key_;
const StringMatcherPtr value_matcher_;
const FilterStateObjectMatcherPtr object_matcher_;
};

using FilterStateMatcherPtr = std::unique_ptr<FilterStateMatcher>;

class PathMatcher : public StringMatcher {
public:
PathMatcher(const envoy::type::matcher::v3::PathMatcher& path,
Expand Down
22 changes: 22 additions & 0 deletions source/common/network/cidr_range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ IpList::create(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRan
return ret;
}

absl::StatusOr<std::unique_ptr<IpList>>
IpList::create(const Protobuf::RepeatedPtrField<xds::core::v3::CidrRange>& cidrs) {
std::unique_ptr<IpList> ret = std::unique_ptr<IpList>(new IpList(cidrs));
if (!ret->error_.empty()) {
return absl::InvalidArgumentError(ret->error_);
}
return ret;
}

IpList::IpList(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs) {
ip_list_.reserve(cidrs.size());
for (const envoy::config::core::v3::CidrRange& entry : cidrs) {
Expand All @@ -228,6 +237,19 @@ IpList::IpList(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRan
}
}

IpList::IpList(const Protobuf::RepeatedPtrField<xds::core::v3::CidrRange>& cidrs) {
ip_list_.reserve(cidrs.size());
for (const xds::core::v3::CidrRange& entry : cidrs) {
absl::StatusOr<CidrRange> range_or_error = CidrRange::create(entry);
if (range_or_error.status().ok()) {
ip_list_.push_back(std::move(range_or_error.value()));
} else {
error_ = fmt::format("invalid ip/mask combo '{}/{}' (format is <ip>/<# mask bits>)",
entry.address_prefix(), entry.prefix_len().value());
}
}
}

bool IpList::contains(const Instance& address) const {
for (const CidrRange& entry : ip_list_) {
if (entry.isInRange(address)) {
Expand Down
5 changes: 4 additions & 1 deletion source/common/network/cidr_range.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ class IpList {
public:
static absl::StatusOr<std::unique_ptr<IpList>>
create(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs);
static absl::StatusOr<std::unique_ptr<IpList>>
create(const Protobuf::RepeatedPtrField<xds::core::v3::CidrRange>& cidrs);

IpList() = default;

Expand All @@ -140,7 +142,8 @@ class IpList {
const std::string& error() const { return error_; }

private:
explicit IpList(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs);
IpList(const Protobuf::RepeatedPtrField<envoy::config::core::v3::CidrRange>& cidrs);
IpList(const Protobuf::RepeatedPtrField<xds::core::v3::CidrRange>& cidrs);
std::vector<CidrRange> ip_list_;
std::string error_;
};
Expand Down
7 changes: 6 additions & 1 deletion source/extensions/filters/common/rbac/matchers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,14 @@ bool MetadataMatcher::matches(const Network::Connection&, const Envoy::Http::Req
return matcher_.match(info.dynamicMetadata());
}

FilterStateMatcher::FilterStateMatcher(const envoy::type::matcher::v3::FilterStateMatcher& matcher,
Server::Configuration::CommonFactoryContext& context)
: matcher_(THROW_OR_RETURN_VALUE(Envoy::Matchers::FilterStateMatcher::create(matcher, context),
Envoy::Matchers::FilterStateMatcherPtr)) {}

bool FilterStateMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&,
const StreamInfo::StreamInfo& info) const {
return matcher_.match(info.filterState());
return matcher_->match(info.filterState());
}

bool PolicyMatcher::matches(const Network::Connection& connection,
Expand Down
Loading
Loading