Skip to content

Report observed egress hosts via the metrics pipeline - #242

Merged
AbhishekBhaskar merged 4 commits into
mainfrom
abhishekbhaskar/add-endpoint-record-egress-hosts
Sep 15, 2026
Merged

AbhishekBhaskar merged 4 commits into
mainfrom
abhishekbhaskar/add-endpoint-record-egress-hosts

Conversation

@AbhishekBhaskar

@AbhishekBhaskar AbhishekBhaskar commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What are you trying to accomplish?

We want to restrict which outbound hosts the proxy allows a job to reach. Before enforcing anything, we need to observe the hosts each ecosystem actually contacts so we can tune the allowlist, then flip on enforcement safely. Today that observability data only lands in proxy logs, which aren't forwarded to Splunk/Kusto — so we can't analyze real egress patterns.

This wires the egress allowlist handler into the proxy's existing metrics reporting pipeline so every observed host is reported to the backend as an  egress_host  metric. The backend (dependabot-api) logs the raw host to Splunk for allowlist discovery.

The handler has two independent, flag-gated modes (both off by default / fail-open):

• observe — logs and reports non-allowlisted hosts; blocks nothing.
• enforce — returns a 403 for non-allowlisted hosts.

Anything you want to highlight for special attention from reviewers?

• Reuses the existing metrics collector instead of a dedicated pipeline. Per review feedback, the earlier dedicated egress collector +  record_egress_hosts  endpoint have been removed. Observations now flow through the same buffering / flush / retry / job-lifecycle path as other metrics — one pipeline to maintain, not two.
• Recording happens at the allowlist decision point, inside the handler. goproxy stops the request chain on the first blocking response, and the allowlist handler runs before the metrics handler — so a downstream handler would never see enforce-blocked hosts. Emitting the observation from the allowlist handler itself (before the 403) is what lets us capture blocked hosts.
• What's reported: every observed host with an  allowlisted  flag (not just non-allowlisted), giving the full egress picture per ecosystem. Tags: raw  request_host ,  allowlisted ;  package_manager  is added automatically by the collector's default tags.
• Two fixes to the metrics collector that this depends on:
• Tag-aware aggregation — the collector previously merged records by metric name + type only, so series with different tags (e.g. different hosts) were incorrectly folded together. It now also compares tags. This also corrects the existing bucketed metric.
• Bounded buffer — a cap so a job hitting many distinct hosts can't grow the buffer unbounded (new series are dropped once the cap is reached; existing ones keep aggregating).
• Gated by the existing experiment flags ( proxy_egress_observe  /  proxy_egress_enforce ); fail-open mode reports nothing.

How will you know you've accomplished your goal?

• Unit tests: the handler reports observed hosts with the correct  allowlisted  status, reports enforce-blocked hosts, and reports nothing when disabled; the collector keeps distinct-tag series separate and enforces the buffer cap.
• Full suite +  gofmt  /  go vet  pass locally ( go test ./... , incl.  -race  on the affected packages).
• End-to-end: once the API change is live, observed hosts appear in Splunk keyed by  package_manager , letting us list non-allowlisted hosts per ecosystem before enabling enforce.

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes to ensure they work as expected, including adding additional tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request, including the problem it addresses, how it fixes the problem, and any relevant details about the implementation.
  • I have ensured that the code is well-documented and easy to understand.

@AbhishekBhaskar AbhishekBhaskar self-assigned this Sep 11, 2026
@AbhishekBhaskar
AbhishekBhaskar marked this pull request as ready for review September 11, 2026 19:06
@AbhishekBhaskar
AbhishekBhaskar requested a review from a team as a code owner September 11, 2026 19:06
Copilot AI balanced review requested due to automatic review settings September 11, 2026 19:06
@AbhishekBhaskar
AbhishekBhaskar force-pushed the abhishekbhaskar/add-endpoint-record-egress-hosts branch from 17bd2cd to 6c4c9bd Compare September 11, 2026 19:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Shutdown synchronization and failed-batch retention must be addressed to prevent buffered telemetry loss.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite (auto)
Findings: 2 Medium severity

Note

Copilot is running an experiment and ran this review at Lite.

