Skip to content

feat: add authenticated OpenTelemetry gateway#231

Open
rsampaio wants to merge 1 commit into
mainfrom
rsampaio-otel-gateway
Open

feat: add authenticated OpenTelemetry gateway#231
rsampaio wants to merge 1 commit into
mainfrom
rsampaio-otel-gateway

Conversation

@rsampaio

@rsampaio rsampaio commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds an optional, production-hardened OpenTelemetry gateway deployment for Fleet Intelligence. Agents route OTLP metrics and logs through a per-cluster custom Collector, which owns backend SAK-to-JWT authentication; direct agent enrollment remains unchanged for inventory and attestation.

The gateway is disabled by default and does not change bare-metal or existing Kubernetes behavior unless otelGateway.enabled=true.

Security and reliability

  • Keeps the service SAK in a Kubernetes Secret and an opaque Collector config field.
  • Requires HTTPS enrollment, rejects redirects, bounds response bodies, and supports custom CA/TLS settings.
  • Serializes concurrent JWT refreshes and retries a replayable request at most once after a 401.
  • Runs non-root with a read-only filesystem, dropped capabilities, RuntimeDefault seccomp, and no service-account token.
  • Defaults to two replicas with rolling updates, topology spreading, a PDB, probes, resource limits, and ingress NetworkPolicy.
  • Publishes multi-architecture images to GHCR and builds against OpenTelemetry Collector v0.156.0.

Review guide

  1. Agent routing: cmd/fleetint/run.go, internal/config/, and internal/exporter/ establish collector precedence without forwarding node credentials.
  2. Gateway authentication: otelcol/auth/sakauth/ contains enrollment, token refresh, TLS, and request authentication.
  3. Deployment: deployments/helm/fleet-intelligence-agent/templates/otel-gateway-* contains availability and security controls.
  4. Build/release: otelcol/Dockerfile, otelcol/otelcol-builder.yaml, and GitHub workflows build, test, and publish the collector.
  5. Tests: collector_integration_test.go starts the built Collector and verifies real OTLP metrics/logs forwarding, identity attributes, and backend authentication.

Operational notes

  • Agent-to-gateway OTLP is cluster-local HTTP and is restricted by NetworkPolicy; clusters must use a CNI that enforces NetworkPolicy.
  • Production installations should use otelGateway.enroll.existingSecret rather than passing a SAK through Helm values.
  • The sending queue is in-memory; persistent queueing is not introduced by this change.

Test plan

  • Gateway unit tests with race detector (90.8% statement coverage).
  • End-to-end built-Collector test for SAK enrollment and OTLP metrics/logs forwarding.
  • Core config, backend client, endpoint, and agent-state regression tests.
  • Gateway module go vet and full golangci-lint (0 issues).
  • Standalone Collector configuration validation.
  • Helm lint and rendering with gateway disabled and enabled.
  • Full Linux Docker suite and root lint will run in PR CI; local execution is blocked by Docker socket sandboxing and macOS-only build constraints around DCGM/NVML/procfs.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Added an optional OpenTelemetry gateway for collecting and forwarding agent metrics and logs.
    • Added Helm configuration for gateway deployment, authentication, health checks, networking, resource limits, and disruption protection.
    • Added multi-architecture gateway container images to the release process.
    • Added support for routing telemetry through a configured collector endpoint.
  • Bug Fixes

    • Sensitive authentication tokens are now consistently masked in metadata output.
    • Improved validation for collector endpoint configuration.

Add an optional custom OpenTelemetry Collector that centralizes SAK-to-JWT authentication and forwards agent metrics and logs while preserving direct enrollment for inventory and attestation.

Harden the gateway for production with current Collector dependencies, secure token and TLS handling, highly available Helm resources, GHCR publishing, and end-to-end OTLP integration coverage.

Signed-off-by: Rodrigo Sampaio Vaz <rvaz@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an optional OpenTelemetry gateway with SAK-to-JWT authentication, collector routing from the agent, Helm deployment resources, a custom multi-architecture collector image, CI integration coverage, and release publishing.

Changes

OpenTelemetry authentication and collector

