Skip to content

Commit 4170bca

Browse files
wata727Kaniska244abdurriq
authored
terraform: Add support for GitHub Attestations in TFLint installation (#1589)
* terraform: Add support for GitHub Attestations in TFLint installation * terraform: Pin TFLint version in tflint_fallback_test * terraform: Bump version to 1.4.3 * terraform: Update tflint_fallback_test for the latest version --------- Co-authored-by: Kaniska <kaniska244@github.com> Co-authored-by: Abdurrahmaan Iqbal <abdurriq@github.com>
1 parent 99a3f1c commit 4170bca

4 files changed

Lines changed: 114 additions & 52 deletions

File tree

src/terraform/devcontainer-feature.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"id": "terraform",
3-
"version": "1.4.4",
3+
"version": "1.4.5",
44
"name": "Terraform, tflint, and TFGrunt",
55
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/terraform",
66
"description": "Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects latest version and installs needed dependencies.",
@@ -79,6 +79,11 @@
7979
}
8080
}
8181
},
82+
"dependsOn": {
83+
"ghcr.io/devcontainers/features/github-cli:1": {
84+
"version": "latest"
85+
}
86+
},
8287
"installsAfter": [
8388
"ghcr.io/devcontainers/features/common-utils"
8489
]

src/terraform/install.sh

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,23 @@ install_tflint() {
460460
curl -sSL -o /tmp/tf-downloads/${TFLINT_FILENAME} https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/${TFLINT_FILENAME}
461461
}
462462

463+
verify_tflint_attestations() {
464+
local checksums=$1
465+
local checksums_sha256=$(sha256sum "$checksums" | cut -d " " -f 1)
466+
467+
check_packages jq
468+
469+
curl -L -f "https://api.github.com/repos/terraform-linters/tflint/attestations/sha256:${checksums_sha256}" > attestation.json
470+
curl_exit_code=$?
471+
if [ $curl_exit_code -ne 0 ]; then
472+
echo "(*) Failed to fetch GitHub Attestations for tflint checksums"
473+
return 1
474+
fi
475+
476+
jq ".attestations[].bundle" attestation.json > bundle.jsonl
477+
gh at verify "$checksums" -R terraform-linters/tflint -b bundle.jsonl
478+
}
479+
463480
if [ "${TFLINT_VERSION}" != "none" ]; then
464481
echo "Downloading tflint..."
465482
TFLINT_FILENAME="tflint_linux_${architecture}.zip"
@@ -475,31 +492,44 @@ if [ "${TFLINT_VERSION}" != "none" ]; then
475492
else
476493
curl -sSL -o tflint_checksums.txt https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt
477494

495+
# Attempt GitHub Attestation verification (0.51.1+)
478496
set +e
479-
curl -sSL -o checksums.txt.keyless.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.keyless.sig
497+
verify_tflint_attestations tflint_checksums.txt
498+
verify_result=$?
480499
set -e
481500

482-
# Check that checksums.txt.keyless.sig exists and is not empty
483-
if [ -s checksums.txt.keyless.sig ]; then
484-
# Validate checksums with cosign
485-
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
486-
ensure_cosign
487-
cosign verify-blob \
488-
--certificate=/tmp/tf-downloads/checksums.txt.pem \
489-
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
490-
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
491-
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
492-
/tmp/tf-downloads/tflint_checksums.txt
493-
# Ensure that checksums.txt has $TFLINT_FILENAME
494-
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
495-
# Validate downloaded file
501+
if [ $verify_result -eq 0 ]; then
496502
sha256sum --ignore-missing -c tflint_checksums.txt
503+
echo "(*) tflint_checksums.txt verified successfully using GitHub Attestation."
497504
else
498-
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
499-
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
500-
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
501-
gpg -q --import tflint_key
502-
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
505+
# Fallback to cosign verification
506+
echo "(*) GitHub Attestation verification failed or not supported for this version, falling back to Cosign verification..."
507+
set +e
508+
curl -sSL -o checksums.txt.keyless.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.keyless.sig
509+
set -e
510+
511+
# Check that checksums.txt.keyless.sig exists and is not empty
512+
if [ -s checksums.txt.keyless.sig ]; then
513+
# Validate checksums with cosign
514+
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
515+
ensure_cosign
516+
cosign verify-blob \
517+
--certificate=/tmp/tf-downloads/checksums.txt.pem \
518+
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
519+
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
520+
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
521+
/tmp/tf-downloads/tflint_checksums.txt
522+
# Ensure that checksums.txt has $TFLINT_FILENAME
523+
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
524+
# Validate downloaded file
525+
sha256sum --ignore-missing -c tflint_checksums.txt
526+
else
527+
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
528+
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
529+
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
530+
gpg -q --import tflint_key
531+
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
532+
fi
503533
fi
504534
fi
505535
fi

test/terraform/scenarios.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"installSentinel": true
1515
}
1616
}
17-
},
17+
},
1818
"install_in_ubuntu_noble": {
1919
"image": "mcr.microsoft.com/devcontainers/base:noble",
2020
"features": {
@@ -138,4 +138,4 @@
138138
}
139139
}
140140
}
141-
}
141+
}

test/terraform/tflint_fallback_test.sh

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ keyserver hkps://keys.openpgp.org
2222
keyserver hkps://keyserver.pgp.com"
2323