New issues introduced by this change (2)
Severity Finding
Medium severity internal/​egress/​collector.go — Shutdown can exit before the final egress flush View comment
Medium severity internal/​egress/​collector.go — Failed reports permanently discard the buffered hosts View comment
What changed in this PR

Adds batched outbound-host reporting from the proxy to the Dependabot API for Splunk/Kusto observability.

Changes:

  • Collects and aggregates observed egress hosts.
  • Integrates recording with allowlist handling and proxy lifecycle.
  • Adds API support and unit tests for reporting.
File Summary
proxy.go Wires and stops the egress collector; shutdown does not wait for final flushing.
internal/​metrics/​collector_client_test.go Updates the API client mock.
internal/​handlers/​egress_allowlist.go Records observed hosts and allowlist status.
internal/​handlers/​egress_allowlist_test.go Tests recording behavior.
internal/​egress/​collector.go Implements batching and reporting; requires shutdown synchronization and failed-batch retention.
internal/​egress/​collector_test.go Tests aggregation and flushing.
internal/​apiclient/​client.go Adds the egress reporting endpoint.
internal/​apiclient/​client_test.go Tests endpoint requests and payload handling.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/egress/collector.go Outdated
Comment thread internal/egress/collector.go Outdated
@AbhishekBhaskar AbhishekBhaskar changed the title Report outbound hosts to backend endpoint to forward to splunk/kusto Report observed egress hosts via the metrics pipeline Sep 15, 2026
Comment thread internal/metrics/collector_client.go Outdated
for i, existingMetric := range c.MetricsBuffer {
if existingMetric["metric"] == prefixedName && existingMetric["type"] == metricType {
existingTags, _ := existingMetric["tags"].(map[string]string)
if existingMetric["metric"] == prefixedName && existingMetric["type"] == metricType && maps.Equal(existingTags, combinedTags) {

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.

Keeping hosts separate exposes a bug in the existing size-limit path. flushBuffer clears the records but doesn't reset estimatedBufferSize, so that estimate accumulates across flushes. Eventually SendMetric hits the byte limit and calls flushBuffer while already holding BufferMutex. flushBuffer tries to acquire the same mutex and deadlocks.

I reproduced this with repeated batches of 900 distinct hosts, flushing between batches. Each batch stays below the new series cap, but the request eventually hangs.

Can we reset the byte estimate whenever we drain the buffer, and fix the size-triggered flush so it doesn't acquire a mutex we already hold? Please add coverage for repeated flushes and reaching the byte limit.

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.

Thanks for identifying this bug! I've fixed this by resetting estimatedBufferSize on every drain (in a new drainLocked helper), so it can't accumulate across flushes. Size-triggered flush no longer re-enters the mutex — SendMetric  drains into a local batch and posts it after releasing BufferMutex , so flushBuffer is never called while we hold the lock. Added test coverage for the same.

Comment thread internal/metrics/collector_client.go Outdated
Comment on lines +168 to +170
if len(c.MetricsBuffer) >= c.MaxBufferSize {
return nil
}

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.

This cap applies to all metrics, so egress observations can fill the buffer and cause us to silently drop ordinary request/response metrics.

I reproduced this by recording one HTTP 200 response, then 999 distinct egress hosts, then an HTTP 500 response. The 500 metric is dropped, and SendMetric returns success.

Can we give egress observations a separate quota within this collector, or reserve capacity for the existing metrics? Also, is 1,000 an intentional limit for this use case, or are we just inheriting the previously unused default?

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.

The distinct-series cap is now applied per metric name rather than globally, so egress cardinality can't drop ordinary request/response metrics. 1000 was just the inherited unused default. I've set it to 500 per name now (comfortably above what either metric emits per flush window).

@AbhishekBhaskar
AbhishekBhaskar force-pushed the abhishekbhaskar/add-endpoint-record-egress-hosts branch from 6e8f2c8 to 160e23b Compare September 15, 2026 20:02
@AbhishekBhaskar
AbhishekBhaskar merged commit 180e0ff into main Sep 15, 2026
110 of 111 checks passed
@AbhishekBhaskar
AbhishekBhaskar deleted the abhishekbhaskar/add-endpoint-record-egress-hosts branch September 15, 2026 20:08
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.

3 participants