Layer / File(s) Summary
SAK authentication extension
otelcol/auth/sakauth/*
Adds collector configuration, factory registration, SAK enrollment, JWT propagation and refresh handling, actor-ID extraction, and tests for error, redirect, replay, concurrency, and refresh cases.
Collector build and integration validation
otelcol/Dockerfile, otelcol/otelcol-builder.yaml, deployments/otel-collector/config.yaml, otelcol/auth/sakauth/collector_integration_test.go, .github/workflows/ci.yml
Builds the custom collector image, configures metrics and logs pipelines, and validates end-to-end OTLP forwarding across amd64 and arm64 build jobs.
Agent collector routing
internal/config/config.go, internal/config/config_test.go, internal/exporter/*, cmd/fleetint/run.go, cmd/fleetint/metadata.go, cmd/fleetint/gateway_test.go
Adds and validates collector_endpoint, routes metrics and logs through the collector without forwarding the node token, supports environment configuration, and centralizes metadata masking.
Helm gateway deployment
deployments/helm/fleet-intelligence-agent/values.yaml, deployments/helm/fleet-intelligence-agent/templates/*otel-gateway*, deployments/helm/fleet-intelligence-agent/templates/daemonset.yaml
Adds optional collector configuration and Kubernetes resources, including authentication secrets, service routing, health probes, security settings, network policy, disruption budget, and agent endpoint injection.
Build and release wiring
Makefile, .gitignore, .github/workflows/release.yml
Allows revision overrides, passes version metadata to Docker tests, ignores generated collector binaries, builds multi-architecture collector images, and reports their release tags.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent as fleet-intelligence-agent
  participant Gateway as fleetint-otelcol
  participant Enrollment as enrollment endpoint
  participant Backend as OTLP backend
  Agent->>Gateway: Send OTLP metrics and logs
  Gateway->>Enrollment: Enroll with SAK
  Enrollment-->>Gateway: Return JWT assertion
  Gateway->>Backend: Export metrics and logs with JWT
  Backend-->>Gateway: Return response or refreshed JWT
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding an authenticated OpenTelemetry gateway.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rsampaio-otel-gateway

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 188-189: Update the actions/checkout@v4 step in the build job to
set persist-credentials to false, ensuring the checkout action does not store
the GITHUB_TOKEN in git configuration before third-party tooling runs.
- Around line 191-197: Update the “Set up Go” workflow step to use
actions/setup-go@v6 instead of v4, keeping its existing go-version configuration
unchanged.

In `@cmd/fleetint/run.go`:
- Around line 225-227: Update the FLEETINT_COLLECTOR_ENDPOINT handling in the
environment override block so Infow does not log the raw endpoint value. Log
only that the collector endpoint override was enabled, or pass a safely redacted
endpoint while preserving assignment of the full value to he.CollectorEndpoint.

In `@internal/config/config.go`:
- Around line 181-190: Update the collector_endpoint validation around
parsed.ForceQuery and the raw endpoint string to reject bare trailing ? and #
characters, while preserving the existing credential, query, fragment, and
HTTP(S) checks. Add regression tests covering http://collector.example? and
http://collector.example#.

In `@otelcol/auth/sakauth/config.go`:
- Around line 57-59: Update the SAK enrollment TLS validation in the
configuration validation method containing the c.TLS.Insecure check to also
reject c.TLS.InsecureSkipVerify. Return the same validation error for either
insecure TLS setting, ensuring enrollment requires HTTPS certificate
verification.

In `@otelcol/auth/sakauth/extension.go`:
- Around line 246-252: Guard the response-token update in the request flow
around the jwt_assertion handling so it only applies when the shared rt.ext.jwt
still equals the request’s usedJWT; otherwise leave the newer token and
customerID unchanged. Add a regression test covering concurrent or out-of-order
responses, including a 401 refresh, to verify stale responses cannot overwrite
the newest JWT.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9c891f07-c5ab-4125-b79f-f13e6e4bb1f6

📥 Commits

Reviewing files that changed from the base of the PR and between b7e7093 and 71498ff.

⛔ Files ignored due to path filters (1)
  • otelcol/auth/sakauth/go.sum is excluded by !**/*.sum
