-
Notifications
You must be signed in to change notification settings - Fork 13
feat: add authenticated OpenTelemetry gateway #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| .git | ||
| .github | ||
| .cache | ||
| coverage | ||
| dist | ||
| bin | ||
| build | ||
| otelcol/bin | ||
| **/*.test | ||
| **/*.out | ||
| **/*.log | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -177,6 +177,40 @@ jobs: | |||||||||||||
| path: fleetint-${{ matrix.goos }}-${{ matrix.goarch }} | ||||||||||||||
| retention-days: 7 | ||||||||||||||
|
|
||||||||||||||
| build_check_otelcol: | ||||||||||||||
| name: Build Check (otelcol) | ||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||
| needs: [lint] | ||||||||||||||
| strategy: | ||||||||||||||
| matrix: | ||||||||||||||
| goarch: [amd64, arm64] | ||||||||||||||
| steps: | ||||||||||||||
| - name: Checkout code | ||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||
|
Comment on lines
+188
to
+189
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ( 🔒 Proposed fix - name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@v4
+ with:
+ persist-credentials: false📝 Committable suggestion
Suggested change
🧰 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 AgentsSource: Linters/SAST tools |
||||||||||||||
|
|
||||||||||||||
| - 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 | ||||||||||||||
|
Comment on lines
+191
to
+197
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
💡 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 -nRepository: NVIDIA/fleet-intelligence-agent Length of output: 4316 🌐 Web query:
💡 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:
💡 Result: When using GitHub Actions, specifically with Citations:
🌐 Web query:
💡 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 Citations:
Update 🧰 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 AgentsSource: Linters/SAST tools |
||||||||||||||
|
|
||||||||||||||
| - name: Build fleetint-otelcol | ||||||||||||||
| working-directory: otelcol | ||||||||||||||
| env: | ||||||||||||||
| GOOS: linux | ||||||||||||||
| GOARCH: ${{ matrix.goarch }} | ||||||||||||||
| CGO_ENABLED: 0 | ||||||||||||||
| run: builder --config=otelcol-builder.yaml | ||||||||||||||
|
|
||||||||||||||
| - name: Run OTel gateway integration test | ||||||||||||||
| if: matrix.goarch == 'amd64' | ||||||||||||||
| working-directory: otelcol/auth/sakauth | ||||||||||||||
| env: | ||||||||||||||
| FLEETINT_OTELCOL_INTEGRATION: "1" | ||||||||||||||
| run: go test -race -run '^TestCollectorGatewayEndToEnd$' . | ||||||||||||||
|
|
||||||||||||||
| codeql: | ||||||||||||||
| name: CodeQL Analysis | ||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,3 +94,4 @@ AGENTS.md | |
|
|
||
| .worktrees/ | ||
| docs/plans/ | ||
| otelcol/bin/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| pkgmetadata "github.com/NVIDIA/fleet-intelligence-sdk/pkg/metadata" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/NVIDIA/fleet-intelligence-agent/internal/config" | ||
| ) | ||
|
|
||
| func TestConfigureHealthExporterFromEnvCollectorEndpoint(t *testing.T) { | ||
| t.Setenv("FLEETINT_COLLECTOR_ENDPOINT", "http://fleetint-otel-gateway:4318") | ||
| cfg := &config.Config{HealthExporter: &config.HealthExporterConfig{}} | ||
|
|
||
| require.NoError(t, configureHealthExporterFromEnv(cfg)) | ||
| require.Equal(t, "http://fleetint-otel-gateway:4318", cfg.HealthExporter.CollectorEndpoint) | ||
| } | ||
|
|
||
| func TestConfigureHealthExporterFromEnvPreservesCollectorEndpointWhenUnset(t *testing.T) { | ||
| t.Setenv("FLEETINT_COLLECTOR_ENDPOINT", "") | ||
| cfg := &config.Config{HealthExporter: &config.HealthExporterConfig{ | ||
| CollectorEndpoint: "https://collector.example", | ||
| }} | ||
|
|
||
| require.NoError(t, configureHealthExporterFromEnv(cfg)) | ||
| require.Equal(t, "https://collector.example", cfg.HealthExporter.CollectorEndpoint) | ||
| } | ||
|
|
||
| func TestMaskMetadataValue(t *testing.T) { | ||
| const secret = "secret-token-value" | ||
|
|
||
| require.Equal(t, pkgmetadata.MaskToken(secret), maskMetadataValue(pkgmetadata.MetadataKeyToken, secret)) | ||
| require.Equal(t, pkgmetadata.MaskToken(secret), maskMetadataValue("sak_token", secret)) | ||
| require.Equal(t, secret, maskMetadataValue("backend_base_url", secret)) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,14 @@ func configureHealthExporterFromEnv(cfg *config.Config) error { | |
| return err | ||
| } | ||
|
|
||
| // OTel gateway collector mode: when set, metrics/logs are routed to the gateway | ||
| // instead of the backend directly. Enrollment remains independent for inventory | ||
| // and attestation; backend credentials are not forwarded to the gateway. | ||
| if val := os.Getenv("FLEETINT_COLLECTOR_ENDPOINT"); val != "" { | ||
| he.CollectorEndpoint = val | ||
| log.Logger.Infow("set OTel gateway collector endpoint from env", "collector_endpoint", val) | ||
|
Comment on lines
+225
to
+227
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Do not log the raw collector endpoint.
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| {{- if .Values.otelGateway.enabled }} | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: {{ include "fleet-intelligence-agent.fullname" . }}-otel-gateway | ||
| labels: | ||
| {{- include "fleet-intelligence-agent.labels" . | nindent 4 }} | ||
| data: | ||
| config.yaml: | | ||
| extensions: | ||
| # Fleet Intelligence auth: enrolls with the backend using the SAK token, extracts | ||
| # the customer ID from the returned JWT, and refreshes the JWT proactively via | ||
| # response headers and on 401 as a fallback. | ||
| sakauth: | ||
| enroll_endpoint: ${env:ENROLL_ENDPOINT} | ||
| sak_token: ${env:SAK_TOKEN} | ||
| health_check: | ||
| endpoint: 0.0.0.0:13133 | ||
|
|
||
| receivers: | ||
| otlp: | ||
| protocols: | ||
| http: | ||
| # No inbound auth: the gateway is a ClusterIP service accessible only | ||
| # within the cluster. Cluster network isolation is the security boundary. | ||
| # The gateway uses its own SAK JWT when forwarding to the backend. | ||
| # Agent identity flows through OTLP resource attributes (machine.id, GPU UUIDs). | ||
| endpoint: 0.0.0.0:{{ .Values.otelGateway.service.port }} | ||
|
|
||
| processors: | ||
| memory_limiter: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. from default values.yaml, is this limiter computed from the total node mem or the pod limit? if it's the former, there could possibly be OOM issue if the pod is capped at a smaller mem e.g. |
||
| check_interval: 5s | ||
| limit_percentage: {{ .Values.otelGateway.memoryLimiter.limitPercentage }} | ||
| spike_limit_percentage: {{ .Values.otelGateway.memoryLimiter.spikeLimitPercentage }} | ||
| batch: | ||
| timeout: 10s | ||
| send_batch_size: 1000 | ||
|
|
||
| exporters: | ||
| otlp_http/backend: | ||
| metrics_endpoint: ${env:BACKEND_ENDPOINT}/metrics | ||
| logs_endpoint: ${env:BACKEND_ENDPOINT}/logs | ||
| compression: none | ||
| auth: | ||
| authenticator: sakauth | ||
| retry_on_failure: | ||
| enabled: true | ||
| initial_interval: 5s | ||
| max_interval: 30s | ||
| max_elapsed_time: 300s | ||
| sending_queue: | ||
| enabled: true | ||
| num_consumers: 4 | ||
| queue_size: 1000 | ||
|
|
||
| service: | ||
| extensions: [sakauth, health_check] | ||
| pipelines: | ||
| metrics: | ||
| receivers: [otlp] | ||
| processors: [memory_limiter, batch] | ||
| exporters: [otlp_http/backend] | ||
| logs: | ||
| receivers: [otlp] | ||
| processors: [memory_limiter, batch] | ||
| exporters: [otlp_http/backend] | ||
| telemetry: | ||
| logs: | ||
| level: warn | ||
| {{- end }} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otelcol/auth/sakauthis a separate Go module (it has its owngo.mod). In.github/workflows/ci.yml,unit_testruns make test →go test ./...from the root module, which does not descend into nested modules. similar to the lint runsgolangci-lintat the root and vuln runsgovulncheck ./...at the root.build_check_otelcolonly runs the single integration test: run^TestCollectorGatewayEndToEnd$and only for amd64). soextension_test.goand config tests are never executed in CI. Should we add something like following in ci to include that?