2424
check "tflint version as installed by feature" tflint --version
25-
check "cosign version as installed by feature" cosign version
2625

2726
architecture="$(uname -m)"
2827
case ${architecture} in
@@ -221,14 +220,31 @@ install_tflint() {
221220
curl -sSL -o /tmp/tf-downloads/${TFLINT_FILENAME} https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/${TFLINT_FILENAME}
222221
}
223222

223+
verify_tflint_attestations() {
224+
local checksums=$1
225+
local checksums_sha256=$(sha256sum "$checksums" | cut -d " " -f 1)
224226

225-
try_install_dummy_tflint_cosign_version() {
227+
check_packages jq
228+
229+
curl -L -f "https://api.github.com/repos/terraform-linters/tflint/attestations/sha256:${checksums_sha256}" > attestation.json
230+
curl_exit_code=$?
231+
if [ $curl_exit_code -ne 0 ]; then
232+
echo "(*) Failed to fetch GitHub Attestations for tflint checksums"
233+
return 1
234+
fi
235+
236+
jq ".attestations[].bundle" attestation.json > bundle.jsonl
237+
gh at verify "$checksums" -R terraform-linters/tflint -b bundle.jsonl
238+
}
239+
240+
241+
try_install_dummy_tflint_version() {
226242
mode=$1
227243
tflint_url='https://github.com/terraform-linters/tflint'
228244
mkdir -p /tmp/tf-downloads
229245
cd /tmp/tf-downloads
230246
echo -e "\nTrying to install dummy tflint version..."
231-
TFLINT_VERSION="0.50.XYZ"
247+
TFLINT_VERSION="0.60.XYZ"
232248
echo "Downloading tflint...v${TFLINT_VERSION}"
233249
TFLINT_FILENAME="tflint_linux_${architecture}.zip"
234250
install_tflint "$TFLINT_VERSION"
@@ -237,37 +253,50 @@ try_install_dummy_tflint_cosign_version() {
237253
fi
238254
if [ "${TFLINT_SHA256}" != "dev-mode" ]; then
239255

240-
if [ "${TFLINT_SHA256}" != "automatic" ]; then
256+
if [ "${TFLINT_SHA256}" != "automatic" ]; then
241257
echo "${TFLINT_SHA256} *${TFLINT_FILENAME}" > tflint_checksums.txt
242258
sha256sum --ignore-missing -c tflint_checksums.txt
243259
else
244260
curl -sSL -o tflint_checksums.txt https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt
245261

262+
# Attempt GitHub Attestation verification (0.51.1+)
246263
set +e
247-
curl -sSL -o checksums.txt.keyless.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.keyless.sig
264+
verify_tflint_attestations tflint_checksums.txt
265+
verify_result=$?
248266
set -e
249-
250-
# Check that checksums.txt.keyless.sig exists and is not empty
251-
if [ -s checksums.txt.keyless.sig ]; then
252-
# Validate checksums with cosign
253-
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
254-
ensure_cosign $mode
255-
cosign verify-blob \
256-
--certificate=/tmp/tf-downloads/checksums.txt.pem \
257-
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
258-
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
259-
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
260-
/tmp/tf-downloads/tflint_checksums.txt
261-
# Ensure that checksums.txt has $TFLINT_FILENAME
262-
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
263-
# Validate downloaded file
267+
268+
if [ $verify_result -eq 0 ]; then
264269
sha256sum --ignore-missing -c tflint_checksums.txt
270+
echo "(*) tflint_checksums.txt verified successfully using GitHub Attestation."
265271
else
266-
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
267-
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
268-
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
269-
gpg -q --import tflint_key
270-
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
272+
# Fallback to cosign verification
273+
echo "(*) GitHub Attestation verification failed or not supported for this version, falling back to Cosign verification..."
274+
set +e
275+
curl -sSL -o checksums.txt.keyless.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.keyless.sig
276+
set -e
277+
278+
# Check that checksums.txt.keyless.sig exists and is not empty
279+
if [ -s checksums.txt.keyless.sig ]; then
280+
# Validate checksums with cosign
281+
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
282+
ensure_cosign $mode
283+
cosign verify-blob \
284+
--certificate=/tmp/tf-downloads/checksums.txt.pem \
285+
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
286+
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
287+
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
288+
/tmp/tf-downloads/tflint_checksums.txt
289+
# Ensure that checksums.txt has $TFLINT_FILENAME
290+
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
291+
# Validate downloaded file
292+
sha256sum --ignore-missing -c tflint_checksums.txt
293+
else
294+
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
295+
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
296+
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
297+
gpg -q --import tflint_key
298+
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
299+
fi
271300
fi
272301
fi
273302
fi
@@ -276,12 +305,10 @@ try_install_dummy_tflint_cosign_version() {
276305
sudo mv -f tflint /usr/local/bin/
277306
}
278307

279-
try_install_dummy_tflint_cosign_version "mode1"
308+
try_install_dummy_tflint_version "mode1"
280309

281310
check "tflint version as installed when mode=1" tflint --version
282-
check "cosign version as installed when mode=1" cosign version
283311

284-
try_install_dummy_tflint_cosign_version "mode2"
312+
try_install_dummy_tflint_version "mode2"
285313

286314
check "tflint version as installed when mode=2" tflint --version
287-
check "cosign version as installed when mode=2" cosign version

0 commit comments

Comments
 (0)