📒 Files selected for processing (29)
  • .dockerignore
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .gitignore
  • Makefile
  • cmd/fleetint/gateway_test.go
  • cmd/fleetint/metadata.go
  • cmd/fleetint/run.go
  • deployments/helm/fleet-intelligence-agent/templates/daemonset.yaml
  • deployments/helm/fleet-intelligence-agent/templates/otel-gateway-configmap.yaml
  • deployments/helm/fleet-intelligence-agent/templates/otel-gateway-deployment.yaml
  • deployments/helm/fleet-intelligence-agent/templates/otel-gateway-networkpolicy.yaml
  • deployments/helm/fleet-intelligence-agent/templates/otel-gateway-pdb.yaml
  • deployments/helm/fleet-intelligence-agent/templates/otel-gateway-secret.yaml
  • deployments/helm/fleet-intelligence-agent/templates/otel-gateway-service.yaml
  • deployments/helm/fleet-intelligence-agent/values.yaml
  • deployments/otel-collector/config.yaml
  • internal/config/config.go
  • internal/config/config_test.go
  • internal/exporter/exporter.go
  • internal/exporter/exporter_test.go
  • otelcol/Dockerfile
  • otelcol/auth/sakauth/collector_integration_test.go
  • otelcol/auth/sakauth/config.go
  • otelcol/auth/sakauth/extension.go
  • otelcol/auth/sakauth/extension_test.go
  • otelcol/auth/sakauth/factory.go
  • otelcol/auth/sakauth/go.mod
  • otelcol/otelcol-builder.yaml

Comment thread .github/workflows/ci.yml
Comment on lines +188 to +189
- name: Checkout code
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable credential persistence on checkout for this build job.

This job installs and runs third-party build tooling (go install .../builder@v0.156.0, then compiles further transitive dependencies via ocb). Without persist-credentials: false, the GITHUB_TOKEN is persisted in git config and readable by any code executed later in the job, widening the blast radius if a fetched dependency is compromised.

🔒 Proposed fix
       - name: Checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 188-189: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 188 - 189, Update the
actions/checkout@v4 step in the build job to set persist-credentials to false,
ensuring the checkout action does not store the GITHUB_TOKEN in git
configuration before third-party tooling runs.

Source: Linters/SAST tools

Comment thread .github/workflows/ci.yml
Comment on lines +191 to +197
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}

- name: Install OTel Collector Builder
run: go install go.opentelemetry.io/collector/cmd/builder@v0.156.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the workflow trigger and relevant job section.
file=".github/workflows/ci.yml"
echo "== File lines around trigger =="
nl -ba "$file" | sed -n '1,120p'
echo
echo "== File lines around setup-go step =="
nl -ba "$file" | sed -n '170,220p'

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 241


🌐 Web query:

actions/setup-go v6 cache default enabled cache behavior workflow cache poisoning GitHub Actions

💡 Result:

The actions/setup-go action (including v6) has caching enabled by default [1][2]. The action supports caching for Go modules and build outputs to improve performance, using the cache input which defaults to true [1][3]. Regarding cache poisoning, this is a known security concern in GitHub Actions environments [4][5]. Because actions/setup-go historically enables read-write caching by default, untrusted workflows—such as those triggered by pull requests from forks or external contributors—can potentially write malicious artifacts to the shared cache [4][6]. These tainted artifacts can then be restored by subsequent, trusted workflow runs on the main branch, leading to a supply chain vulnerability [4][5]. To mitigate this risk, several strategies are employed: 1. Manual Disabling: In sensitive workflows, such as release or deployment pipelines, it is standard practice to explicitly set cache: false to prevent the action from writing to the cache [5][6]. 2. Read-Only Cache Support: Recent updates to actions/setup-go include the addition of a cache-write input [4]. By setting cache-write: false, users can allow the action to restore existing caches while skipping the post-step cache save, effectively implementing a read-only cache mode [4]. This allows workflows (like PR checks) to benefit from existing dependencies without risking the integrity of the cache for future runs [4]. When designing workflows, it is recommended to evaluate whether full read-write caching is necessary or if a read-only configuration provides sufficient protection against cache poisoning [4][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/ci.yml"

echo "== Trigger and top-level workflow lines =="
sed -n '1,90p' "$file" | cat -n

echo
echo "== Step around actions/setup-go =="
sed -n '180,205p' "$file" | cat -n

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 4316


🌐 Web query:

actions/setup-go v4 deprecated Node runtime current major v6 release notes

💡 Result:

The actions/setup-go action has progressed through several major versions, primarily driven by the deprecation and end-of-life cycles of Node.js runtimes on GitHub Actions runners [1][2][3]. Key points regarding the transition and v6 release: 1. Node.js Runtime Updates: Older versions of the action used outdated Node.js runtimes. Specifically, v5 upgraded the runtime from Node 16 to Node 20 [2]. The current major release, v6, further upgrades the Node.js runtime from Node 20 to Node 24 [3]. 2. Deprecation Context: The deprecation of Node 20 on GitHub Actions runners was announced in September 2025, as Node 20 reached its end-of-life (EOL) in April 2026 [1]. Users are encouraged to update to actions that support newer Node.js runtimes, such as v6 of setup-go [4][3]. 3. v6 Release Notes: The v6.0.0 release (published September 4, 2025) includes the upgrade to Node 24 and improvements to toolchain handling [3]. Because of the runtime change, users must ensure their runners are on version v2.327.1 or later to maintain compatibility [4][3]. Additional enhancements in the v6.x series have included updated download sources for Go distributions and support for.tool-versions files [5]. In summary, the transition from v4 to v6 involves significant updates to the underlying Node.js runtime (from Node 16 in v4/early versions to Node 24 in v6) to maintain compliance with GitHub Actions runner lifecycle requirements [2][3].

Citations:


🌐 Web query:

GitHub Actions pull_request fork cache save actions/setup-go default cache poisoning trusted push restore

💡 Result:

When using GitHub Actions, specifically with pull_request events from forks or pull_request_target workflows, understanding cache access and security risks is critical. Cache Access Limitations By default, GitHub Actions enforces cache isolation [1][2]. A workflow run triggered by a pull request from a fork has restricted access to the cache: - It can restore existing cache entries available to the base branch or default branch [1]. - It is generally restricted from creating or overwriting cache entries to prevent unauthorized cache modifications [3][1]. When a cache write is denied in these scenarios, the actions/cache step reports a warning but does not fail the job [3][4]. Security Risks: Cache Poisoning "Cache poisoning" is a security concern where an attacker attempts to inject malicious data into the cache [5][6][7]. - Risk with pull_request_target: This trigger runs in the context of the base repository and has access to secrets and write permissions [8][4][6]. If a workflow using pull_request_target is misconfigured to check out and execute code from an untrusted fork (the "Pwn Request" pattern), that code runs with the privileges of the base repository [5][6][7]. - Mechanism: Even if you restrict the job's GITHUB_TOKEN permissions (e.g., contents: read), the cache save step uses an internal, runner-specific token to authenticate with the cache service [5][6][7]. Consequently, if malicious code runs during the workflow, it can write arbitrary data to the cache under a deterministic key that a future, trusted workflow might restore [6][7]. This can lead to downstream supply-chain compromises [5][7]. Best Practices for actions/setup-go and Caching The actions/setup-go action uses actions/cache internally [9][10]. To maintain security: - Avoid Unsafe Saves: Do not allow cache writes in workflows that execute untrusted fork code (such as those using pull_request_target that check out the PR's head ref) [5][11]. - Use Restore-Only Logic: In untrusted contexts, configure workflows to be "restore-only" [5]. You can implement custom logic to detect if a PR is from a fork and modify your cache keys to prevent collision with trusted branches [12]. - Rely on Trusted Triggers: Only perform cache saves in workflows triggered by trusted events, such as push to your default branch [8][4]. For further implementation guidance, refer to the documentation on securing pull_request_target workflows and the specific behavior of the actions/cache action [8][3][4].

Citations:


🌐 Web query:

GitHub Actions cache forked pull requests read-only save disabled official docs

💡 Result:

GitHub Actions restricts cache access for workflows triggered by pull requests from forks to ensure security and prevent cache poisoning [1][2]. In these scenarios, workflows have read-only access to the cache, meaning they can restore existing cache entries but cannot save or create new ones [1][3]. When a workflow attempts to save a cache while in a read-only state: - The job does not fail [1][3]. - The actions/cache action reports a warning (e.g., "cache write denied") in the workflow logs [1][3]. - The step completes successfully without writing any data to the cache [1][3]. This behavior is a security measure applied to untrusted triggers, including pull requests from forks, to prevent unauthorized modification of the shared cache [4][2]. For workflows that require populating the cache, GitHub recommends using trusted triggers, such as events on the base repository's default branch (e.g., push events) [2]. As of June 2026, GitHub has further reinforced read-only cache protections for workflow events that can be triggered without explicit write permissions [4].

Citations:


Update actions/setup-go to v6. actions/setup-go@v4 is behind the current major and should be bumped to avoid Node-runtime deprecation issues.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 192-192: the runner of "actions/setup-go@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 zizmor (1.26.1)

[error] 192-192: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 191 - 197, Update the “Set up Go”
workflow step to use actions/setup-go@v6 instead of v4, keeping its existing
go-version configuration unchanged.

Source: Linters/SAST tools

