mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: plumb user secrets through provisioner chain to terraform (#24542)
This change 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 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. Note that user secrets will be stored in the workspace_builds table in provisioner_state with other Terraform state (including other sensitive data).
This commit is contained in:
Generated
+54
@@ -477,6 +477,35 @@ export interface InitComplete {
|
||||
moduleFilesHash: Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* UserSecretValue carries a single user secret to a provisioner. env_name and
|
||||
* file_path describe the bindings the user requested when creating the secret.
|
||||
* The terraform provisioner exposes secrets via CODER_SECRET_ENV_* and
|
||||
* CODER_SECRET_FILE_* environment variables consumed by terraform-provider-coder's
|
||||
* coder_secret data source
|
||||
*/
|
||||
export interface UserSecretValue {
|
||||
/**
|
||||
* Environment variable name the user selected (e.g. "GITHUB_TOKEN"). Intended
|
||||
* to be treated as an opaque lookup key, i.e. consumers must preserve it
|
||||
* verbatim when matching against a data.coder_secret.env_name attribute.
|
||||
* Consumers can assume names are POSIX-compliant. Optional: env_name and
|
||||
* file_path are independent.
|
||||
*/
|
||||
envName: string;
|
||||
/**
|
||||
* Filesystem path the user requested this secret be bound to (e.g. "~/creds"
|
||||
* or "/etc/creds"). This path is not expanded. Expansion happens only where
|
||||
* the secret is actually materialized on disk. Intended to be treated as an
|
||||
* opaque lookup key, i.e. consumers must preserve it verbatim when matching
|
||||
* against a data.coder_secret.file attribute. Optional; env_name and
|
||||
* file_path are independent.
|
||||
*/
|
||||
filePath: string;
|
||||
/** Secret value, which may be arbitrary binary data. */
|
||||
value: Uint8Array;
|
||||
}
|
||||
|
||||
/** PlanRequest asks the provisioner to plan what resources & parameters it will create */
|
||||
export interface PlanRequest {
|
||||
metadata: Metadata | undefined;
|
||||
@@ -486,6 +515,13 @@ export interface PlanRequest {
|
||||
previousParameterValues: RichParameterValue[];
|
||||
/** state is the provisioner state (if any) */
|
||||
state: Uint8Array;
|
||||
/**
|
||||
* User secrets to make available during plan. Not carried on ApplyRequest
|
||||
* because plan evaluates data.coder_secret references and bakes the
|
||||
* resolved values into plan state, so apply does not need the raw secrets.
|
||||
* Provisioner-specific handling is documented on the UserSecretValue message.
|
||||
*/
|
||||
userSecrets: UserSecretValue[];
|
||||
}
|
||||
|
||||
/** PlanComplete indicates a request to plan completed. */
|
||||
@@ -1479,6 +1515,21 @@ export const InitComplete = {
|
||||
},
|
||||
};
|
||||
|
||||
export const UserSecretValue = {
|
||||
encode(message: UserSecretValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
||||
if (message.envName !== "") {
|
||||
writer.uint32(10).string(message.envName);
|
||||
}
|
||||
if (message.filePath !== "") {
|
||||
writer.uint32(18).string(message.filePath);
|
||||
}
|
||||
if (message.value.length !== 0) {
|
||||
writer.uint32(26).bytes(message.value);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
};
|
||||
|
||||
export const PlanRequest = {
|
||||
encode(message: PlanRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
||||
if (message.metadata !== undefined) {
|
||||
@@ -1499,6 +1550,9 @@ export const PlanRequest = {
|
||||
if (message.state.length !== 0) {
|
||||
writer.uint32(50).bytes(message.state);
|
||||
}
|
||||
for (const v of message.userSecrets) {
|
||||
UserSecretValue.encode(v!, writer.uint32(58).fork()).ldelim();
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user