Skip to content

reverse_tunnels: Add EventReporter for reverse tunnel connection reporting#44342

Open
aakugan wants to merge 1 commit intoenvoyproxy:mainfrom
aakugan:reverse_tunnel_reporter
Open

reverse_tunnels: Add EventReporter for reverse tunnel connection reporting#44342
aakugan wants to merge 1 commit intoenvoyproxy:mainfrom
aakugan:reverse_tunnel_reporter

Conversation

@aakugan
Copy link
Copy Markdown
Contributor

@aakugan aakugan commented Apr 9, 2026

BUILDS ON #44327, depends on the protos introduced there.
Incremental DIFFS

Commit Message

Add EventReporter for reverse tunnel connection reporting

Additional Description:

Adds the EventReporter contrib extension, which aggregates reverse tunnel connection/disconnection events and fans them out to pluggable clients.

Key components:

  • ReverseTunnelReporterWithState / ReverseTunnelReporterClient: interfaces allowing multiple transport clients to share connection state from a single reporter. Clients receive incremental diffs and can request a full state snapshot so they dont have to be "stateful" other than the protocol they manage.
  • EventReporter: A basic implementation which does refcounting of connections by the name and stores in a map for interfacing with the clients.

Risk Level: Low, opt in extension.

Testing

Unit tests.

@aakugan aakugan changed the title Reverse tunnel reporter reverse_tunnels: Add EventReporter for reverse tunnel connection reporting Apr 9, 2026
@aakugan aakugan force-pushed the reverse_tunnel_reporter branch from e36b38c to 66d9466 Compare April 9, 2026 04:53
@aakugan aakugan force-pushed the reverse_tunnel_reporter branch from 66d9466 to e9ef353 Compare April 9, 2026 07:18
@phlax
Copy link
Copy Markdown
Member

phlax commented Apr 13, 2026

its not immediately clear to me why - but docs build is failing with

