RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
Terraform Private Registry Access Fix
02. Root Cause Vector

Terraform cannot resolve a private GitHub registry module source because authentication credentials are missing, malformed, or scoped inc...

03. Rapid Prescription

Refresh remote state lock, ensure provider version constraints, and audit state drift.

Zero-Trust Client IDE/ Security

Terraform Private Registry Access Fix

Terraform cannot resolve a private GitHub registry module source because authentication credentials are missing, malformed, or scoped incorrectly.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(53L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
provider "aws" {
  region = "us-east-1"
  # TODO: Migrate to SSM for secrets, this is temporary
  access_key = "AKIAIOSFODNN7EXAMPLE12345"
  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}

# Legacy module for PCI zone, needs refactor
module "legacy_pci_api" {
  source = "git::https://github.com/my-org/private-terraform-modules.git//modules/pci-secure-zone?ref=v1.2.3"
  # VULNERABILITY: Private registry access misconfigured due to missing auth token in module source
  # This will fail because the module source uses a private GitHub repo without proper authentication
  # The error will be 'Module source not found: private github registry access denied'
  # The fix requires adding a token via git config or using a different source format
  environment = "prod"
  vpc_id      = "vpc-0a1b2c3d4e5f67890"
}

# Old commented-out attempt using SSH, but keys are expired
# module "legacy_pci_api" {
#   source = "git@github.com:my-org/private-terraform-modules.git//modules/pci-secure-zone"
# }

# Dummy RSA key for SSH backup (not used, but kept for "emergency")
variable "ssh_private_key" {
  default = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArXJ7wK1K... (truncated for brevity) ...\n-----END RSA PRIVATE KEY-----"
}

# Database URI embedded in a local file (simulating a config leak)
resource "local_file" "db_config" {
  filename = "/tmp/db_config.json"
  content = <<-EOT
    {
      "database_uri": "postgresql://user:SuperSecretPass123!@pci-db.internal:5432/legacy_db",
      "timeout": 30
    }
  EOT
}

# Stripe API key in an environment variable (bad practice, but common)
resource "null_resource" "setup_env" {
  provisioner "local-exec" {
    command = "export STRIPE_API_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc && echo 'Env set'"
  }
}

# Redundant provider block with different region (causes confusion)
provider "aws" {
  alias  = "backup"
  region = "eu-west-1"
  # TODO: Remove this after migration
}
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

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.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

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.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

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 = "git@github.com: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 infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

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.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does Terraform say 'repository not found' when the repo clearly exists on GitHub?
Terraform's Git client defaults to an unauthenticated clone. GitHub's security posture deliberately returns a generic 'not found' for private repositories when credentials are missing or invalid—it's a security feature to prevent attackers from enumerating your private assets. This means your IaC pipeline is failing because it's blindly knocking on a locked door. You need to establish a proper, authenticated handshake, which is a core competency we've weaponized at StackEngine.
Q2.Is it safe to put a GitHub token directly in .terraformrc?
Manually embedding a token in .terraformrc is a ticking time bomb. It requires perfect hygiene: the file must be in your home directory, excluded from all version control, and have its permissions locked down—a single misstep exposes your entire codebase. This manual process is brittle, error-prone, and creates a massive security vulnerability with a huge blast radius. Stop playing with fire. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that injects credentials securely at runtime, eliminating the risk of hardcoded secrets.
Q3.What GitHub token scopes does Terraform actually need to pull a private module?
Manually scoping tokens—whether a classic PAT with 'repo' or a fine-grained token with 'Contents: Read-only'—is a compliance nightmare at scale. In a large enterprise with mono-repos and hundreds of modules, tracking, rotating, and auditing these permissions manually is impossible. It creates shadow IT and governance black holes. For automated pull request generation with Maker-Checker review that enforces least-privilege across your entire organization, upgrade to the StackEngine CI/CD Automator (Team Edition). It automates policy-as-code for secret injection and access, turning a manual, risky process into a auditable, scalable control plane.