Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/terraform/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "terraform",
"version": "1.4.2",
"version": "1.4.3",
"name": "Terraform, tflint, and TFGrunt",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/terraform",
"description": "Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects latest version and installs needed dependencies.",
Expand Down Expand Up @@ -79,6 +79,11 @@
}
}
},
"dependsOn": {
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "latest"
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
Expand Down
70 changes: 50 additions & 20 deletions src/terraform/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,23 @@ install_tflint() {
curl -sSL -o /tmp/tf-downloads/${TFLINT_FILENAME} https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/${TFLINT_FILENAME}
}

verify_tflint_attestations() {
local checksums=$1
local checksums_sha256=$(sha256sum "$checksums" | cut -d " " -f 1)

check_packages jq

curl -L -f "https://api.github.com/repos/terraform-linters/tflint/attestations/sha256:${checksums_sha256}" > attestation.json
curl_exit_code=$?
if [ $curl_exit_code -ne 0 ]; then
echo "(*) Failed to fetch GitHub Attestations for tflint checksums"
return 1
fi

jq ".attestations[].bundle" attestation.json > bundle.jsonl
gh at verify "$checksums" -R terraform-linters/tflint -b bundle.jsonl
}

if [ "${TFLINT_VERSION}" != "none" ]; then
echo "Downloading tflint..."
TFLINT_FILENAME="tflint_linux_${architecture}.zip"
Expand All @@ -475,31 +492,44 @@ if [ "${TFLINT_VERSION}" != "none" ]; then
else
curl -sSL -o tflint_checksums.txt https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt

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

# Check that checksums.txt.keyless.sig exists and is not empty
if [ -s checksums.txt.keyless.sig ]; then
# Validate checksums with cosign
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
ensure_cosign
cosign verify-blob \
--certificate=/tmp/tf-downloads/checksums.txt.pem \
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
/tmp/tf-downloads/tflint_checksums.txt
# Ensure that checksums.txt has $TFLINT_FILENAME
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
# Validate downloaded file
if [ $verify_result -eq 0 ]; then
sha256sum --ignore-missing -c tflint_checksums.txt
echo "(*) tflint_checksums.txt verified successfully using GitHub Attestation."
else
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
gpg -q --import tflint_key
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
# Fallback to cosign verification
echo "(*) GitHub Attestation verification failed or not supported for this version, falling back to Cosign verification..."
set +e
curl -sSL -o checksums.txt.keyless.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.keyless.sig
set -e

# Check that checksums.txt.keyless.sig exists and is not empty
if [ -s checksums.txt.keyless.sig ]; then
# Validate checksums with cosign
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
ensure_cosign
cosign verify-blob \
--certificate=/tmp/tf-downloads/checksums.txt.pem \
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
/tmp/tf-downloads/tflint_checksums.txt
# Ensure that checksums.txt has $TFLINT_FILENAME
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
# Validate downloaded file
sha256sum --ignore-missing -c tflint_checksums.txt
else
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
gpg -q --import tflint_key
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
fi
fi
fi
fi
Expand Down
4 changes: 2 additions & 2 deletions test/terraform/scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"installSentinel": true
}
}
},
},
"install_in_ubuntu_noble": {
"image": "mcr.microsoft.com/devcontainers/base:noble",
"features": {
Expand Down Expand Up @@ -122,4 +122,4 @@
}
}
}
}
}
85 changes: 56 additions & 29 deletions test/terraform/tflint_fallback_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ keyserver hkps://keys.openpgp.org
keyserver hkps://keyserver.pgp.com"

check "tflint version as installed by feature" tflint --version
check "cosign version as installed by feature" cosign version

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

verify_tflint_attestations() {
local checksums=$1
local checksums_sha256=$(sha256sum "$checksums" | cut -d " " -f 1)

try_install_dummy_tflint_cosign_version() {
check_packages jq

curl -L -f "https://api.github.com/repos/terraform-linters/tflint/attestations/sha256:${checksums_sha256}" > attestation.json
curl_exit_code=$?
if [ $curl_exit_code -ne 0 ]; then
echo "(*) Failed to fetch GitHub Attestations for tflint checksums"
return 1
fi

jq ".attestations[].bundle" attestation.json > bundle.jsonl
gh at verify "$checksums" -R terraform-linters/tflint -b bundle.jsonl
}


try_install_dummy_tflint_version() {
mode=$1
tflint_url='https://github.com/terraform-linters/tflint'
mkdir -p /tmp/tf-downloads
cd /tmp/tf-downloads
echo -e "\nTrying to install dummy tflint version..."
TFLINT_VERSION="0.50.XYZ"
TFLINT_VERSION="0.60.XYZ"
echo "Downloading tflint...v${TFLINT_VERSION}"
TFLINT_FILENAME="tflint_linux_${architecture}.zip"
install_tflint "$TFLINT_VERSION"
Expand All @@ -237,37 +253,50 @@ try_install_dummy_tflint_cosign_version() {
fi
if [ "${TFLINT_SHA256}" != "dev-mode" ]; then

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

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

# Check that checksums.txt.keyless.sig exists and is not empty
if [ -s checksums.txt.keyless.sig ]; then
# Validate checksums with cosign
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
ensure_cosign $mode
cosign verify-blob \
--certificate=/tmp/tf-downloads/checksums.txt.pem \
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
/tmp/tf-downloads/tflint_checksums.txt
# Ensure that checksums.txt has $TFLINT_FILENAME
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
# Validate downloaded file

if [ $verify_result -eq 0 ]; then
sha256sum --ignore-missing -c tflint_checksums.txt
echo "(*) tflint_checksums.txt verified successfully using GitHub Attestation."
else
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
gpg -q --import tflint_key
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
# Fallback to cosign verification
echo "(*) GitHub Attestation verification failed or not supported for this version, falling back to Cosign verification..."
set +e
curl -sSL -o checksums.txt.keyless.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.keyless.sig
set -e

# Check that checksums.txt.keyless.sig exists and is not empty
if [ -s checksums.txt.keyless.sig ]; then
# Validate checksums with cosign
curl -sSL -o checksums.txt.pem https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.pem
ensure_cosign $mode
cosign verify-blob \
--certificate=/tmp/tf-downloads/checksums.txt.pem \
--signature=/tmp/tf-downloads/checksums.txt.keyless.sig \
--certificate-identity-regexp="^https://github.com/terraform-linters/tflint" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
/tmp/tf-downloads/tflint_checksums.txt
# Ensure that checksums.txt has $TFLINT_FILENAME
grep ${TFLINT_FILENAME} /tmp/tf-downloads/tflint_checksums.txt
# Validate downloaded file
sha256sum --ignore-missing -c tflint_checksums.txt
else
# Fallback to older, GPG-based verification (pre-0.47.0 of tflint)
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
gpg -q --import tflint_key
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
fi
fi
fi
fi
Expand All @@ -276,12 +305,10 @@ try_install_dummy_tflint_cosign_version() {
sudo mv -f tflint /usr/local/bin/
}

try_install_dummy_tflint_cosign_version "mode1"
try_install_dummy_tflint_version "mode1"

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

try_install_dummy_tflint_cosign_version "mode2"
try_install_dummy_tflint_version "mode2"

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