Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/extensions/filters/common/rbac/matchers.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/filters/common/rbac/matchers.h"
2
3
#include "envoy/config/rbac/v3/rbac.pb.h"
4
#include "envoy/upstream/upstream.h"
5
6
#include "source/common/config/utility.h"
7
#include "source/extensions/filters/common/rbac/matcher_extension.h"
8
9
namespace Envoy {
10
namespace Extensions {
11
namespace Filters {
12
namespace Common {
13
namespace RBAC {
14
15
MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Permission& permission,
16
11.9k
                                      ProtobufMessage::ValidationVisitor& validation_visitor) {
17
11.9k
  switch (permission.rule_case()) {
18
1.54k
  case envoy::config::rbac::v3::Permission::RuleCase::kAndRules:
19
1.54k
    return std::make_shared<const AndMatcher>(permission.and_rules(), validation_visitor);
20
867
  case envoy::config::rbac::v3::Permission::RuleCase::kOrRules:
21
867
    return std::make_shared<const OrMatcher>(permission.or_rules(), validation_visitor);
22
34
  case envoy::config::rbac::v3::Permission::RuleCase::kHeader:
23
34
    return std::make_shared<const HeaderMatcher>(permission.header());
24
26
  case envoy::config::rbac::v3::Permission::RuleCase::kDestinationIp:
25
26
    return std::make_shared<const IPMatcher>(permission.destination_ip(),
26
26
                                             IPMatcher::Type::DownstreamLocal);
27
5.51k
  case envoy::config::rbac::v3::Permission::RuleCase::kDestinationPort:
28
5.51k
    return std::make_shared<const PortMatcher>(permission.destination_port());
29
241
  case envoy::config::rbac::v3::Permission::RuleCase::kDestinationPortRange:
30
241
    return std::make_shared<const PortRangeMatcher>(permission.destination_port_range());
31
1.76k
  case envoy::config::rbac::v3::Permission::RuleCase::kAny:
32
1.76k
    return std::make_shared<const AlwaysMatcher>();
33
115
  case envoy::config::rbac::v3::Permission::RuleCase::kMetadata:
34
115
    return std::make_shared<const MetadataMatcher>(permission.metadata());
35
851
  case envoy::config::rbac::v3::Permission::RuleCase::kNotRule:
36
851
    return std::make_shared<const NotMatcher>(permission.not_rule(), validation_visitor);
37
889
  case envoy::config::rbac::v3::Permission::RuleCase::kRequestedServerName:
38
889
    return std::make_shared<const RequestedServerNameMatcher>(permission.requested_server_name());
39
97
  case envoy::config::rbac::v3::Permission::RuleCase::kUrlPath:
40
97
    return std::make_shared<const PathMatcher>(permission.url_path());
41
0
  case envoy::config::rbac::v3::Permission::RuleCase::kMatcher: {
42
0
    auto& factory =
43
0
        Config::Utility::getAndCheckFactory<MatcherExtensionFactory>(permission.matcher());
44
0
    return factory.create(permission.matcher(), validation_visitor);
45
0
  }
46
0
  case envoy::config::rbac::v3::Permission::RuleCase::RULE_NOT_SET:
47
0
    break; // Fall through to PANIC.
48
11.9k
  }
49
0
  PANIC_DUE_TO_CORRUPT_ENUM;
50
0
}
51
52
14.7k
MatcherConstSharedPtr Matcher::create(const envoy::config::rbac::v3::Principal& principal) {
53
14.7k
  switch (principal.identifier_case()) {
54
512
  case envoy::config::rbac::v3::Principal::IdentifierCase::kAndIds:
55
512
    return std::make_shared<const AndMatcher>(principal.and_ids());
56
1.78k
  case envoy::config::rbac::v3::Principal::IdentifierCase::kOrIds:
57
1.78k
    return std::make_shared<const OrMatcher>(principal.or_ids());
58
4.16k
  case envoy::config::rbac::v3::Principal::IdentifierCase::kAuthenticated:
59
4.16k
    return std::make_shared<const AuthenticatedMatcher>(principal.authenticated());
60
452
  case envoy::config::rbac::v3::Principal::IdentifierCase::kSourceIp:
61
452
    return std::make_shared<const IPMatcher>(principal.source_ip(),
62
452
                                             IPMatcher::Type::ConnectionRemote);
63
302
  case envoy::config::rbac::v3::Principal::IdentifierCase::kDirectRemoteIp:
64
302
    return std::make_shared<const IPMatcher>(principal.direct_remote_ip(),
65
302
                                             IPMatcher::Type::DownstreamDirectRemote);
66
518
  case envoy::config::rbac::v3::Principal::IdentifierCase::kRemoteIp:
67
518
    return std::make_shared<const IPMatcher>(principal.remote_ip(),
68
518
                                             IPMatcher::Type::DownstreamRemote);
69
34
  case envoy::config::rbac::v3::Principal::IdentifierCase::kHeader:
70
34
    return std::make_shared<const HeaderMatcher>(principal.header());
71
4.95k
  case envoy::config::rbac::v3::Principal::IdentifierCase::kAny:
72
4.95k
    return std::make_shared<const AlwaysMatcher>();
73
300
  case envoy::config::rbac::v3::Principal::IdentifierCase::kMetadata:
74
300
    return std::make_shared<const MetadataMatcher>(principal.metadata());
75
1.59k
  case envoy::config::rbac::v3::Principal::IdentifierCase::kNotId:
76
1.59k
    return std::make_shared<const NotMatcher>(principal.not_id());
77
158
  case envoy::config::rbac::v3::Principal::IdentifierCase::kUrlPath:
78
158
    return std::make_shared<const PathMatcher>(principal.url_path());
79
17
  case envoy::config::rbac::v3::Principal::IdentifierCase::kFilterState:
80
17
    return std::make_shared<const FilterStateMatcher>(principal.filter_state());
81
0
  case envoy::config::rbac::v3::Principal::IdentifierCase::IDENTIFIER_NOT_SET:
82
0
    break; // Fall through to PANIC.
83
14.7k
  }
84
0
  PANIC_DUE_TO_CORRUPT_ENUM;
85
0
}
86
87
AndMatcher::AndMatcher(const envoy::config::rbac::v3::Permission::Set& set,
88
1.54k
                       ProtobufMessage::ValidationVisitor& validation_visitor) {
89
2.60k
  for (const auto& rule : set.rules()) {
90
2.60k
    matchers_.push_back(Matcher::create(rule, validation_visitor));
91
2.60k
  }
92
1.54k
}
93
94
512
AndMatcher::AndMatcher(const envoy::config::rbac::v3::Principal::Set& set) {
95
1.84k
  for (const auto& id : set.ids()) {
96
1.84k
    matchers_.push_back(Matcher::create(id));
97
1.84k
  }
98
512
}
99
100
bool AndMatcher::matches(const Network::Connection& connection,
101
                         const Envoy::Http::RequestHeaderMap& headers,
102
1.47k
                         const StreamInfo::StreamInfo& info) const {
103
2.08k
  for (const auto& matcher : matchers_) {
104
2.08k
    if (!matcher->matches(connection, headers, info)) {
105
684
      return false;
106
684
    }
107
2.08k
  }
108
109
786
  return true;
110
1.47k
}
111
112
OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField<envoy::config::rbac::v3::Permission>& rules,
113
5.59k
                     ProtobufMessage::ValidationVisitor& validation_visitor) {
114
8.49k
  for (const auto& rule : rules) {
115
8.49k
    matchers_.push_back(Matcher::create(rule, validation_visitor));
116
8.49k
  }
117
5.59k
}
118
119
6.50k
OrMatcher::OrMatcher(const Protobuf::RepeatedPtrField<envoy::config::rbac::v3::Principal>& ids) {
120
11.3k
  for (const auto& id : ids) {
121
11.3k
    matchers_.push_back(Matcher::create(id));
122
11.3k
  }
123
6.50k
}
124
125
bool OrMatcher::matches(const Network::Connection& connection,
126
                        const Envoy::Http::RequestHeaderMap& headers,
127
31.5k
                        const StreamInfo::StreamInfo& info) const {
128
37.5k
  for (const auto& matcher : matchers_) {
129
37.5k
    if (matcher->matches(connection, headers, info)) {
130
28.7k
      return true;
131
28.7k
    }
132
37.5k
  }
133
134
2.85k
  return false;
135
31.5k
}
136
137
bool NotMatcher::matches(const Network::Connection& connection,
138
                         const Envoy::Http::RequestHeaderMap& headers,
139
5.42k
                         const StreamInfo::StreamInfo& info) const {
140
5.42k
  return !matcher_->matches(connection, headers, info);
141
5.42k
}
142
143
bool HeaderMatcher::matches(const Network::Connection&,
144
                            const Envoy::Http::RequestHeaderMap& headers,
145
49
                            const StreamInfo::StreamInfo&) const {
146
49
  return Envoy::Http::HeaderUtility::matchHeaders(headers, header_);
147
49
}
148
149
bool IPMatcher::matches(const Network::Connection& connection, const Envoy::Http::RequestHeaderMap&,
150
1.89k
                        const StreamInfo::StreamInfo& info) const {
151
1.89k
  Envoy::Network::Address::InstanceConstSharedPtr ip;
152
1.89k
  switch (type_) {
153
857
  case ConnectionRemote:
154
857
    ip = connection.connectionInfoProvider().remoteAddress();
155
857
    break;
156
24
  case DownstreamLocal:
157
24
    ip = info.downstreamAddressProvider().localAddress();
158
24
    break;
159
715
  case DownstreamDirectRemote:
160
715
    ip = info.downstreamAddressProvider().directRemoteAddress();
161
715
    break;
162
295
  case DownstreamRemote:
163
295
    ip = info.downstreamAddressProvider().remoteAddress();
164
295
    break;
165
1.89k
  }
166
1.89k
  return range_.isInRange(*ip.get());
167
1.89k
}
168
169
bool PortMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&,
170
6.94k
                          const StreamInfo::StreamInfo& info) const {
171
6.94k
  const Envoy::Network::Address::Ip* ip =
172
6.94k
      info.downstreamAddressProvider().localAddress().get()->ip();
173
6.94k
  return ip && ip->port() == port_;
174
6.94k
}
175
176
PortRangeMatcher::PortRangeMatcher(const ::envoy::type::v3::Int32Range& range)
177
241
    : start_(range.start()), end_(range.end()) {
178
241
  auto start = range.start();
179
241
  auto end = range.end();
180
241
  if (start < 0 || start > 65536) {
181
1
    throw EnvoyException(fmt::format("range start {} is out of bounds", start));
182
1
  }
183
240
  if (end < 0 || end > 65536) {
184
3
    throw EnvoyException(fmt::format("range end {} is out of bounds", end));
185
3
  }
186
237
  if (start >= end) {
187
5
    throw EnvoyException(
188
5
        fmt::format("range start {} cannot be greater or equal than range end {}", start, end));
189
5
  }
190
237
}
191
192
bool PortRangeMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&,
193
1.46k
                               const StreamInfo::StreamInfo& info) const {
194
1.46k
  const Envoy::Network::Address::Ip* ip =
195
1.46k
      info.downstreamAddressProvider().localAddress().get()->ip();
196
1.46k
  if (ip) {
197
25
    const auto port = ip->port();
198
25
    return start_ <= port && port < end_;
199
1.43k
  } else {
200
1.43k
    return false;
201
1.43k
  }
202
1.46k
}
203
204
bool AuthenticatedMatcher::matches(const Network::Connection& connection,
205
                                   const Envoy::Http::RequestHeaderMap&,
206
1.36k
                                   const StreamInfo::StreamInfo&) const {
207
1.36k
  const auto& ssl = connection.ssl();
208
1.36k
  if (!ssl) { // connection was not authenticated
209
1.36k
    return false;
210
1.36k
  } else if (!matcher_.has_value()) { // matcher allows any subject
211
0
    return true;
212
0
  }
213
214
  // If set, The URI SAN  or DNS SAN in that order is used as Principal, otherwise the subject field
215
  // is used.
216
0
  if (!ssl->uriSanPeerCertificate().empty()) {
217
0
    for (const std::string& uri : ssl->uriSanPeerCertificate()) {
218
0
      if (matcher_.value().match(uri)) {
219
0
        return true;
220
0
      }
221
0
    }
222
0
  }
223
0
  if (!ssl->dnsSansPeerCertificate().empty()) {
224
0
    for (const std::string& dns : ssl->dnsSansPeerCertificate()) {
225
0
      if (matcher_.value().match(dns)) {
226
0
        return true;
227
0
      }
228
0
    }
229
0
  }
230
0
  return matcher_.value().match(ssl->subjectPeerCertificate());
231
0
}
232
233
bool MetadataMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&,
234
295
                              const StreamInfo::StreamInfo& info) const {
235
295
  return matcher_.match(info.dynamicMetadata());
236
295
}
237
238
bool FilterStateMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap&,
239
15
                                 const StreamInfo::StreamInfo& info) const {
240
15
  return matcher_.match(info.filterState());
241
15
}
242
243
bool PolicyMatcher::matches(const Network::Connection& connection,
244
                            const Envoy::Http::RequestHeaderMap& headers,
245
15.0k
                            const StreamInfo::StreamInfo& info) const {
246
15.0k
  return permissions_.matches(connection, headers, info) &&
247
15.0k
         principals_.matches(connection, headers, info) &&
248
15.0k
         (expr_ == nullptr ? true : Expr::matches(*expr_, info, headers));
249
15.0k
}
250
251
bool RequestedServerNameMatcher::matches(const Network::Connection& connection,
252
                                         const Envoy::Http::RequestHeaderMap&,
253
569
                                         const StreamInfo::StreamInfo&) const {
254
569
  return match(connection.requestedServerName());
255
569
}
256
257
bool PathMatcher::matches(const Network::Connection&, const Envoy::Http::RequestHeaderMap& headers,
258
183
                          const StreamInfo::StreamInfo&) const {
259
183
  if (headers.Path() == nullptr) {
260
0
    return false;
261
0
  }
262
183
  return path_matcher_.match(headers.getPathValue());
263
183
}
264
265
} // namespace RBAC
266
} // namespace Common
267
} // namespace Filters
268
} // namespace Extensions
269
} // namespace Envoy