Comment thread cmd/fleetint/run.go
Comment on lines +225 to +227
if val := os.Getenv("FLEETINT_COLLECTOR_ENDPOINT"); val != "" {
he.CollectorEndpoint = val
log.Logger.Infow("set OTel gateway collector endpoint from env", "collector_endpoint", val)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log the raw collector endpoint.

FLEETINT_COLLECTOR_ENDPOINT is runtime-controlled, but the full value is written to logs. A misconfigured value containing userinfo, a token in the path, or other sensitive material would leak into application logs. Log only that the override was enabled, or emit a safely redacted value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/fleetint/run.go` around lines 225 - 227, Update the
FLEETINT_COLLECTOR_ENDPOINT handling in the environment override block so Infow
does not log the raw endpoint value. Log only that the collector endpoint
override was enabled, or pass a safely redacted endpoint while preserving
assignment of the full value to he.CollectorEndpoint.

Comment thread internal/config/config.go
Comment on lines +181 to +190
if endpoint := config.HealthExporter.CollectorEndpoint; endpoint != "" {
parsed, err := url.Parse(endpoint)
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Host == "" {
return fmt.Errorf("collector_endpoint must be an absolute HTTP(S) URL")
}
if parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" {
return fmt.Errorf("collector_endpoint must not contain credentials, a query, or a fragment")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/sh
go doc net/url.URL.ForceQuery

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 466


🏁 Script executed:

#!/bin/sh
set -eu

printf '\n## internal/config/config.go (around the cited lines)\n'
sed -n '150,230p' internal/config/config.go

printf '\n## Search for refreshConfigFromMetadata and collector_endpoint tests\n'
rg -n "refreshConfigFromMetadata|collector_endpoint|ForceQuery|RawQuery|Fragment" internal -g '!**/vendor/**'

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 5035


🏁 Script executed:

#!/bin/sh
set -eu

printf '\n## internal/exporter/exporter.go (refreshConfigFromMetadata)\n'
sed -n '260,360p' internal/exporter/exporter.go

printf '\n## internal/endpoint/endpoint.go (related URL validation)\n'
sed -n '1,220p' internal/endpoint/endpoint.go

printf '\n## internal/config/config_test.go around collector_endpoint cases\n'
sed -n '280,360p' internal/config/config_test.go

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 12909


🏁 Script executed:

#!/bin/sh
set -eu

cat <<'GO' >/tmp/check_url.go
package main

import (
	"fmt"
	"net/url"
)

func main() {
	for _, raw := range []string{
		"http://collector.example?",
		"http://collector.example#",
		"http://collector.example",
	} {
		u, err := url.Parse(raw)
		fmt.Printf("RAW=%q err=%v\n", raw, err)
		if err == nil {
			fmt.Printf("  Scheme=%q Host=%q Path=%q RawQuery=%q Fragment=%q ForceQuery=%v\n",
				u.Scheme, u.Host, u.Path, u.RawQuery, u.Fragment, u.ForceQuery)
			fmt.Printf("  String=%q\n", u.String())
		}
	}
}
GO

go run /tmp/check_url.go

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 672


Reject bare ? and # in collector_endpoint. url.Parse("http://collector.example?") sets ForceQuery, and a trailing # is dropped by Parse, so both bypass the current checks and later get appended to /v1/... incorrectly. Check parsed.ForceQuery and the raw input for a trailing #; add regression tests for http://collector.example? and http://collector.example#.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/config/config.go` around lines 181 - 190, Update the
collector_endpoint validation around parsed.ForceQuery and the raw endpoint
string to reject bare trailing ? and # characters, while preserving the existing
credential, query, fragment, and HTTP(S) checks. Add regression tests covering
http://collector.example? and http://collector.example#.

Comment on lines +57 to +59
if c.TLS.Insecure {
return fmt.Errorf("sakauth: tls.insecure is not allowed; enroll_endpoint must use HTTPS")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
(cd otelcol/auth/sakauth && go doc go.opentelemetry.io/collector/config/configtls.ClientConfig) |
  grep -F 'InsecureSkipVerify'

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 313


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '\n## config.go excerpt\n'
sed -n '1,140p' otelcol/auth/sakauth/config.go

printf '\n## InsecureSkipVerify references\n'
rg -n "InsecureSkipVerify|insecure_skip_verify" .

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 2764


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

sed -n '110,190p' otelcol/auth/sakauth/collector_integration_test.go

Repository: NVIDIA/fleet-intelligence-agent

Length of output: 2308


Reject tls.insecure_skip_verify for SAK enrollment.
tls.insecure is blocked, but HTTPS with certificate verification disabled still lets a MITM capture the SAK and enrollment JWT. Reject tls.insecure_skip_verify here too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@otelcol/auth/sakauth/config.go` around lines 57 - 59, Update the SAK
enrollment TLS validation in the configuration validation method containing the
c.TLS.Insecure check to also reject c.TLS.InsecureSkipVerify. Return the same
validation error for either insecure TLS setting, ensuring enrollment requires
HTTPS certificate verification.

Comment on lines +246 to +252
// The backend proactively sends a refreshed JWT in the response header when
// the current token is near expiry (>80% of max age). Store it immediately.
if newJWT := resp.Header.Get("jwt_assertion"); newJWT != "" {
rt.ext.mu.Lock()
rt.ext.jwt = newJWT
rt.ext.customerID = extractCustomerID(newJWT)
rt.ext.mu.Unlock()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Prevent stale responses from overwriting a newer JWT.

Concurrent requests can complete out of order. A delayed response using usedJWT can currently replace a newer token installed by another response or 401 refresh, causing subsequent exports to regress to stale credentials.

Only apply the response token while the shared JWT still equals usedJWT, and add an out-of-order response regression test.

Proposed fix
 if newJWT := resp.Header.Get("jwt_assertion"); newJWT != "" {
+	newCustomerID := extractCustomerID(newJWT)
 	rt.ext.mu.Lock()
-	rt.ext.jwt = newJWT
-	rt.ext.customerID = extractCustomerID(newJWT)
+	if rt.ext.jwt == usedJWT {
+		rt.ext.jwt = newJWT
+		rt.ext.customerID = newCustomerID
+	}
 	rt.ext.mu.Unlock()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// The backend proactively sends a refreshed JWT in the response header when
// the current token is near expiry (>80% of max age). Store it immediately.
if newJWT := resp.Header.Get("jwt_assertion"); newJWT != "" {
rt.ext.mu.Lock()
rt.ext.jwt = newJWT
rt.ext.customerID = extractCustomerID(newJWT)
rt.ext.mu.Unlock()
// The backend proactively sends a refreshed JWT in the response header when
// the current token is near expiry (>80% of max age). Store it immediately.
if newJWT := resp.Header.Get("jwt_assertion"); newJWT != "" {
newCustomerID := extractCustomerID(newJWT)
rt.ext.mu.Lock()
if rt.ext.jwt == usedJWT {
rt.ext.jwt = newJWT
rt.ext.customerID = newCustomerID
}
rt.ext.mu.Unlock()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@otelcol/auth/sakauth/extension.go` around lines 246 - 252, Guard the
response-token update in the request flow around the jwt_assertion handling so
it only applies when the shared rt.ext.jwt still equals the request’s usedJWT;
otherwise leave the newer token and customerID unchanged. Add a regression test
covering concurrent or out-of-order responses, including a 401 refresh, to
verify stale responses cannot overwrite the newest JWT.

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.

1 participant