github mattrobinsonsre/terrapod v1.7.3

4 hours ago

A security release. It fixes twenty-one advisories reported by A3C7 across two batches, and it is a drop-in upgrade for most deployments — but four changes alter what an existing, unchanged configuration does. Read Before you upgrade first.

Security

Unauthenticated access, now closed

  • GHSA-cq5h-4hqp-c62v/oauth/authorize and /auth/authorize accepted any redirect_uri, so a crafted link returned an authorization code minted for the victim. Validated at the state-storing chokepoint, so a third route cannot forget it.
  • GHSA-63m3-56rj-qqfh — the runner plan/apply result callbacks and the configuration-version upload required no credential at all. Now authenticated, including the state-version endpoints the report did not name.
  • GHSA-r9v9-24fv-jxm2 — plan logs, apply logs and the plan JSON were served to anyone who knew the run id, which appears in UI links, audit rows and PR comments. Logs now carry a signed, expiring capability in the URL path; the plan JSON takes an ordinary credential.
  • GHSA-fcvc-826r-72m6 — linking a registry module to a workspace was authorised on the module side only, letting any authenticated user force runs in a workspace they had no access to.

Gates that did not hold

  • GHSA-w67x-7rf5-w65g — a plan-only run could be applied, bypassing mandatory policy sets and enforced security scans with nothing but run:apply, recording no override.
  • GHSA-7859-pwwx-vx4f — a workspace variable named TP_API_URL or TP_AUTH_TOKEN overrode the runner's own environment. TP_* is now reserved.

Secrets reaching places they should not

  • GHSA-5mpc-79pv-6mq7 — the AI plan summariser sent values marked sensitive to the configured model endpoint. It now redacts before sending. Stored plans and logs are unchanged; redacting those breaks drift detection.
  • GHSA-3g53-5gw3-hh42 — the MCP server returned an unredacted plan under a readOnly annotation, and ten mutating tools shipped unannotated, so a host could call them — including a governance override — without asking.
  • GHSA-g2c3-vxf6-7jr8terrapod-publish packaged state files, tfvars, dotenv files and private keys into published module tarballs. Published versions are immutable.
  • GHSA-9646-883f-wjjm — six secret-bearing Terraform provider attributes were not marked Sensitive, so they landed in plaintext state and printed in plan output.
  • GHSA-wqfx-wf44-cgvj — a symlink in a migrated Atlantis repository escaped the source guard, uploading another project's Terraform state as this workspace's.
  • GHSA-5fh8-vj57-6gvhgo-terrapod: the platform bearer token survived a redirect that downgraded to http:// or moved host; http:// base URLs were accepted silently; eight path builders did not escape their parameters; responses were read unbounded; and server bytes were reflected verbatim into operator-facing errors.
  • GHSA-5x47-chx9-ww6c — the plan log was uploaded to Slack with signed storage URLs and userinfo credentials intact.

Key material

  • GHSA-hc47-q72v-4vcm — the key signing runner tokens, run-task callbacks, download tickets and Slack links defaulted to sha256(database_url). New installs now generate a random key. An existing deployment keeps its current derivation on purpose — rotating it would invalidate every token in flight, which is what running plans and applies authenticate with — and warns at startup.
  • GHSA-7rj9-qpm8-6mv3 — two EncryptedText columns were absent from ENCRYPTED_COLUMNS, so DEK rotation silently skipped them. This one carries an operator action — see below.

Hardening

  • GHSA-wq2j-pppw-ff2p — the login rate limit was twenty times its intended value, and X-Forwarded-For was believed from any source.
  • GHSA-46gw-rvrr-jqfx — the console's page routes set no X-Frame-Options or frame-ancestors, so its destructive controls could be framed.
  • GHSA-vg6r-fvfr-27v5 — the bundled web NetworkPolicy's ingress rule had no from: selector, so it admitted all sources when enabled.
  • GHSA-mjhj-g43q-w6m9 — four migrations destroyed security-relevant data with no guard.
  • GHSA-7qcr-cvvg-w58f — a variable value beginning tpenc: collided with the encryption marker and made its own row permanently unreadable.

GHSA-qx93-5mjv-78vq (cloud-IAM database TLS) is not fixed in this release. See Known issues.

Before you upgrade

1. If you have ever rotated a DEK, you are not done. gpg_keys.private_key and run_tasks.hmac_key were never re-keyed, so the old DEK still decrypts both. Upgrading does not fix that for you. Re-run encryption_migrate encrypt, and if you rotated in response to a suspected compromise, treat the GPG signing key and every run-task HMAC key as still exposed and rotate those secrets yourself.

2. If you run networkPolicies.enabled: true, set networkPolicies.web.ingressFrom — the chart now refuses to render without it, because the previous rule admitted every source while reading as a restriction.

3. Rate limiting now attributes clients differently. X-Forwarded-For is believed only from rate_limit.trusted_proxy_cidrs, which defaults to the private ranges plus CGNAT. This relies on your ingress sanitising that header; ingress-nginx and Traefik both do so by default. See the new rate limiting guide, especially if a CDN sits in front.

4. Applying a plan-only run now returns 409. Automation that relied on it will stop working — that is the policy-gate bypass being closed.

5. go-terrapod consumers: an http:// base URL is now a construction error (TERRAPOD_ALLOW_INSECURE_TRANSPORT=1 to opt back in), and the low-level Get/Post/Put/Delete require a rooted path.

6. Terraform provider: six attributes became Sensitive. A configuration referencing one in a non-sensitive output will now fail at plan time.

A run in flight during the upgrade holds a log URL minted before the change and sends no credential, so its CLI log stream ends with a 401. The run itself continues and completes normally; re-attaching gets a fresh URL.

Known issues

  • GHSA-qx93-5mjv-78vq — cloud-IAM database authentication defaults to ssl_mode: require, which encrypts without verifying the server while sending the IAM token as the password. Fixing the default can stop a working deployment connecting, so it ships in 2.0 rather than a patch. Mitigate today by setting api.config.database.ssl_mode: verify-full and pointing ssl_root_cert at your provider's CA bundle. Deployments using password or Kubernetes-secret authentication are unaffected.

Credit

All twenty-one were reported by A3C7. The reports were precise enough to fix from directly — file and line, a reproduction, and a suggested fix — and three turned out broader than filed once we followed the thread. They withdrew one finding of their own accord after re-testing it, which saved us chasing something that was not there, and two of their suggestions shipped as better than what we first built.

Status

Stable.

Full Changelog: v1.7.2...v1.7.3

Don't miss a new terrapod release

NewReleases is sending notifications on new releases.