[97](https://github.com/envoyproxy/envoy/actions/runs/24177797097/job/70563146913#step:19:511)
/tmp/tmpfytzszml/generated/rst/api-v3/config/bootstrap/v3/bootstrap.proto.rst:396: WARNING: undefined label: 'extension_envoy.bootstrap.reverse_tunnel.reverse_tunnel_reporting_service' [ref.ref]
/tmp/tmpfytzszml/generated/rst/intro/arch_overview/security/secpos_requires_trusted_downstream_and_upstream.rst:6: WARNING: undefined label: 'extension_envoy.bootstrap.reverse_tunnel.reverse_tunnel_reporting_service' [ref.ref]
/tmp/tmpfytzszml/generated/rst/intro/arch_overview/security/secpos_requires_trusted_downstream_and_upstream.rst:6: WARNING: undefined label: 'extension_envoy.bootstrap.reverse_tunnel.reverse_tunnel_reporting_service' [ref.ref]

@phlax phlax self-assigned this Apr 13, 2026
Copy link
Copy Markdown
Member

@phlax phlax left a comment

Choose a reason for hiding this comment

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

some initial docs review - would like to view it rendered - can help if needed to resolve the build problem

// Configuration for the connection event reporter.
message EventReporterConfig {
// Stat prefix for this reporter's metrics.
// Metrics will be emitted as "{stat_prefix}.events_pushed", etc.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Metrics will be emitted as "{stat_prefix}.events_pushed", etc.
// Metrics will be emitted as ``{stat_prefix}.events_pushed``, etc.

// Represents a single reverse tunnel connection with its metadata.
message ReverseTunnel {
// Unique name to identify this tunnel connection.
// Typically formatted as "{node_id}|{cluster_id}" or similar.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Typically formatted as "{node_id}|{cluster_id}" or similar.
// Typically formatted as ``{node_id}|{cluster_id}`` or similar.

// Detailed information about a reverse tunnel connection.
message ReverseTunnelInfo {
// Identity information of the tunnel initiator (downstream Envoy).
// Contains node_id, cluster_id, and tenant_id for proper identification.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Contains node_id, cluster_id, and tenant_id for proper identification.
// Contains ``node_id``, ``cluster_id``, and ``tenant_id`` for proper identification.

@aakugan aakugan force-pushed the reverse_tunnel_reporter branch 2 times, most recently from 75ee2d3 to 0f8c02a Compare April 17, 2026 03:27
@aakugan
Copy link
Copy Markdown
Contributor Author

aakugan commented Apr 17, 2026

Thanks for taking a look!
I made the changes and fixed the docs build

@aakugan aakugan requested a review from phlax April 17, 2026 04:25
@phlax
Copy link
Copy Markdown
Member

phlax commented Apr 17, 2026

/docs

@repokitteh-read-only
Copy link
Copy Markdown

Docs for this Pull Request will be rendered here:

https://storage.googleapis.com/envoy-cncf-pr/44342/docs/index.html

The docs are (re-)rendered each time the CI Envoy/Prechecks (docs) job completes.

🐱

Caused by: a #44342 (comment) was created by @phlax.

see: more, trace.

void EventReporter::addConnection(std::shared_ptr<ReverseTunnelEvent::Connected>&& connection) {
ASSERT(context_.mainThreadDispatcher().isThreadSafe());

ENVOY_LOG(info, "EventReporter: Accepted a new connection. Node: {}, Cluster: {}, Tenant: {}",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add ReverseTunnel prefix to logs. EventReporter feels pretty generic name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We get the entire path in the log ig I will remove the prefix 😅

Comment thread contrib/reverse_tunnel_reporter/source/reverse_tunnel_event_types.h Outdated
Comment thread contrib/reverse_tunnel_reporter/source/reverse_tunnel_event_types.h Outdated
@agrawroh
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new contrib extension for reverse tunnel reporting, providing a mechanism for Envoy instances to report connection state changes to a management server. It includes the API definitions for a gRPC-based reporting service, the implementation of an EventReporter that aggregates and de-duplicates connection events, and comprehensive unit tests. Review feedback suggests adding lower-bound validation to the reporting interval to prevent potential resource exhaustion, utilizing try_emplace for more efficient map insertions, and optimizing batch event processing by using move iterators.

Comment thread contrib/reverse_tunnel_reporter/source/reporters/event_reporter/reporter.cc Outdated
Comment thread contrib/reverse_tunnel_reporter/source/reverse_tunnel_event_types.h Outdated
@aakugan aakugan force-pushed the reverse_tunnel_reporter branch from 0f8c02a to 1f23b5c Compare April 18, 2026 03:31
Comment on lines +27 to +38
void EventReporter::reportConnectionEvent(absl::string_view node_id, absl::string_view cluster_id,
absl::string_view tenant_id) {
auto ptr = std::make_shared<ReverseTunnelEvent::Connected>(
ReverseTunnelEvent::Connected{std::string(node_id), std::string(cluster_id),
std::string(tenant_id), Envoy::SystemTime::clock::now()});

context_.mainThreadDispatcher().post(
[this, ptr = std::move(ptr)]() mutable { this->addConnection(std::move(ptr)); });
}

void EventReporter::reportDisconnectionEvent(absl::string_view node_id, absl::string_view) {
absl::string_view name = ReverseTunnelEvent::getName(node_id);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

getName() returns the node_id right? How does this work for tenant scoped nodes?

using SharedConnections = std::vector<std::shared_ptr<Connected>>;
using SharedDisconnections = std::vector<std::shared_ptr<Disconnected>>;

struct BatchedEvents {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

BatchedEvents sounds misleading because we aren't adding a 'batch', maybe we can call it Events/TunnelUpdates

}

void EventReporter::reportDisconnectionEvent(absl::string_view node_id, absl::string_view) {
absl::string_view name = ReverseTunnelEvent::getName(node_id);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

getName() returns the node_id right? How does this work for tenant scoped nodes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yea so I kinda expect node_id to be of the format tenant:node in the tenant isolation case.
I have added some comments above the function I will make the changes and add some integ tests to pin it some followup PRs.

explicit Disconnected(absl::string_view n) : name(n) {}
};

using SharedConnections = std::vector<std::shared_ptr<Connected>>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: SharedConnections -> Connections/ConnectionList
SharedDisconnections -> Disconnections/DisconnectionList


std::size_t size() const { return connections.size() + disconnections.size(); }

void operator+=(BatchedEvents&& events) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This does not seem to be used anywhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh I needed it for some followup PRs thought of adding it here as it is part of the struct 😅


virtual void onServerInitialized(ReverseTunnelReporterWithState* reporter) PURE;

virtual void receiveEvents(ReverseTunnelEvent::BatchedEvents events) PURE;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not use const ReverseTunnelEvent::BatchedEvents& events here? Does receiveEvents() mutate events?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So I wanted the copy to be explicit as the clients may have to buffer the events or something, the struct is just a bunch of shared_ptrs so copy is cheap the real risk is if some event comes which deletes the entry from the map which has the original shared_ptr.
As for the const ig there is better value in making it shared_ptr<const> I will look into that part.

Comment on lines +76 to +77
absl::string_view name = ReverseTunnelEvent::getName(connection->node_id);
auto [it, inserted] = connections_.try_emplace(std::move(name), std::move(connection), 1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Using absl::string as the key in connection is fragile, let's use an owned std::string here.

@aakugan aakugan force-pushed the reverse_tunnel_reporter branch 2 times, most recently from 9b327d8 to a4a1eaa Compare April 19, 2026 20:22
Signed-off-by: aakugan <aakashganapathy2@gmail.com>
@aakugan aakugan force-pushed the reverse_tunnel_reporter branch from a4a1eaa to af15b08 Compare April 19, 2026 20:23
@aakugan aakugan requested review from basundhara-c and ivpr April 20, 2026 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants