Skip to content

feat: plumb user secrets through provisioner chain to terraform#24542

Open
zedkipp wants to merge 7 commits intomainfrom
zedkipp/plat-77-build-time-secrets-injection
Open

feat: plumb user secrets through provisioner chain to terraform#24542
zedkipp wants to merge 7 commits intomainfrom
zedkipp/plat-77-build-time-secrets-injection

Conversation

@zedkipp
Copy link
Copy Markdown
Contributor

@zedkipp zedkipp commented Apr 20, 2026

Passes user secrets from coderd to the Terraform process at workspace build time so the data.coder_secret data source in terraform-provider-coder can resolve values at plan time.

Secrets traverse two proto hops: provisionerdserver fetches them viaListUserSecretsWithValues, attaches them to AcquiredJob.WorkspaceBuild.user_secrets on provisionerd.proto; runner.go forwards into PlanRequest.user_secrets on provisioner.proto; the Terraform provisioner encodes each as CODER_SECRET_ENV_<name> or CODER_SECRET_FILE_<hex(path)> before invoking terraform plan. Only plan requests carry secrets; apply runs with nil because values are baked into plan state.

Fetch is gated on a workspace transitioning to start. stop and delete transitions never carry secrets, so revoking or deleting a stored secret cannot make a workspace unstoppable. DB errors on the fetch fail the job outright rather than silently continuing with an empty secret set.

Corresponding terraform-provider-coder change: coder/terraform-provider-coder#501

@zedkipp zedkipp force-pushed the zedkipp/plat-77-build-time-secrets-injection branch 3 times, most recently from b7bc9ed to e457dc3 Compare April 21, 2026 22:00
@zedkipp zedkipp force-pushed the zedkipp/plat-77-build-time-secrets-injection branch 2 times, most recently from 6fad51d to 01ca924 Compare April 22, 2026 21:44
Passes user secrets from coderd to the Terraform process at workspace
build time so the `data.coder_secret` data source in
terraform-provider-coder can resolve values at plan time.

Secrets traverse two proto hops: `provisionerdserver` fetches them via
`ListUserSecretsWithValues`, attaches them to
`AcquiredJob.WorkspaceBuild.user_secrets` on `provisionerd.proto`;
`runner.go` forwards into `PlanRequest.user_secrets` on
`provisioner.proto`; the Terraform provisioner encodes each as
`CODER_SECRET_ENV_<name>` or `CODER_SECRET_FILE_<hex(path)>` before
invoking `terraform plan`. Only plan requests carry secrets; apply runs
with `nil` because values are baked into plan state.

Fetch is gated on `WorkspaceTransitionStart`: stop and delete
transitions never carry secrets, so revoking or deleting a stored
secret cannot make a workspace unstoppable. DB errors on the fetch
fail the job outright rather than silently continuing with an empty
secret set.
@zedkipp zedkipp force-pushed the zedkipp/plat-77-build-time-secrets-injection branch from 01ca924 to a8372f7 Compare April 22, 2026 21:52
@zedkipp zedkipp changed the title feat: build-time user secret injection into terraform provisioner feat: plumb user secrets through provisioner chain to terraform Apr 22, 2026
@zedkipp zedkipp marked this pull request as ready for review April 22, 2026 22:15
Copy link
Copy Markdown
Member

@johnstcn johnstcn left a comment

Choose a reason for hiding this comment

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

need to update provisionerd/proto/version.go

@zedkipp
Copy link
Copy Markdown
Contributor Author

zedkipp commented Apr 23, 2026

need to update provisionerd/proto/version.go

Updated, thanks! I'm going to follow up this PR with a short doc comment in the proto file somewhere that notes the API version must be updated. Myself and an agent missed updating this. I suspect an agent would have caught it when asking "what have I missed?".

@zedkipp zedkipp requested a review from johnstcn April 23, 2026 17:02
@zedkipp
Copy link
Copy Markdown
Contributor Author

zedkipp commented Apr 23, 2026

/coder-agents-review

Copy link
Copy Markdown

@coder-agents-review coder-agents-review Bot left a comment

Choose a reason for hiding this comment

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

Well-structured plumbing change. The proto chain is correctly layered, the transition gating is sound, the error path is explicit (fail-fast, not silent-empty), and the test suite covers the key scenarios with genuine assertions. The InTx-aware DB wrapper in the test harness shows attention to the project's documented pitfalls. Melody walked all six serialization pairings (DB to proto to proto to env vars) and found every layer agrees on types, optionality, and filtering. Kurapika verified authorization, identity binding, input validation, logging, and transport.

4 P3, 2 P4, 4 Nit. No blockers.

Hisoka, tracing the canary path in safeenv.go: "A function that looks like it splits on = but doesn't. A safety net that was never connected. I've been waiting to find one of these."


provisioner/terraform/safeenv.go:26

P4 [DEREM-7] Pre-existing: SplitN(env, "=", 1) returns at most 1 element, which is the whole key=value string. So envName("CODER_DONT_PASS=true") returns "CODER_DONT_PASS=true", not "CODER_DONT_PASS". This means isCanarySet always returns false (it compares the full string against just the name), and the canary check at executor.go:85 is a no-op.

safeEnviron() still works correctly because HasPrefix(name, "CODER_") matches the full string. But the canary was designed to detect regressions if the stripping logic ever breaks. With this PR routing secret values through the same env slice, the consequence of a safeEnviron regression includes leaking user secrets alongside host env vars, with no canary to catch it.

Fix: SplitN(env, "=", 2). Not introduced by this PR, but the consequence is elevated. (Hisoka)

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread coderd/provisionerdserver/provisionerdserver_test.go Outdated
Comment thread provisionersdk/proto/provisioner.proto
Comment thread provisioner/terraform/provision.go
Comment thread coderd/provisionerdserver/provisionerdserver.go
Comment thread provisioner/terraform/provision.go
Comment thread provisionersdk/proto/provisioner.proto
Comment thread provisionerd/proto/version.go Outdated
Comment thread provisionerd/proto/version.go
Comment thread provisioner/terraform/provision.go
zedkipp added 2 commits April 23, 2026 13:46
Update comments and add test coverage for delete transition.
Copy link
Copy Markdown
Contributor

@dylanhuff-at-coder dylanhuff-at-coder left a comment

Choose a reason for hiding this comment

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

First pass, will look more thoroughly tomorrow, but my understanding is that this will store unencrypted secrets in workspace_builds.provisioner_state. If that is true, should this PR scrub the secret before storing it or is that slated for future work?

Copy link
Copy Markdown
Member

It's a good point, provisioner state is extremely sensitive and can contain secrets already. I'd view this as a follow-up and out of scope of this change.

Comment thread coderd/provisionerdserver/provisionerdserver_test.go Outdated
Comment thread coderd/provisionerdserver/provisionerdserver_test.go Outdated
Comment thread coderd/provisionerdserver/provisionerdserver.go Outdated
@zedkipp
Copy link
Copy Markdown
Contributor Author

zedkipp commented Apr 24, 2026

RE: provisioner state, I proposed in the RFC that we scrub secrets from Terraform state before it persists https://www.notion.so/coderhq/User-Secrets-32cd579be59280a98192dec7b3b5daf1?source=copy_link#335d579be5928097bb40c10862694eb4

I was planning to tackle this in a subsequent PR to prevent this one from blowing up too much.

Comment thread coderd/provisionerdserver/provisionerdserver_test.go
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