If you discover a potential security vulnerability, please do not open a public issue or pull request.
- Report via the NVIDIA Vulnerability Disclosure Program (preferred).
- Email psirt@nvidia.com. Sensitive reports may be encrypted with the NVIDIA public PGP key.
- Use GitHub's private vulnerability reporting on this repository if it is enabled.
Include the affected provider version (the -X main.version value baked in by
.goreleaser.yml), the Terraform CLI version, a minimal HCL configuration that
reproduces the issue, the NGC endpoint the provider was pointed at, and — with
all secret values redacted — the relevant TF_LOG=DEBUG output.
If a report involves a credential committed to this repository, treat that credential as compromised. Rotate it at the source of truth first, then remove or re-encrypt the committed copy.
NVIDIA PSIRT will acknowledge the report, validate its severity, coordinate remediation with the maintainers, and publish a security bulletin when appropriate.
This repository holds the NGC Terraform provider: a Go plugin binary, built on
the Terraform Plugin Framework, that lets practitioners declare NVIDIA Cloud
Function (NVCF) resources as infrastructure-as-code. It exposes two resources
(ngc_cloud_function, ngc_cloud_function_telemetry) and two matching data
sources, implemented under internal/provider/. main.go starts the plugin
server; there is no long-running service, no network listener, and no deployment
manifest anywhere in the repository.
This software operates at the CLI-tool / plugin level. Its primary security
responsibility is to protect the credential material it is handed — an NGC
personal API key, plus the third-party telemetry and function secrets a
practitioner passes through it — while faithfully translating Terraform plans
into NGC control-plane mutations. internal/provider/provider.go reads the key
from the ngc_api_key attribute or the NGC_API_KEY environment variable, and
internal/provider/utils/nvcf_client.go attaches it to every request as
Authorization: Bearer. The NGC base URL is practitioner-controlled
(ngc_endpoint / NGC_ENDPOINT, defaulting to https://api.ngc.nvidia.com),
and NVCFClient.NvcfEndpoint derives the org/team-scoped path
/v2/orgs/{ngc_org}/teams/{ngc_team} from provider configuration.
The blast radius of this repository is not the repository itself but its output.
.goreleaser.yml builds cross-platform archives, signs the SHA256SUMS using
code-signing credentials supplied by the release pipeline, publishes a GitHub
release under NVIDIA/terraform-provider-ngc, and pushes to an internal NVIDIA
Artifactory Terraform registry. Anything that reaches a release tag becomes a
signed artifact that executes on practitioner workstations and CI runners
holding live NGC credentials.
.github/workflows/ci.yml runs on main and on pull-request/[0-9]+ branches
— the branches created by NVIDIA's copy-pr-bot, which .github/copy-pr-bot.yaml
enables — and its integration-test job injects secrets.NGC_API_KEY (via the
integration-test GitHub environment) and runs acceptance tests that create
real, billable NVCF functions against a real org.
Repository Exposure Classification: Public. Basis: this repository is public on GitHub, so its contents and full commit history are readable by anyone on the internet, and its releases are consumed directly by external Terraform practitioners.
Service Exposure Classification: External / Regulated (high confidence).
Basis: the build output is externally distributed software — .goreleaser.yml
publishes signed release archives to this repository's public GitHub releases
and to an NVIDIA Artifactory Terraform registry, terraform-registry-manifest.json
packages it for Terraform registry consumption, and the provider handles
customer NGC API keys and third-party observability credentials inside the
practitioner's process. Externally distributed code with code signing in the
release path elevates to the highest tier.
No repository TAVA was found or supplied. This threat model covers the provider
source under internal/ and main.go, the release and CI automation, and the
committed test and example configuration; it does not cover the NGC/NVCF control
plane itself, Terraform Core, or any state backend a practitioner chooses.
Key trust boundaries are:
- Terraform CLI ↔ provider plugin process. Terraform Core passes the
practitioner's HCL configuration — including
ngc_api_key,ngc_cloud_function.secrets[].value, andngc_cloud_function_telemetry.secret.value— across the plugin RPC into code from this repository. Everything the provider receives is already trusted by the practitioner. - Provider process ↔ NGC/NVCF control plane.
NVCFClient.sendRequestininternal/provider/utils/nvcf_client.gois the single egress point; it decides which host receives the bearer token and which org/team path is mutated. - Terraform configuration and state ↔ secret material. The provider writes practitioner-supplied secrets into Terraform state and reads them back on refresh; Terraform, not this provider, controls where that state lands.
- Release automation ↔ signed release artifacts. The code-signing credentials and the GitHub and Artifactory publish targets sit on the far side of this boundary from ordinary contributors.
- External pull-request contributions ↔ CI secrets.
copy-pr-botmoves untrusted fork content ontopull-request/Nbranches inside this repository, where.github/workflows/ci.ymlcan run it withsecrets.NGC_API_KEY. - Provider ↔ third-party observability backends. The provider never contacts
Grafana Cloud, Splunk, Datadog, ServiceNow or Azure Monitor directly, but
ngc_cloud_function_telemetrytransports their credentials to NVCF, which does.
-
Compromise of the release and code-signing path yields supply-chain execution on every consumer.
.goreleaser.ymlsigns checksums using credentials supplied by the release pipeline, then publishes to this repository's public GitHub releases and to an NVIDIA Artifactory registry. An attacker who lands a commit on the release branch, forges a tag, or gains access to the signing credentials distributes a signed provider binary that Terraform executes locally with the practitioner'sNGC_API_KEYin its environment. This is the highest-impact scenario for this repository. -
TF_LOG=DEBUGwrites practitioner secrets to disk in cleartext.internal/provider/utils/nvcf_client.gocallstflog.SetField(ctx, "request_body", requestBody)andtflog.SetField(ctx, "response_body", string(body))immediately beforetflog.Debug(ctx, "Send request"). Notflog.MaskFieldValuesWithFieldKeysor equivalent masking is registered anywhere in the repository. Because the create/update request bodies forngc_cloud_function_telemetrycarrysecret.valueand those forngc_cloud_functioncarrysecrets[].value, any run with debug logging enabled — whichREADME.mdexplicitly instructs developers to turn on — persists third-party observability credentials and function secrets in plaintext log files and CI job output. TheSensitive: trueschema markers on those attributes suppress them in plan output but do not affecttflog. -
Practitioner-controlled endpoint enables silent credential exfiltration.
ngc_endpoint/NGC_ENDPOINTis an unvalidated free-form string that becomes the base URL inNVCFClient.NvcfEndpoint, andinternal/provider/planmodifier/cloud_function_artifact_uri_plan_modifier.goadditionally prefixes bare artifact URIs with$NGC_ENDPOINT. There is no allowlist, no scheme enforcement, and no TLS pinning, so a typo-squatted or attacker-injected endpoint value in a shared module, a CI variable, or a.tfvarsfile causes the NGC personal key to be sent, unaltered, to a host of the attacker's choosing. -
Process-wide client singleton leaks credentials and org scope between provider aliases.
internal/provider/utils/ngc_client.gostores the NVCF client in a package-levelnvcfClientguarded bynvcfClientOnce sync.Once, soNVCFClient()returns whichever client was constructed first for the lifetime of the plugin process. All fourConfigureimplementations go through it. A configuration that declares two aliasedngcproviders with differentngc_api_key,ngc_org, orngc_endpointvalues will silently drive every resource with the first alias's credentials and org path — creating or destroying NVCF functions in the wrong tenant, or sending one org's key to another org's endpoint. -
Secrets are persisted in Terraform state by design.
secretsSchema()ininternal/provider/cloud_function_resource.goand thesecretblock ininternal/provider/cloud_function_telemetry_resource.goboth accept a requiredvaluethat the provider round-trips through state. Terraform stores state unencrypted unless the backend encrypts it, so anyone with read access to the state file or its backend obtains the Grafana Cloud / Splunk / Datadog / ServiceNow credentials and any function secrets managed through this provider. The provider offers no write-only or ephemeral handling of these values. -
Untrusted pull-request code can run against a live NGC organization.
.github/copy-pr-bot.yamlsetsenabled: true, and.github/workflows/ci.ymltriggers onpull-request/[0-9]+branches. Theintegration-testjob bindsNGC_API_KEY: ${{ secrets.NGC_API_KEY }}and runs acceptance tests that execute Go test code taken from the branch under test. If a copied PR reaches that job before a maintainer reviews it — or if theintegration-testGitHub environment lacks required reviewers — attacker-authored test code runs with a live NGC key and can both exfiltrate it and create, mutate, or delete real NVCF functions and telemetry endpoints in the target org. -
Committed test configuration lacks the guard rail that was supposed to protect it.
test-config.env.examplestates that "test-config.env is gitignored and should NEVER be committed!", yettest-config.envandtest-config-stg.envare both tracked in this repository, and.gitignoredoes not list either file. They contain no API key today, but they do publish organization and cluster identifiers to every reader of this public repository — and, more importantly, the mechanism intended to stop a realNGC_API_KEYfrom ever being committed alongside them is absent. -
Leftover scaffolding ships credential-shaped configuration.
docker_compose/conf.jsonanddocker_compose/docker-compose.yamlare unmodified HashiCorp demo-application scaffolding carrying literalpassword=passwordvalues. Nothing ininternal/ormain.goreferences them. They are dead weight that confuses secret scanners, misleads readers about the provider's dependencies, and would become a real weak credential if anyone ever ran the compose file in a shared environment. -
Public repository disclosure is permanent. This repository is world-readable and every commit ever pushed, including history that has since been reverted, remains retrievable. Any credential, internal hostname, or account identifier that has entered the history must be treated as disclosed rather than merely removed, and rotated at its source of truth.
- The
NGC_API_KEYhanded to the provider is a scoped NGC personal key that carries only the Cloud Function permissions needed for the target org, is rotated on a schedule, and is never a long-lived org-admin key. ngc_endpoint/NGC_ENDPOINTis only ever set to a genuine NGC endpoint; the provider performs no validation of this value and cannot detect a hostile one.- Terraform state for configurations using this provider is stored in an encrypted, access-controlled backend, because the provider deliberately places telemetry and function secrets in state.
TF_LOG/TF_LOG_PATHoutput is treated as secret material and is not archived as a CI artifact, because request and response bodies are logged unmasked.- The release branch is protected, releases require review by someone other than the author, and the code-signing credentials are not reachable from lower-trust pipelines or from unprotected branches.
- The GitHub
integration-testenvironment enforces required reviewers, socopy-pr-bot-createdpull-request/Nbranches cannot reachsecrets.NGC_API_KEYbefore a maintainer has read the diff. - Consumers verify the published
_SHA256SUMSand its signature before installing the provider; the provider itself performs no self-integrity check. - Only one
ngcprovider configuration (or, at minimum, only one credential and one org) is used per Terraform run, because the NVCF client singleton cannot currently distinguish between provider aliases. - Terraform Core, the Go toolchain pinned in
.github/workflows/ci.yml, and the module dependencies tracked ingo.sumare trusted; the provider relies on Dependabot (.github/dependabot.yml) rather than any runtime check to keep them current.