Skip to content

Commit b6579f6

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 358fe85 commit b6579f6

1 file changed

Lines changed: 303 additions & 0 deletions

File tree

draft-THREAT-MODEL.md

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
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 Go SDK Security Threat Model — delta (draft)
21+
22+
> **Delta document.** This document is a delta over the canonical
23+
> `apache/cloudstack` threat model. It inherits §3 (out-of-scope),
24+
> §4 B1 (API trust boundary), and §7 (adversary model) from the
25+
> main model. It restates only what `apache/cloudstack-go` uniquely
26+
> introduces. Read the main model first.
27+
28+
## §1 Header
29+
30+
- **Project:** Apache CloudStack Go SDK (`apache/cloudstack-go`) — Go
31+
client library that calls the `apache/cloudstack` JSON API.
32+
- **Commit:** `358fe85` (HEAD of `main` at draft time).
33+
- **Date:** 2026-05-29.
34+
- **Authors:** ASF Security team draft, awaiting CloudStack PMC review.
35+
- **Status:** Draft delta over `cloudstack-threat-model-draft.md`.
36+
- **Version binding:** as of the commit above. The SDK's release line is
37+
versioned independently of the management-server release line
38+
*(documented: `README.md` — "SDK releases tagged based on the ACS
39+
version")*.
40+
- **Reporting:** same as the main model — `security@apache.org`.
41+
- **Provenance legend:** as in the main model.
42+
- **Draft confidence:** 9 documented / 0 maintainer / 11 inferred.
43+
44+
**About the project.** `cloudstack-go` is a generated-then-curated Go
45+
library that wraps every CloudStack JSON API command in a typed
46+
parameter struct and a typed response *(documented: `README.md`,
47+
`generate/generate.go`)*. Its main moving parts: an HMAC-SHA1 signer
48+
+ HTTP client (`cloudstack/cloudstack.go`) and one `<Service>.go` file
49+
per API service group (Account, Address, VirtualMachine, …) generated
50+
from `generate/listApis.json` against the latest stable CloudStack
51+
release (currently v4.18.x baseline) *(documented: `README.md`)*.
52+
53+
## §2 Scope and intended use
54+
55+
**Primary intended use.** *(documented — README)* In-process import by
56+
a Go program that wants to drive a CloudStack management server. The
57+
embedding application instantiates a client with `NewClient(endpoint,
58+
apiKey, secretKey, verifySSL)` or `NewAsyncClient(…)`, then issues
59+
typed API calls.
60+
61+
**Deployment shape.** Always in-process; the SDK is a library, never
62+
a daemon.
63+
64+
**Caller expectations.** The caller is trusted to:
65+
66+
- choose the management-server endpoint URL,
67+
- supply a valid `apiKey` + `secretKey` pair,
68+
- pass `verifySSL = true` *unless* connecting to a known dev cluster
69+
using a self-signed CloudStack Root CA cert,
70+
- not source any of the above from end-user input.
71+
72+
**Component-family table.**
73+
74+
| Family | Representative entry | Touches outside the process? | In this delta? |
75+
| --- | --- | --- | --- |
76+
| Client + signer (`cloudstack/cloudstack.go`) | `NewAsyncClient`, signer in `cs.sign(params)` | **yes — network + creds** | yes |
77+
| Per-service generated wrappers (`cloudstack/<Service>.go`, ~600 services) | `cs.VirtualMachine.DeployVirtualMachine(…)` | inherited from client | yes |
78+
| Code generator (`generate/`) | reads `generate/listApis.json` and emits `cloudstack/*.go` | host filesystem at generation time | **out of model** *(§3)* — generator output is in-model, generator itself is build-time tooling |
79+
| `examples/` | `examples/AddHost.go`, `examples/CreateDeleteDomain.go`, `mock_test.go` | network | **out of model** *(§3)* |
80+
| `test/`, `ci/` | tests + CI scaffolding | varies | **out of model** *(§3)* |
81+
| `cloudstack/*_mock.go` | generated mocks | none | **out of model** *(§3)* |
82+
83+
## §3 Out of scope (explicit non-goals)
84+
85+
The main model's §3 applies in full. **Additional** out-of-scope items
86+
specific to the SDK:
87+
88+
1. **Storage of `apiKey` / `secretKey` on disk.** Where the caller
89+
chooses to persist credentials is out of model. *(inferred — Q1)*
90+
2. **Server-side correctness of the management server's response.** If
91+
the management server returns wrong data, the SDK forwards it; the
92+
SDK is not a re-verification layer. *(inferred — Q2)*
93+
3. **TLS transport configuration.** When the caller passes `verifySSL =
94+
false` (the fourth `NewAsyncClient` argument), the SDK explicitly
95+
sets `InsecureSkipVerify: true` *(documented:
96+
`cloudstack/cloudstack.go` line ~216
97+
`TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifyssl}`)*.
98+
That is a documented caller-supplied weakening; reports of "the SDK
99+
permits TLS verification to be disabled" are `OUT-OF-MODEL:
100+
trusted-input` because the caller chose it.
101+
4. **`examples/`, `cloudstack/*_mock.go`, `test/`, `ci/`.**
102+
5. **The four sibling repos**`apache/cloudstack`,
103+
`apache/cloudstack-cloudmonkey`, `apache/cloudstack-terraform-provider`,
104+
`apache/cloudstack-kubernetes-provider`. Covered by their own models.
105+
106+
## §4 Trust boundaries and data flow
107+
108+
The only boundary the SDK contributes is **the embedded application's
109+
call into `NewAsyncClient` (or `NewClient`) and subsequent typed API
110+
calls**. Bytes the caller passes — endpoint, `apiKey`, `secretKey`,
111+
verifySSL, per-method parameters — are caller-controlled config and not
112+
attacker-controlled per the main-model §7 adversary set.
113+
114+
The bytes that come *back* from the management server (JSON responses)
115+
are trusted control-plane content per the main model's B1 transition.
116+
117+
**Per-call request shape** *(documented: `cloudstack/cloudstack.go`
118+
lines ~547–575)*:
119+
120+
- the SDK forces `signatureversion=3`, which mandates an `expires`
121+
parameter (good — see main-model §5a "api.signature.version" row);
122+
- the SDK computes HMAC-SHA1 of the lowercase-sorted parameter string
123+
under `secretKey` (`crypto/hmac` + `crypto/sha1`);
124+
- the signature is base64-encoded and placed in either the POST body
125+
or the URL (when `HTTPGETOnly` is set);
126+
- when `HTTPGETOnly = true`, the SDK constructs the URL as
127+
`baseURL + "?" + paramString + "&signature=" + url.QueryEscape(sig)`
128+
— note the signature lands in the *URL* (and thus the access log /
129+
reverse proxy log) for GET requests *(inferred — Q3)*.
130+
131+
## §5 Assumptions about the environment
132+
133+
- **Go version**: per `go.mod` (`go 1.x`) — confirm with maintainer
134+
*(inferred — Q4)*.
135+
- **TLS**: provided by `crypto/tls` from the Go stdlib; verification
136+
default is on, weakened only by the explicit `verifyssl=false` caller
137+
choice.
138+
- **Transport**: `net/http` with stdlib defaults; no connection pool
139+
sharing across `cloudstack.Client` instances *(inferred — Q5)*.
140+
- **Concurrency**: `cloudstack.Client` is intended to be safe for
141+
concurrent use *(inferred — Q5)*.
142+
- **What the SDK does not do to its host**: no global state mutation,
143+
no signal handlers, no env-var consumption (credentials are passed
144+
in via the constructor, not read from env). *(inferred — Q6)*
145+
146+
## §5a Build-time and configuration variants
147+
148+
| Knob | Default | Stance | Effect |
149+
| --- | --- | --- | --- |
150+
| `verifyssl` arg to `NewClient` / `NewAsyncClient` | passed-in *(no default)* | calling with `false` is documented for dev / self-signed-CA setups | when `false`, `InsecureSkipVerify: true` is set in the TLS config |
151+
| `cs.HTTPGETOnly` | `false` *(inferred — Q3)* | the Kubernetes / Terraform satellites set this `true` to force GET, which inlines signatures in the URL | when `true`, requests are GET with signature in URL |
152+
| async timeout via `cs.AsyncTimeout(…)` | `300s` default *(inferred — Q7)* | bounds how long async jobs are polled | timeout exhaustion returns an error, not a hang |
153+
154+
## §6 Assumptions about inputs
155+
156+
| Entry point | Parameter | Attacker-controllable in the model? | Caller must enforce |
157+
| --- | --- | --- | --- |
158+
| `NewClient` / `NewAsyncClient` | `endpoint`, `apiKey`, `secretKey`, `verifyssl` | **no** — operator config per §3 item 1 | sanity-check the endpoint URL; do not source from end-user input |
159+
| Per-service typed call (e.g. `VirtualMachine.NewDeployVirtualMachineParams`) | typed params | **no** — typed params come from the embedding app's own logic | the embedding app applies its own user authn/authz before constructing params |
160+
| HTTP response | JSON body | trusted as far as the management server is trusted | bytes are JSON-decoded into typed responses |
161+
162+
## §7 Adversary model
163+
164+
Main-model §7 applies. **Adjustments specific to the SDK**:
165+
166+
- The "unauthenticated network peer reaching `:8080`/`:8443`" actor is
167+
*upstream* of the SDK and not in scope for SDK code.
168+
- An additional adversary worth naming: **a passive observer of HTTP
169+
access logs / reverse-proxy logs** when `HTTPGETOnly = true`. The
170+
signature lands in the URL and thus in any log that records URLs.
171+
Whether this is a §10 caller responsibility or a §11 misuse is the
172+
Q3 ruling. *(inferred — Q3)*
173+
174+
## §8 Security properties the SDK provides
175+
176+
### S1 — HMAC-SHA1 signature with `signatureversion=3`
177+
178+
- **Property.** Every API call carries a v3 signature with an `expires`
179+
parameter, computed via `crypto/hmac` + `crypto/sha1` over the
180+
canonical parameter string. *(documented: `cloudstack/cloudstack.go`
181+
lines ~547–575)*
182+
- **Conditions.** Caller supplied a valid `secretKey`.
183+
- **Violation symptom.** A request leaves the SDK without a signature,
184+
or with a signature derived from input outside the canonical
185+
parameter string.
186+
- **Severity.** Security-critical, `VALID` per main-model §13.
187+
188+
### S2 — TLS verification on by default
189+
190+
- **Property.** The HTTP client uses stdlib TLS defaults; verification
191+
is on unless the caller passes `verifyssl = false`.
192+
- **Conditions.** Caller did not pass `verifyssl = false`.
193+
- **Violation symptom.** TLS verification skipped despite `verifyssl =
194+
true`.
195+
- **Severity.** Security-critical, `VALID`.
196+
197+
### S3 — Constant-time secret-handling at the signer
198+
199+
- **Property.** *(inferred — Q8)* The SDK does not compare secrets, so
200+
the question is whether the signer leaks the secret via timing /
201+
branch-predictable paths. The Go `crypto/hmac` is the only consumer.
202+
- **Conditions.** as above.
203+
- **Violation symptom.** Side-channel recovery of `secretKey`.
204+
- **Severity.** Security-critical if reachable; otherwise `OUT-OF-MODEL`
205+
per main-model §7 side-channel exclusion.
206+
207+
## §9 Security properties the SDK does *not* provide
208+
209+
- **No protection of `apiKey` / `secretKey` at rest.** The caller decides
210+
where to store them. *(inferred — Q1)*
211+
- **No defence when `verifyssl = false`.** Explicit caller-supplied
212+
TLS-verification disablement.
213+
- **No retry / rate-limit / circuit-breaker by default.** The async
214+
client polls; that is the only built-in scheduling.
215+
- **No re-validation of management-server response correctness.**
216+
- **No log-redaction of signatures or credentials by the SDK.** If the
217+
embedding application logs request URLs (which would include the
218+
signature when `HTTPGETOnly = true`), the signature ends up in the
219+
log. *(inferred — Q3)*
220+
221+
### False-friend properties
222+
223+
- **`HTTPGETOnly = true` is a transport choice, not "more secure".** It
224+
forces signatures into the URL where they will appear in any URL log.
225+
- **"HMAC-SHA1" is not a SHA1-broken construction.** Collision attacks
226+
on SHA1 do not extend to HMAC-SHA1. Reports flagging "SHA1 is
227+
deprecated" against the signer are `KNOWN-NON-FINDING` per the main
228+
model's §11a.
229+
230+
## §10 Downstream responsibilities
231+
232+
The embedding Go application MUST:
233+
234+
1. Pass `verifyssl = true` unless connecting to a known dev cluster.
235+
2. Source `apiKey` / `secretKey` from a secret store, not from
236+
user-controlled input.
237+
3. Not log full request URLs when `HTTPGETOnly = true`.
238+
4. Apply its own timeouts and retries on top of the SDK as appropriate.
239+
5. Use the SDK version matched to the management-server version
240+
*(documented: README — "SDK releases tagged based on the ACS
241+
version")*.
242+
243+
## §11 Known misuse patterns
244+
245+
- Calling `NewAsyncClient(endpoint, key, secret, false)` in production
246+
(TLS verification disabled).
247+
- Embedding credentials in source / build artifacts.
248+
- Using `HTTPGETOnly = true` *and* shipping URL logs off-host.
249+
- Using a mismatched SDK / server major version and assuming the
250+
request shape will still verify.
251+
252+
## §11a Known non-findings (recurring false positives)
253+
254+
- **"HMAC-SHA1 — SHA1 is deprecated."**`KNOWN-NON-FINDING` per main
255+
model §11a.
256+
- **"`InsecureSkipVerify: true` is hardcoded."** It is gated by the
257+
caller-supplied `verifyssl` argument. → `OUT-OF-MODEL: trusted-input`.
258+
- **"Signature appears in URL."** Only when caller sets `HTTPGETOnly =
259+
true`. → `OUT-OF-MODEL: non-default-build` if `false` is the default
260+
per Q3. Else escalate.
261+
- **"Examples in `examples/` have weak input handling."** Out of
262+
scope. → `OUT-OF-MODEL: unsupported-component`.
263+
264+
## §12 Conditions that would change this delta
265+
266+
- Change in default signing algorithm in the main model (SHA1 → SHA256).
267+
- Change in `HTTPGETOnly` default or new option that affects signature
268+
placement.
269+
- Addition of credential storage to the SDK.
270+
271+
## §13 Triage dispositions
272+
273+
Use the same table as the main model.
274+
275+
## §14 Open questions for the maintainers
276+
277+
**Q1.** Out-of-scope: where the caller stores `apiKey` / `secretKey`
278+
on disk. Confirm.
279+
280+
**Q2.** Out-of-scope: revalidating management-server response
281+
correctness in the SDK. Confirm.
282+
283+
**Q3.** `HTTPGETOnly` default and signature-in-URL leakage — is `false`
284+
the default and is "do not log URLs when `HTTPGETOnly = true`" a
285+
documented caller responsibility? *(maps to §5a, §6, §10, §11a)*
286+
287+
**Q4.** Confirm the Go MSRV.
288+
289+
**Q5.** Concurrency: is `cloudstack.Client` documented as safe for
290+
concurrent use across goroutines? Is the underlying `http.Client`
291+
shared or per-call?
292+
293+
**Q6.** Confirm the §5 negative side-effect inventory: no env-var
294+
consumption, no signal handlers, no global mutation.
295+
296+
**Q7.** Confirm `AsyncTimeout` default.
297+
298+
**Q8.** Constant-time / side-channel posture at the signer — confirm
299+
the SDK delegates entirely to Go stdlib `crypto/hmac` and makes no
300+
additional claim.
301+
302+
**Q9.** Meta — should this delta document live in `apache/cloudstack-go`
303+
at `docs/threat-model.md`, or only on the website?

0 commit comments

Comments
 (0)