Skip to content

Commit 9ce7e6d

Browse files
potiukclaude
andcommitted
Add draft project security threat-model document
Adds a draft project-level security threat-model document (draft-THREAT-MODEL.md) at repo root, improving discoverability for automated security scanners running against this repository. The file follows the rubric format used by several other ASF projects piloting security-model discoverability. The "draft-" prefix signals this is a proposal for the PMC to review, correct, or reject — not a finalised maintainer-blessed model. Every claim carries a provenance tag (documented / inferred / maintainer) so reviewers can see where each claim originates; §14 collects open questions for the maintainers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f19bffc commit 9ce7e6d

1 file changed

Lines changed: 301 additions & 0 deletions

File tree

draft-THREAT-MODEL.md

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
# Apache CloudStack Terraform Provider Security Threat Model — delta (draft)
21+
22+
> **Delta document.** Inherits §3, §4 B1, and §7 from
23+
> `cloudstack-threat-model-draft.md`. Read the main model first.
24+
25+
## §1 Header
26+
27+
- **Project:** Apache CloudStack Terraform Provider
28+
(`apache/cloudstack-terraform-provider`) — Terraform provider that
29+
drives the CloudStack JSON API as a Hashicorp Terraform plugin.
30+
- **Commit:** `f19bffc` (HEAD of `main` at draft time).
31+
- **Date:** 2026-05-29.
32+
- **Authors:** ASF Security team draft.
33+
- **Status:** Draft delta over `cloudstack-threat-model-draft.md`.
34+
- **Version binding:** as of the commit above. Provider released at
35+
`v0.5.x` series *(documented: `README.md` example)*.
36+
- **Reporting:** as in the main model.
37+
- **Provenance legend:** as in the main model.
38+
- **Draft confidence:** 8 documented / 0 maintainer / 10 inferred.
39+
40+
**About the project.** A Terraform provider plugin written in Go,
41+
built on top of `apache/cloudstack-go`. Each Terraform resource
42+
(`cloudstack_instance`, `cloudstack_network`, `cloudstack_template`,
43+
…) and data source maps to one or more CloudStack API calls. Runs
44+
inside the Terraform plugin host as a separate process invoked by
45+
the `terraform` binary over the Terraform plugin RPC protocol
46+
*(documented: `README.md`, Terraform plugin architecture)*.
47+
48+
## §2 Scope and intended use
49+
50+
**Primary intended use.** *(documented — README)* Operators describe
51+
their CloudStack-managed infrastructure as Terraform resources, then
52+
`terraform apply` drives the API to converge. Used for IaC-style
53+
infrastructure provisioning.
54+
55+
**Deployment shape.** A short-lived Go process launched by the
56+
Terraform binary on the operator's workstation or a CI runner. No
57+
long-running daemon, no listener.
58+
59+
**Caller expectations.** The caller (the operator / CI) is trusted to:
60+
61+
- supply provider config (`api_url`, `api_key`, `secret_key`,
62+
`http_get_only`, `timeout`) via either the provider block or env
63+
vars,
64+
- not source provider config from end-user input,
65+
- store the Terraform state file (which may contain resource IDs and
66+
some sensitive metadata) per Terraform's own best practices.
67+
68+
**Component-family table.**
69+
70+
| Family | Representative entry | Touches outside the process? | In this delta? |
71+
| --- | --- | --- | --- |
72+
| Provider config / client (`cloudstack/config.go`) | `Config.NewClient()` *(documented)* | **yes — network + creds via cloudstack-go** | yes |
73+
| Resource implementations (`cloudstack/resource_cloudstack_*.go`, ~80 resources) | CRUD lifecycles | inherited from client | yes |
74+
| Data sources (`cloudstack/data_source_cloudstack_*.go`, ~30 data sources) | read-only lifecycles | inherited from client | yes |
75+
| `main.go` (provider entry) | Terraform plugin handshake | Terraform RPC | yes |
76+
| `website/`, `scripts/`, `CHANGELOG.md` | docs + release tooling | n/a | **out of model** *(§3)* |
77+
| Tests under `cloudstack/*_test.go` | acceptance tests | network | **out of model** *(§3)* |
78+
| `vendor/` (if vendored) | upstream Go deps | n/a | **out of model** *(§3)* |
79+
80+
## §3 Out of scope (explicit non-goals)
81+
82+
The main model's §3 applies. **Additional** out-of-scope items
83+
specific to the Terraform provider:
84+
85+
1. **Terraform state file confidentiality and storage.** Where the
86+
operator stores Terraform state (local, S3, Terraform Cloud, …)
87+
and what protections are applied are operator decisions out of
88+
model. *(inferred — Q1)*
89+
2. **Server-side correctness of management-server responses.** Same
90+
as the Go SDK delta.
91+
3. **Terraform plugin RPC trust.** The provider trusts the `terraform`
92+
binary that invoked it.
93+
4. **`vendor/`, `website/`, `scripts/`, tests.**
94+
5. **The four sibling repos.**
95+
6. **Terraform itself.** Bugs in HashiCorp Terraform are upstream.
96+
7. **HCL configuration trust.** A malicious `*.tf` file is `OUT-OF-MODEL:
97+
trusted-input` — operator's responsibility.
98+
99+
## §4 Trust boundaries and data flow
100+
101+
The boundary is **the Terraform plugin handshake** plus the
102+
provider-block config. Provider config (`api_url`, `api_key`,
103+
`secret_key`, `http_get_only`) flows from the operator's environment
104+
into `Config.NewClient()` *(documented: `cloudstack/config.go`)*. The
105+
client then makes B1-shape API calls; see the Go SDK delta for the
106+
signing flow.
107+
108+
Crucially, *(documented: `cloudstack/config.go` line ~36)*:
109+
110+
```go
111+
cs := cloudstack.NewAsyncClient(c.APIURL, c.APIKey, c.SecretKey, false)
112+
cs.HTTPGETOnly = c.HTTPGETOnly
113+
```
114+
115+
The fourth `NewAsyncClient` argument is **hardcoded to `false`**
116+
meaning **`InsecureSkipVerify: true` is unconditionally set** in the
117+
TLS config of every Terraform-provider client *(documented: see the Go
118+
SDK `cloudstack.go` line ~216)*. That is the single most security-
119+
relevant fact in this delta and is the centerpiece of §14 Q2 below.
120+
The provider does **not** appear to expose a `verify_ssl` provider
121+
attribute to the operator *(inferred — needs maintainer confirmation
122+
via Q2)*.
123+
124+
## §5 Assumptions about the environment
125+
126+
- **Host**: Linux/macOS/Windows operator workstation or CI runner;
127+
Terraform 1.0+ *(documented: `README.md`)*.
128+
- **Go**: 1.20+ *(documented: `README.md`)*.
129+
- **Filesystem**: Terraform state file under operator-chosen backend.
130+
- **Network**: outbound HTTPS to `api_url`.
131+
- **What the provider does not do**: no listener, no daemon, no
132+
global state mutation outside Terraform plugin handshake
133+
*(inferred — Q3)*.
134+
135+
## §5a Build-time and configuration variants
136+
137+
| Knob | Default | Stance | Effect |
138+
| --- | --- | --- | --- |
139+
| `api_url` | none | operator config | endpoint |
140+
| `api_key`, `secret_key` | none | operator config | credentials |
141+
| `http_get_only` | per provider block | when `true`, signatures land in URL — see Go SDK delta Q3 | transport choice |
142+
| `timeout` | provider default *(inferred — Q4)* | bounds async polling | |
143+
| TLS verification | **hardcoded off (`verifyssl=false`)** *(documented: `cloudstack/config.go` line ~36)* | **maintainer ruling required** *(inferred — Q2)* | **all HTTPS calls skip cert verification** |
144+
145+
## §6 Assumptions about inputs
146+
147+
| Entry point | Parameter | Attacker-controllable in the model? | Caller must enforce |
148+
| --- | --- | --- | --- |
149+
| Provider block | `api_url`, `api_key`, `secret_key`, `http_get_only`, `timeout` | **no** — operator / CI config | not from end-user input |
150+
| HCL resource definitions | resource attributes | **no** — IaC author | not from end-user input |
151+
| HTTP response | JSON body | trusted (B1) | typed-decoded |
152+
153+
## §7 Adversary model
154+
155+
Main-model §7 applies. **Adjustments specific to this provider**:
156+
157+
- "Unauthenticated network peer reaching `:8080`" is upstream.
158+
- **A network adversary between the operator / CI runner and the
159+
CloudStack management server can man-in-the-middle the API
160+
conversation undetected**, because TLS verification is disabled at
161+
the provider layer. Whether this is a `VALID` report (provider
162+
should change) or `BY-DESIGN: property-disclaimed` (provider
163+
intentionally delegates TLS to the operator's network) is Q2.
164+
165+
## §8 Security properties the provider provides
166+
167+
### T1 — HMAC-SHA1 signature via `cloudstack-go`
168+
169+
- **Property.** Each API call carries a valid HMAC-SHA1 signature per
170+
main-model §8 P1, via the Go SDK.
171+
- **Conditions / violation / severity.** As main model §8 P1.
172+
173+
### T2 — Terraform plugin handshake
174+
175+
- **Property.** The provider speaks only the Terraform plugin RPC
176+
protocol on stdin/stdout to its parent `terraform` process.
177+
- **Conditions.** Invoked by Terraform.
178+
- **Violation symptom.** Provider accepts commands from a non-Terraform
179+
caller.
180+
- **Severity.** Security-critical, `VALID`.
181+
182+
## §9 Security properties the provider does *not* provide
183+
184+
- **No TLS verification of the management-server cert** — TLS-verify
185+
is hardcoded off in `cloudstack/config.go` *(documented)*. This is
186+
the headline non-property and the single biggest §14 question.
187+
- **No protection of `secret_key` in Terraform state.** Provider-level
188+
secrets may end up in plan output and state.
189+
- **No re-validation of management-server response correctness.**
190+
- **No defence against malicious Terraform HCL.** A `*.tf` file with
191+
destructive resource definitions executes as written.
192+
193+
### False-friend properties
194+
195+
- **The provider running over HTTPS is not "TLS-protected" in the
196+
authentication sense** because cert verification is off. Any
197+
on-path attacker can MitM, present any cert, see all signed
198+
requests, and replay them within their `expires` window.
199+
200+
## §10 Downstream responsibilities
201+
202+
The operator / CI MUST:
203+
204+
1. Run the provider only over a network where the
205+
operator-to-management-server path is already trusted (private
206+
network, VPN, internal CI runner inside the CloudStack control
207+
plane), because the provider does not verify the management-server
208+
cert. *(pending Q2 — if the PMC adds a `verify_ssl` knob, this
209+
responsibility narrows.)*
210+
2. Treat the Terraform state file as sensitive — `secret_key` and
211+
resource secrets may live there.
212+
3. Source `api_key` / `secret_key` from a secret manager, not from
213+
the HCL file.
214+
4. Match provider version to the management-server version.
215+
5. Apply `http_get_only` only when URL logs cannot leak the signature.
216+
217+
## §11 Known misuse patterns
218+
219+
- Running the provider over an untrusted network (public Internet)
220+
expecting TLS verification.
221+
- Hardcoding `secret_key` in HCL or env vars committed to a repo.
222+
- Storing Terraform state in a backend without per-team ACLs.
223+
- Sharing one set of `api_key` / `secret_key` across multiple CI
224+
jobs without isolation.
225+
226+
## §11a Known non-findings (recurring false positives)
227+
228+
- **"HMAC-SHA1 — SHA1 is deprecated."**`KNOWN-NON-FINDING` per main
229+
model §11a.
230+
- **"`InsecureSkipVerify: true` is hardcoded in `Config.NewClient`."**
231+
This is the *actual provider behaviour* *(documented:
232+
`cloudstack/config.go` line ~36)*. Whether it is a `VALID` finding
233+
or `BY-DESIGN: property-disclaimed` is Q2. **Do not silently
234+
suppress.**
235+
- **"Secrets appear in Terraform state."** Out of model — Terraform
236+
state security is operator's job. → `OUT-OF-MODEL: trusted-input`.
237+
- **"Tests in `cloudstack/*_test.go` have weak input handling."**
238+
Out of model. → `OUT-OF-MODEL: unsupported-component`.
239+
240+
## §12 Conditions that would change this delta
241+
242+
- A `verify_ssl` (or `insecure_skip_verify`) provider attribute is
243+
added — would change Q2.
244+
- A switch in default to verifying TLS — would change §9 first
245+
bullet.
246+
- A new resource that introduces a new credential-bearing state shape
247+
(e.g. a `cloudstack_user_credentials` resource that persists secrets
248+
outside Terraform state).
249+
- Change in signing algorithm at the main-model layer.
250+
251+
## §13 Triage dispositions
252+
253+
Use the same table as the main model.
254+
255+
## §14 Open questions for the maintainers
256+
257+
**Q1.** Out of scope: Terraform state file storage and encryption.
258+
Confirm.
259+
260+
**Q2.** **Highest-leverage question in this delta.** The provider
261+
calls `cloudstack.NewAsyncClient(c.APIURL, c.APIKey, c.SecretKey,
262+
false)` in `cloudstack/config.go` line ~36 — hardcoding
263+
`verifyssl=false` and thus `InsecureSkipVerify: true` on every HTTP
264+
call.
265+
266+
- (a) Is this intentional (the provider assumes operators run only
267+
on a trusted control-plane network and explicitly opts out of TLS
268+
verification), and should the §9 first bullet stay?
269+
- (b) Or should the provider expose a `verify_ssl` attribute and
270+
default it to `true`, in which case the current hardcoding is a
271+
`VALID` finding to fix?
272+
273+
The text of §9 and §10 assumes (a) — please confirm or correct.
274+
*(maps to §5a, §9, §10, §11a)*
275+
276+
**Q3.** Confirm §5 negative side-effect inventory: no env-var
277+
consumption beyond Terraform plugin standard, no signal handlers, no
278+
global mutation.
279+
280+
**Q4.** Confirm `timeout` default for the cloudstack-go async client
281+
as used by the provider.
282+
283+
**Q5.** Where in the provider's docs is the credential-handling
284+
responsibility documented for HCL authors?
285+
286+
**Q6.** Does the provider redact `secret_key` from Terraform plan
287+
output? From `terraform show`?
288+
289+
**Q7.** Does the provider integrate with a Vault or other
290+
secret-manager pattern, or is the operator expected to wire that in
291+
externally?
292+
293+
**Q8.** Meta — should this delta live at `docs/threat-model.md` in
294+
`apache/cloudstack-terraform-provider`, or in the website tree?
295+
296+
**Q9.** When the main model's signing algorithm changes (e.g. v3 →
297+
v4, SHA1 → SHA256), what is the release-cadence commitment to update
298+
the provider's vendored `cloudstack-go`?
299+
300+
**Q10.** Confirm the unsupported-component list (tests, website,
301+
scripts, vendor).

0 commit comments

Comments
 (0)