Skip to content

[terraform] Add Custom Download Server Support for Terraform Dev Container Feature #1364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
31 changes: 31 additions & 0 deletions src/terraform/NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@

On August 10, 2023, HashiCorp announced a change of license for its products, including Terraform. After ~9 years of Terraform being open source under the MPL v2 license, it was to move under a non-open source BSL v1.1 license, starting from the next (1.6) version. See https://github.com/hashicorp/terraform/blob/main/LICENSE

## Custom Download Server

The `customDownloadServer` option allows you to specify an alternative server for downloading Terraform and Sentinel packages. This is useful for organizations that maintain internal mirrors or have proxies for HashiCorp downloads.

When using this option:
- Provide the complete URL including protocol (e.g., `https://my-mirror.example.com`)
- The server should mirror the HashiCorp releases structure

Example:
```json
"features": {
"ghcr.io/devcontainers/features/terraform:1": {
"customDownloadServer": "https://my-mirror.example.com"
}
}
```

### ⚠️ Security Considerations

When using a custom download server, be aware of the following security implications:

- **Server Verification**: Always verify that the custom server is trustworthy and maintained by your organization or a trusted entity. Using an untrusted or compromised server could lead to downloading malicious software.

- **Supply Chain Risks**: Malicious actors may attempt to distribute compromised versions of Terraform that contain backdoors, cryptominers, or other harmful code.

- **Integrity Checks**: The feature performs SHA256 checks when available, but these are only as trustworthy as the source of the checksums. If both the binaries and checksums come from a compromised server, the integrity check may pass despite the software being malicious.

- **Organizational Policy**: Ensure your custom download server adheres to your organization's security policies and implements proper access controls.

Always use the official HashiCorp download server (https://releases.hashicorp.com) unless you have a specific need for an alternative source.

## OS Support

This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
Expand Down
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.3.10",
"version": "1.4.0",
"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 @@ -54,6 +54,11 @@
"type": "string",
"default": "",
"description": "Connect to a keyserver using a proxy by configuring this option"
},
"customDownloadServer": {
"type": "string",
"default": "",
"description": "Custom server URL for downloading Terraform and Sentinel packages, including protocol (e.g., https://releases.hashicorp.com). If not provided, the default HashiCorp download server (https://releases.hashicorp.com) will be used."
}
},
"customizations": {
Expand Down
14 changes: 10 additions & 4 deletions src/terraform/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ TERRAGRUNT_VERSION="${TERRAGRUNT:-"latest"}"
INSTALL_SENTINEL=${INSTALLSENTINEL:-false}
INSTALL_TFSEC=${INSTALLTFSEC:-false}
INSTALL_TERRAFORM_DOCS=${INSTALLTERRAFORMDOCS:-false}
CUSTOM_DOWNLOAD_SERVER="${CUSTOMDOWNLOADSERVER:-""}"

TERRAFORM_SHA256="${TERRAFORM_SHA256:-"automatic"}"
TFLINT_SHA256="${TFLINT_SHA256:-"automatic"}"
Expand All @@ -26,6 +27,11 @@ SENTINEL_SHA256="${SENTINEL_SHA256:-"automatic"}"
TFSEC_SHA256="${TFSEC_SHA256:-"automatic"}"
TERRAFORM_DOCS_SHA256="${TERRAFORM_DOCS_SHA256:-"automatic"}"

HASHICORP_RELEASES_URL="https://releases.hashicorp.com"
if [ -n "${CUSTOM_DOWNLOAD_SERVER}" ]; then
HASHICORP_RELEASES_URL="${CUSTOM_DOWNLOAD_SERVER}"
fi

TERRAFORM_GPG_KEY="72D7468F"
TFLINT_GPG_KEY_URI="https://raw.githubusercontent.com/terraform-linters/tflint/v0.46.1/8CE69160EB3F2FE9.key"
KEYSERVER_PROXY="${HTTPPROXY:-"${HTTP_PROXY:-""}"}"
Expand Down Expand Up @@ -357,7 +363,7 @@ find_version_from_git_tags TERRAGRUNT_VERSION "$terragrunt_url"
install_terraform() {
local TERRAFORM_VERSION=$1
terraform_filename="terraform_${TERRAFORM_VERSION}_linux_${architecture}.zip"
curl -sSL -o ${terraform_filename} "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/${terraform_filename}"
curl -sSL -o ${terraform_filename} "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/${terraform_filename}"
}

mkdir -p /tmp/tf-downloads
Expand All @@ -373,8 +379,8 @@ fi
if [ "${TERRAFORM_SHA256}" != "dev-mode" ]; then
if [ "${TERRAFORM_SHA256}" = "automatic" ]; then
receive_gpg_keys TERRAFORM_GPG_KEY
curl -sSL -o terraform_SHA256SUMS https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS
curl -sSL -o terraform_SHA256SUMS.sig https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig
curl -sSL -o terraform_SHA256SUMS "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS"
curl -sSL -o terraform_SHA256SUMS.sig "${HASHICORP_RELEASES_URL}/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig"
gpg --verify terraform_SHA256SUMS.sig terraform_SHA256SUMS
else
echo "${TERRAFORM_SHA256} *${terraform_filename}" > terraform_SHA256SUMS
Expand Down Expand Up @@ -464,7 +470,7 @@ fi

if [ "${INSTALL_SENTINEL}" = "true" ]; then
SENTINEL_VERSION="latest"
sentinel_releases_url='https://releases.hashicorp.com/sentinel'
sentinel_releases_url="${HASHICORP_RELEASES_URL}/sentinel"
find_sentinel_version_from_url SENTINEL_VERSION ${sentinel_releases_url}
sentinel_filename="sentinel_${SENTINEL_VERSION}_linux_${architecture}.zip"
echo "(*) Downloading Sentinel... ${sentinel_filename}"
Expand Down
13 changes: 13 additions & 0 deletions test/terraform/custom_download_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

set -e

# Import test library
source dev-container-features-test-lib

# Check if terraform was installed correctly and it's the expected version
check "terraform installed" terraform --version
check "terraform version matches" terraform --version | grep "1.6.5"

# Report results
reportResults
16 changes: 16 additions & 0 deletions test/terraform/custom_download_server_with_sentinel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -e

# Import test library
source dev-container-features-test-lib

# Check if terraform was installed correctly and it's the expected version
check "terraform installed" terraform --version
check "terraform version matches" terraform --version | grep "1.6.5"

# Check if sentinel was installed correctly
check "sentinel installed" sentinel --version

# Report results
reportResults
19 changes: 19 additions & 0 deletions test/terraform/scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,24 @@
"tflint": "0.40.0"
}
}
},
"custom_download_server": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"version": "1.6.5",
"customDownloadServer": "https://releases.hashicorp.com"
}
}
},
Comment on lines +73 to +82

Choose a reason for hiding this comment

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

Since the default is already "https://releases.hashicorp.com", this test makes the case that adding it does not break functionality. However, I would like to test that it does override - I can see from the code that it does, but still would be good to systematically test it. Would you be able to make an additional test with a fake URL and expect that it fails please?

Copy link
Author

@iTiPo iTiPo May 31, 2025

Choose a reason for hiding this comment

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

@AlvaroRausell could you tell me how I can check that the feature installation fails? I didn't find any examples of such checks in the repository?

"custom_download_server_with_sentinel": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"version": "1.6.5",
"installSentinel": true,
"customDownloadServer": "https://releases.hashicorp.com"
}
}
}
}