Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/extensions/filters/network/ext_authz/ext_authz.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/filters/network/ext_authz/ext_authz.h"
2
3
#include <chrono>
4
#include <cstdint>
5
#include <string>
6
7
#include "envoy/stats/scope.h"
8
9
#include "source/common/common/assert.h"
10
#include "source/common/tracing/http_tracer_impl.h"
11
#include "source/extensions/filters/network/well_known_names.h"
12
13
namespace Envoy {
14
namespace Extensions {
15
namespace NetworkFilters {
16
namespace ExtAuthz {
17
18
9
InstanceStats Config::generateStats(const std::string& name, Stats::Scope& scope) {
19
9
  const std::string final_prefix = fmt::format("ext_authz.{}.", name);
20
9
  return {ALL_TCP_EXT_AUTHZ_STATS(POOL_COUNTER_PREFIX(scope, final_prefix),
21
9
                                  POOL_GAUGE_PREFIX(scope, final_prefix))};
22
9
}
23
24
3
void Filter::callCheck() {
25
3
  Filters::Common::ExtAuthz::CheckRequestUtils::createTcpCheck(filter_callbacks_, check_request_,
26
3
                                                               config_->includePeerCertificate(),
27
3
                                                               config_->destinationLabels());
28
  // Store start time of ext_authz filter call
29
3
  start_time_ = filter_callbacks_->connection().dispatcher().timeSource().monotonicTime();
30
3
  status_ = Status::Calling;
31
3
  config_->stats().active_.inc();
32
3
  config_->stats().total_.inc();
33
34
3
  calling_check_ = true;
35
3
  client_->check(*this, check_request_, Tracing::NullSpan::instance(),
36
3
                 filter_callbacks_->connection().streamInfo());
37
3
  calling_check_ = false;
38
3
}
39
40
243
Network::FilterStatus Filter::onData(Buffer::Instance&, bool /* end_stream */) {
41
243
  if (!filterEnabled(filter_callbacks_->connection().streamInfo().dynamicMetadata())) {
42
63
    config_->stats().disabled_.inc();
43
63
    return Network::FilterStatus::Continue;
44
63
  }
45
46
180
  if (status_ == Status::NotStarted) {
47
    // By waiting to invoke the check at onData() the call to authorization service will have
48
    // sufficient information to fill out the checkRequest_.
49
3
    callCheck();
50
3
  }
51
180
  return filter_return_ == FilterReturn::Stop ? Network::FilterStatus::StopIteration
52
180
                                              : Network::FilterStatus::Continue;
53
243
}
54
55
359
Network::FilterStatus Filter::onNewConnection() {
56
  // Wait till onData() happens.
57
359
  return Network::FilterStatus::Continue;
58
359
}
59
60
5
void Filter::onEvent(Network::ConnectionEvent event) {
61
5
  if (event == Network::ConnectionEvent::RemoteClose ||
62
5
      event == Network::ConnectionEvent::LocalClose) {
63
5
    if (status_ == Status::Calling) {
64
      // Make sure that any pending request in the client is cancelled. This will be NOP if the
65
      // request already completed.
66
3
      client_->cancel();
67
3
      config_->stats().active_.dec();
68
3
    }
69
5
  }
70
5
}
71
72
0
void Filter::onComplete(Filters::Common::ExtAuthz::ResponsePtr&& response) {
73
0
  status_ = Status::Complete;
74
0
  config_->stats().active_.dec();
75
76
0
  switch (response->status) {
77
0
  case Filters::Common::ExtAuthz::CheckStatus::OK:
78
0
    config_->stats().ok_.inc();
79
    // Add duration of call to dynamic metadata if applicable
80
0
    if (start_time_.has_value()) {
81
0
      ProtobufWkt::Value ext_authz_duration_value;
82
0
      auto duration = filter_callbacks_->connection().dispatcher().timeSource().monotonicTime() -
83
0
                      start_time_.value();
84
0
      ext_authz_duration_value.set_number_value(
85
0
          std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
86
0
      (*response->dynamic_metadata.mutable_fields())["ext_authz_duration"] =
87
0
          ext_authz_duration_value;
88
0
    }
89
0
    break;
90
0
  case Filters::Common::ExtAuthz::CheckStatus::Error:
91
0
    config_->stats().error_.inc();
92
0
    break;
93
0
  case Filters::Common::ExtAuthz::CheckStatus::Denied:
94
0
    config_->stats().denied_.inc();
95
0
    break;
96
0
  }
97
98
0
  if (!response->dynamic_metadata.fields().empty()) {
99
100
0
    filter_callbacks_->connection().streamInfo().setDynamicMetadata(
101
0
        NetworkFilterNames::get().ExtAuthorization, response->dynamic_metadata);
102
0
  }
103
104
  // Fail open only if configured to do so and if the check status was a error.
105
0
  if (response->status == Filters::Common::ExtAuthz::CheckStatus::Denied ||
106
0
      (response->status == Filters::Common::ExtAuthz::CheckStatus::Error &&
107
0
       !config_->failureModeAllow())) {
108
0
    config_->stats().cx_closed_.inc();
109
0
    filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, "ext_authz_close");
110
0
    filter_callbacks_->connection().streamInfo().setResponseFlag(
111
0
        StreamInfo::ResponseFlag::UnauthorizedExternalService);
112
0
    filter_callbacks_->connection().streamInfo().setResponseCodeDetails(
113
0
        response->status == Filters::Common::ExtAuthz::CheckStatus::Denied
114
0
            ? Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzDenied
115
0
            : Filters::Common::ExtAuthz::ResponseCodeDetails::get().AuthzError);
116
0
  } else {
117
    // Let the filter chain continue.
118
0
    filter_return_ = FilterReturn::Continue;
119
0
    if (config_->failureModeAllow() &&
120
0
        response->status == Filters::Common::ExtAuthz::CheckStatus::Error) {
121
      // Status is Error and yet we are configured to allow traffic. Click a counter.
122
0
      config_->stats().failure_mode_allowed_.inc();
123
0
    }
124
125
    // We can get completion inline, so only call continue if that isn't happening.
126
0
    if (!calling_check_) {
127
0
      filter_callbacks_->continueReading();
128
0
    }
129
0
  }
130
0
}
131
132
} // namespace ExtAuthz
133
} // namespace NetworkFilters
134
} // namespace Extensions
135
} // namespace Envoy