Skip to content

Commit 2592c9f

Browse files
authored
chore(terraform): terraform security hardening (#993)
Two small, independent hardening changes to the reusable `terraform.yml` workflow. ## 1. Stop leaking `ENCRYPTION_PASSWORD` to every step Removed `ENCRYPTION_PASSWORD` from the workflow-level `env:` block and scoped it to only the two steps that actually need it (`Create tarball` in the plan job, `Extract tarball` in the apply job). Secrets passed via `secrets:` to a reusable workflow are auto-masked by GitHub. Promoting them to a workflow-level `env:` widens exposure to every step (including any third-party actions added later) with no benefit. The secret's description was updated to note this. ## 2. Make plan-tarball retention configurable (default 5 days, was 35) Added a new `artifact_retention_days` input (number, default `5`) and use it for `retention-days` on the `Upload artifact` step. The artifact contains the full Terraform configuration and plan, encrypted with `ENCRYPTION_PASSWORD`. It is only consumed by the Apply job in the same workflow run. 35 days of retention is unnecessary exposure window if the password ever leaks. Making it an input lets consumers tune the value to their deployment cadence (e.g. workflows that span multiple environments over several days can opt for a higher value). This is an additive input change — existing consumers get the new 5-day default automatically without any code changes. --- The lock-semantics change originally proposed here has been split out into #997.
1 parent 3964f88 commit 2592c9f

2 files changed

Lines changed: 17 additions & 7 deletions

File tree

.github/workflows/terraform.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ on:
4949
type: string
5050
required: false
5151

52+
artifact_retention_days:
53+
description: Number of days to retain the uploaded plan artifact. The artifact contains the encrypted Terraform configuration and plan, and is only consumed by the Apply job in the same workflow run. Keep this low to limit the exposure window if `ENCRYPTION_PASSWORD` ever leaks.
54+
type: number
55+
required: false
56+
default: 5
57+
5258
run_terraform_plan:
5359
description: Run `terraform plan`? Defaults to `false` if the workflow was triggered by Dependabot, else `true`.
5460
type: boolean
@@ -79,7 +85,7 @@ on:
7985
required: false
8086

8187
ENCRYPTION_PASSWORD:
82-
description: A password used to encrypt the archive containing the Terraform configuration and plan file.
88+
description: A password used to encrypt the archive containing the Terraform configuration and plan file. Passed via `secrets:` so GitHub auto-masks it in all logs.
8389
required: true
8490

8591
SSH_PRIVATE_KEY:
@@ -114,7 +120,6 @@ env:
114120
PLAN_FILE: tfplan
115121
TARBALL: terraform.tar.gpg
116122
ARTIFACT_NAME: ${{ inputs.artifact_name || format('terraform-{0}', inputs.environment) }}
117-
ENCRYPTION_PASSWORD: ${{ secrets.ENCRYPTION_PASSWORD }}
118123

119124
jobs:
120125
terraform-plan:
@@ -246,6 +251,8 @@ jobs:
246251
id: tar
247252
# Only run if Terraform Plan succeeded with non-empty diff (changes present).
248253
if: steps.plan.outputs.exitcode == 2
254+
env:
255+
ENCRYPTION_PASSWORD: ${{ secrets.ENCRYPTION_PASSWORD }}
249256
run: |
250257
tarball_path="$RUNNER_TEMP/$TARBALL"
251258
tar -c . | gpg -c --batch --passphrase "$ENCRYPTION_PASSWORD" -o "$tarball_path"
@@ -259,10 +266,7 @@ jobs:
259266
name: ${{ env.ARTIFACT_NAME }}
260267
path: ${{ steps.tar.outputs.tarball_path }}
261268
if-no-files-found: error
262-
# Automatically delete artifact after the workflow run time limit (35 days) to save storage space.
263-
# If a workflow reaches this limit, it will be cancelled and the artifact will no longer be needed.
264-
# Ref: https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits
265-
retention-days: 35
269+
retention-days: ${{ inputs.artifact_retention_days }}
266270

267271
- name: Save cache
268272
id: cache-save
@@ -358,6 +362,8 @@ jobs:
358362
terraform_wrapper: false
359363

360364
- name: Extract tarball
365+
env:
366+
ENCRYPTION_PASSWORD: ${{ secrets.ENCRYPTION_PASSWORD }}
361367
run: |
362368
gpg -d --batch --passphrase "$ENCRYPTION_PASSWORD" "$TARBALL" | tar -x
363369
rm "$TARBALL"

docs/workflows/terraform.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ The path, relative to the working directory, of a variable definitions file (`.t
103103

104104
The name of the artifact to upload. If not specified, an artifact name will be generated based on the environment name. Defaults to `terraform-<environment>`
105105

106+
### (*Optional*) `artifact_retention_days`
107+
108+
Number of days to retain the uploaded plan artifact. The artifact contains the encrypted Terraform configuration and plan, and is only consumed by the Apply job in the same workflow run. Keep this low to limit the exposure window if `ENCRYPTION_PASSWORD` ever leaks. Defaults to `5`.
109+
106110
### (*Optional*) `run_terraform_plan`
107111

108112
Run `terraform plan`? Defaults to `false` if the workflow was triggered by Dependabot, else `true`.
@@ -115,7 +119,7 @@ Run `terraform apply` for the saved plan file? Defaults to `true`.
115119

116120
### `ENCRYPTION_PASSWORD`
117121

118-
A password used to encrypt the archive containing the Terraform configuration and plan file.
122+
A password used to encrypt the archive containing the Terraform configuration and plan file. Passed via `secrets:` so GitHub auto-masks it in all logs.
119123

120124
### (*Optional*) `SSH_PRIVATE_KEY`
121125

0 commit comments

Comments
 (0)