Initializing Enclave...

Fixing Terraform 'Module Source Not Found' for Private GitHub Registry Access Denied Errors

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins

TL;DR

  • What broke: Terraform cannot pull a module from a private GitHub repository because the GitHub token is missing, has insufficient scopes (repo required), or the .terraformrc credentials block is misconfigured.
  • How to fix it: Set a valid GITHUB_TOKEN with repo scope and wire it into .terraformrc as a credentials block for github.com, or switch to SSH-based module sourcing.
  • Use the Client-Side Sandbox above to paste your module {} block and .terraformrc — it will auto-refactor the authentication config without sending your token anywhere.

The Incident (What Does the Error Mean?)

Raw error output from terraform init:

│ Error: Module source not found
│
│   on main.tf line 12, in module "vpc":
│   12:   source = "github.com/your-org/terraform-modules//vpc"
│
│ Could not download module "vpc" (main.tf:12) source code from
│ "https://github.com/your-org/terraform-modules//vpc":
│ error downloading 'https://github.com/your-org/terraform-modules//vpc':
│ /usr/bin/git exited with 128: Cloning into '...'...
│ remote: Repository not found.
│ fatal: repository 'https://github.com/your-org/terraform-modules/' not found
│ (or access denied)

Immediate consequence: terraform init hard-fails. No plan, no apply, no pipeline. Every downstream job — PR validation, staging deploys, production promotions — is blocked until credentials are resolved.


The Attack Vector / Blast Radius

This isn't just a broken build. The failure mode exposes two compounding risks:

  1. Credential sprawl via workarounds. Engineers under pressure hardcode a PAT directly into the source URL (https://token:[email protected]/...). That token now lives in .terraform.lock.hcl, state files, CI logs, and potentially remote state backends — all readable by anyone with S3/GCS read access.

  2. Overly-scoped tokens. To "just make it work," teams generate a classic PAT with repo + admin:org + workflow scopes instead of a fine-grained PAT scoped to a single repository. A leaked token of that scope allows an attacker to exfiltrate all private repos, modify Actions workflows, and pivot into CI/CD secrets.

Blast radius: Private source code exfiltration → secrets harvesting from CI workflows → lateral movement into cloud environments via OIDC or long-lived keys stored in Actions secrets.


How to Fix It (The Solution)

Basic Fix — Environment Variable + .terraformrc

Terraform's Git-based module fetcher respects a credentials block in .terraformrc. Wire your GitHub token in without ever touching the source URL.

Step 1: Generate a fine-grained GitHub PAT with Contents: Read-only on the specific module repository only.

Step 2: Configure .terraformrc

- # No credentials block — Terraform falls back to unauthenticated git clone
+ credentials "github.com" {
+   token = "ghp_YourFineGrainedTokenHere"
+ }

Step 3: Verify your module source format

- source = "https://github.com/your-org/terraform-modules//vpc"
+ source = "github.com/your-org/terraform-modules//vpc?ref=v1.4.0"

Note: Drop the https:// prefix. Terraform's module registry protocol handles the scheme. Always pin a ref.


Enterprise Best Practice — SSH Key Auth + Fine-Grained PAT in CI

For CI/CD pipelines (GitHub Actions, GitLab CI, Atlantis), never store PATs in .terraformrc committed to the repo. Use one of the following patterns:

Pattern A: SSH Deploy Key (recommended for single-repo module sources)

- source = "github.com/your-org/terraform-modules//vpc?ref=v1.4.0"
+ source = "[email protected]:your-org/terraform-modules.git//vpc?ref=v1.4.0"

Add a read-only SSH deploy key to the module repo. Mount the private key in CI via a secret and configure ~/.ssh/config:

+ Host github.com
+   HostName github.com
+   User git
+   IdentityFile ~/.ssh/terraform_deploy_key
+   StrictHostKeyChecking no

Pattern B: GitHub Actions — Token injected at runtime via env

- # Hardcoded token in .terraformrc checked into repo
- credentials "github.com" {
-   token = "ghp_hardcodedBADPractice"
- }

+ # .terraformrc uses env var interpolation (Terraform 1.1+)
+ credentials "github.com" {
+   token = "$GITHUB_TOKEN"
+ }

In your GitHub Actions workflow:

+ - name: Terraform Init
+   env:
+     GITHUB_TOKEN: ${{ secrets.TF_MODULE_GITHUB_TOKEN }}
+   run: terraform init

The secret TF_MODULE_GITHUB_TOKEN is a fine-grained PAT scoped only to the module repository with Contents: Read-only. Zero other permissions.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Checkov — Detect hardcoded tokens in Terraform files

Add to your pre-commit or CI pipeline:

checkov -d . --check CKV_SECRET_6

Checkov's secrets scanning will flag any ghp_, github_pat_, or bearer token patterns in .tf and .terraformrc files.

2. OPA/Conftest policy — Enforce ref pinning on all module sources

package terraform.modules

deny[msg] {
  module := input.configuration.root_module.module_calls[_]
  not contains(module.source, "?ref=")
  msg := sprintf("Module '%v' must pin a ref tag. Unpinned modules are a supply chain risk.", [module.source])
}

Run with:

conftest test plan.json --policy ./policies/

3. tflint — Validate module source format before terraform init runs

tflint --enable-rule=terraform_module_pinned_source

4. Rotate and audit PATs quarterly. Use GitHub's GET /orgs/{org}/credential-authorizations API to enumerate all active tokens and their last-used timestamps. Revoke anything idle for 30+ days.

5. Prefer Terraform Private Registry (Terraform Cloud/Enterprise) over raw GitHub sources for shared modules. The registry enforces versioning, access control, and audit logs natively — removing the GitHub auth problem entirely.

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →