Fold in vishesh92 2026-06-08 review: resolve §14 Q8/Q9/Q10/Q17/Q18/Q19

Promotes six §14 questions from proposed to maintainer-confirmed per
vishesh92's inline answers (apache/cloudstack#13293):

- Q8 root admin: confirmed trusted operator (direct access anyway) ->
  OUT-OF-MODEL: equivalent-harm (§3 item 4, §7).
- Q9 guest/hypervisor: side channels + in-allocation exhaustion out of
  scope; one in-model case is CloudStack applying wrong/insecure
  hypervisor settings (Daan to confirm boundary).
- Q10 userdata: end-user guest-OS customization, tenant-controlled data
  in their own boundary, not a CloudStack injection surface.
- Q17 proxy headers: proxy.header.verify default false; proxy.header.names
  read only when Remote_Addr in proxy.cidr.
- Q18 2FA: corrected the stale setting names to the real ones -
  enable.user.2fa + mandate.user.2fa (both default false, domain-
  configurable); 2FA-off is a deployment choice, not a §10 violation.
- Q19 password encoders: greenfield md5/plaintext hashing is
  OUT-OF-MODEL: non-default-build; effective set PBKDF2,SHA256SALT,SAML2.

Clears the pending-Q18/Q19 notes in §10/§11 and updates the tally.
L976 (simulator/tools-appliance scope) and L1007 stay open on Daan.

Generated-by: Claude Opus 4.8 (1M context)
This commit is contained in:
Jarek Potiuk 2026-06-11 03:47:59 +02:00
parent 5b4e0ec21f
commit dcc71cd098
1 changed files with 54 additions and 35 deletions

View File

@ -63,9 +63,10 @@
response to this draft; *(inferred)* = synthesized by the producer from
code structure or domain knowledge, awaiting PMC ratification (every
*(inferred)* tag has a matching §14 question).
- **Draft confidence (provenance-tag tally):** 51 *(documented)* / 35
*(maintainer)* / 45 *(inferred)*. Five formerly-open questions (Q1, Q2,
Q4, Q5, Q12 — including the highest-leverage Root-CA strictness default)
- **Draft confidence (provenance-tag tally):** 51 *(documented)* / 42
*(maintainer)* / 38 *(inferred)*. Eleven formerly-open questions (Q1,
Q2, Q4, Q5, Q12 — including the highest-leverage Root-CA strictness
default — plus Q8, Q9, Q10, Q17, Q18, Q19 from the 2026-06-08 review)
were resolved by the CloudStack PMC review (DaanHoogland, vishesh92) and
their tags promoted from *(inferred)* to *(maintainer)*.
@ -183,7 +184,7 @@ requiring any of these will be closed with the cited disposition:
configuration, upload templates and scripts to system VMs, register
arbitrary network/storage plugins, and run `runCustomAction`-style
commands. A new way for a root admin to do something they are already
authorized to do is not a vulnerability *(inferred — §14 Q8)*. →
authorized to do is not a vulnerability *(maintainer: vishesh92 — §14 Q8)*. →
`OUT-OF-MODEL: equivalent-harm`.
5. **A defender against a guest VM doing things the hypervisor allows it
to do.** A guest VM consuming CPU, memory, or disk up to its allocated
@ -191,8 +192,9 @@ requiring any of these will be closed with the cited disposition:
security group, or exploiting another VM via the hypervisor's own
shared surfaces (sidechannel, RowHammer, GPU passthrough leak) is out
of model. CloudStack is responsible only for the orchestration that
*places* the guest, not for hypervisor-level isolation *(inferred —
§14 Q9)*. → `OUT-OF-MODEL: adversary-not-in-scope` for the
*places* the guest, not for hypervisor-level isolation *(maintainer:
vishesh92 — §14 Q9; the in-model case is CloudStack applying
wrong/insecure hypervisor settings — Daan to confirm boundary)*. → `OUT-OF-MODEL: adversary-not-in-scope` for the
side-channel case, `BY-DESIGN: property-disclaimed` for the
resource-limit case.
6. **A sandbox for templates, ISO images, or user-data scripts.** A
@ -200,7 +202,7 @@ requiring any of these will be closed with the cited disposition:
hypervisor with the privileges the offering grants. cloud-init /
user-data / metadata is passed through to the guest; CloudStack does
not parse or sanitize its semantics *(documented: kubernetes-service
plugin `userdata` references; inferred — §14 Q10)*. →
plugin `userdata` references; maintainer: vishesh92 — §14 Q10, end-user guest customization)*. →
`BY-DESIGN: property-disclaimed`.
7. **Code that ships but is not part of the supported product:**
`tools/marvin/`, `test/`, `developer/`, `quickcloud/`, `cloud-cli/`,
@ -346,10 +348,10 @@ security-relevant subset:
| `ca.plugin.root.auth.strictness` | **`true` for new setups; `false` only on upgrade from pre-Aug-2017 versions** *(maintainer: vishesh92 — `https://github.com/apache/cloudstack/pull/2239`)* | New setups are strict by default; the `false`-on-upgrade case is called out in the upgrade instructions and is therefore not a concern *(maintainer: DaanHoogland)* | When `false`, the management server's `RootCACustomTrustManager` does **not** require a client certificate from a peer attempting to connect on `:8250` (agent port) or cluster ports. A peer without a cert is allowed in. |
| `ca.plugin.root.allow.expired.cert` | **`true`** *(documented: `RootCAProvider.java`)* | operational default to survive cert-rotation lag *(maintainer: paired with the strictness ruling above)* | When `true`, an expired client cert is accepted during SSL handshake. |
| `ca.plugin.root.issuer.dn` | `CN=ca.cloudstack.apache.org` *(documented: same file)* | configured at first management-server boot | Subject DN of the auto-generated self-signed Root CA. |
| `proxy.header.verify` | off by default *(inferred — §14 Q17; setting name maintainer: vishesh92)* | When on, the operator must restrict `proxy.cidr` to the trusted reverse-proxy CIDR | When set, `ApiServlet.getClientAddress` honours proxy-set forward headers *only* for source IPs in `proxy.cidr` *(documented: `server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress`; setting name maintainer: vishesh92)*. |
| `proxy.header.verify` | `false` by default *(maintainer: vishesh92 — §14 Q17)* | When on, the operator must restrict `proxy.cidr` to the trusted reverse-proxy CIDR | When set, `ApiServlet.getClientAddress` honours proxy-set forward headers *only* for source IPs in `proxy.cidr` *(documented: `server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress`; setting name maintainer: vishesh92)*. |
| `proxy.header.names` | list of header names; semantics: names to check for allowed IP addresses from a proxy-set header *(maintainer: vishesh92)* | list of header names to consult for the allowed client address when set by a proxy | Names the request header(s) carrying the proxy-set client IP. |
| `proxy.cidr` | unset *(inferred — §14 Q17; setting name maintainer: vishesh92)* | required when `proxy.header.verify` is on | List of CIDRs for which `proxy.header.names` headers are honoured when the connecting `Remote_Addr` is in this list *(semantics maintainer: vishesh92)*. |
| `enable.2fa.for.users` / `enable.2fa.for.api` | per-domain toggle *(documented: `plugins/user-two-factor-authenticators/`)* | dev-test default off; production posture depends on PMC ruling *(inferred — §14 Q18)* | When on, users must complete static-pin or TOTP 2FA after login. |
| `proxy.cidr` | unset *(maintainer: vishesh92 — §14 Q17; headers honoured only when `Remote_Addr` ∈ this list)* | required when `proxy.header.verify` is on | List of CIDRs for which `proxy.header.names` headers are honoured when the connecting `Remote_Addr` is in this list *(semantics maintainer: vishesh92)*. |
| `enable.user.2fa` / `mandate.user.2fa` | both default `false`; domain-configurable *(maintainer: vishesh92 — §14 Q18)* | `enable.user.2fa` turns 2FA on; `mandate.user.2fa` makes it mandatory (only when `enable.user.2fa` is true) — a deployment choice, not a §10 violation when off | When on, users must complete static-pin or TOTP 2FA after login. |
| `security.encryption.key`, `security.encryption.iv` | auto-generated at first boot *(documented: `framework/security/.../KeysManager.java`)* | trusted secret | Base64-encoded JaSypt master key + IV used to encrypt application-level secrets in the DB. |
| `user.password.encoders.order` | `PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT` *(maintainer: vishesh92)* | first encoder in the order is used to hash new passwords; the list also defines the verification fall-through order | Governs how user passwords are stored and which encoders are accepted on verify. |
| `user.password.encoders.exclude` | `MD5,LDAP,PLAINTEXT` *(maintainer: vishesh92)* | excluded encoders are not used to (re)hash passwords | Excludes weak/legacy encoders from being chosen, even though they remain in the order list for verifying already-stored hashes. |
@ -514,8 +516,8 @@ For each property: condition, violation symptom, severity tier, provenance.
### P6 — Reverse-proxy IP-trust gating for forward headers
- **Condition**: `proxy.header.verify` on *(inferred — §14 Q17; setting
name maintainer: vishesh92)*;
- **Condition**: `proxy.header.verify` on (default `false`) *(maintainer:
vishesh92 — §14 Q17)*;
only requests whose `Remote_Addr` falls in `proxy.cidr` have their
`proxy.header.names` forward header(s) consulted *(documented:
`ApiServlet.java` `getClientAddress` `NetUtils.isIpInCidrList`; setting
@ -709,9 +711,12 @@ The operator deploying CloudStack in production **must**:
`MD5,LDAP,PLAINTEXT` (so the weak encoders are not chosen for hashing,
only retained for verifying already-stored hashes) *(maintainer:
vishesh92)*. Do not remove `MD5`/`PLAINTEXT` from the exclude list in
production *(pending §14 Q19 ruling on the supported greenfield set)*.
production — the supported greenfield encoder set is
`PBKDF2,SHA256SALT,SAML2` *(maintainer: vishesh92 — §14 Q19)*.
8. Enable 2FA (`totp` or `static-pin`) for administrators and ideally for
all users *(pending §14 Q18 ruling)*.
all users — 2FA on/off is a deployment choice via `enable.user.2fa`
and `mandate.user.2fa` (both default `false`) *(maintainer: vishesh92 —
§14 Q18)*.
9. Rotate per-user API secret keys on personnel change and on suspected
compromise.
10. Treat user-uploaded templates and ISOs as crossing a trust boundary —
@ -749,7 +754,7 @@ The operator deploying CloudStack in production **must**:
reordering them to the front of `user.password.encoders.order`) in
production.** The encoders ship for verifying legacy hashes; promoting
them to hash new passwords stores weakly-protected credentials
*(maintainer: vishesh92)*. Pending §14 Q19 on the greenfield-supported set.
*(maintainer: vishesh92 — §14 Q19; the supported greenfield encoder set is `PBKDF2,SHA256SALT,SAML2`)*.
- **Granting domain admin to too many users.** A domain admin can manage
all accounts within the domain — including reading guest console URLs.
- **Embedding console-proxy URLs in screenshots, ticketing systems, or
@ -952,22 +957,31 @@ This resolution reshaped §3 item 1, §5a, §7 (the un-certed peer row),
### Wave 3 — adjacent insecure defaults and admin-only surfaces
**Q8.** Is "a root admin with full RBAC role causes harm Y via a
**Q8.** ~~Is "a root admin with full RBAC role causes harm Y via a
documented path Z" out of scope (proposed: **yes**, `OUT-OF-MODEL:
equivalent-harm`)? In particular: `runCustomAction`, template upload,
plugin registration, global config change, system-VM patching, system-VM
console access. *(maps to §3 item 4, §9)*
console access.~~ **RESOLVED** *(maintainer: vishesh92)* — yes; a root
admin generally has direct access to most of these resources anyway →
`OUT-OF-MODEL: equivalent-harm`. *(maps to §3 item 4, §9)*
**Q9.** Guest VM workloads — confirm that hypervisor-mediated side
**Q9.** ~~Guest VM workloads — confirm that hypervisor-mediated side
channels and resource-exhaustion-within-allocation are out of scope, and
that the in-scope orchestration concerns are limited to "did CloudStack
place the VM in the right VLAN / apply the right security group / route
the right IP" (proposed)? *(maps to §3 item 5, §7, §9)*
the right IP" (proposed)?~~ **RESOLVED** *(maintainer: vishesh92)* — yes;
side channels + resource-exhaustion-within-allocation are out of scope.
The one in-model case: CloudStack applying a wrong/insecure setting while
launching or managing the guest (CloudStack must use correct/secure
hypervisor settings). *(DaanHoogland to confirm the boundary.)* *(maps to
§3 item 5, §7, §9)*
**Q10.** Templates / ISOs / user-data — confirm that there is no
**Q10.** ~~Templates / ISOs / user-data — confirm that there is no
sandboxing of user-supplied OS images, and that user-data is intentionally
a code-execution channel into the guest (proposed)? *(maps to §3 item 6,
§9)*
a code-execution channel into the guest (proposed)?~~ **RESOLVED**
*(maintainer: vishesh92)* — yes; userdata is the end user customizing
their own guest OS (tenant-controlled data inside their own boundary), not
a CloudStack-side injection surface. *(maps to §3 item 6, §9)*
**Q11.** Confirm the unsupported-component list: `tools/marvin/`,
`test/`, `developer/`, `quickcloud/`, `cloud-cli/`,
@ -978,26 +992,31 @@ item 7)*
**Q17.** Forward-header gating — the **setting names are confirmed**
*(maintainer: vishesh92)*: `proxy.header.verify` (the on/off gate),
`proxy.header.names` (header names to consult), and `proxy.cidr` (CIDRs of
the `Remote_Addr` values for which those headers are honoured). **Still
open:** confirm `proxy.header.verify` is **off by default** and that
`proxy.cidr` must be set for the headers to take effect. *(maps to §5a,
§6, §10)*
the `Remote_Addr` values for which those headers are honoured). **RESOLVED** *(maintainer: vishesh92)*`proxy.header.verify` is
**`false` by default**; only when the connecting `Remote_Addr`
`proxy.cidr` does CloudStack read the client IP from `proxy.header.names`.
*(maps to §5a, §6, §10)*
**Q18.** 2FA — proposed: off by default, operator turns it on per
**Q18.** ~~2FA — proposed: off by default, operator turns it on per
domain / per user via `enable.2fa.*`. Confirm; and is "2FA disabled in
production" a §10 violation or a deployment choice? *(maps to §5a,
§10)*
production" a §10 violation or a deployment choice?~~ **RESOLVED**
*(maintainer: vishesh92)* — deployment choice, not a §10 violation. Two
domain-configurable global settings: `enable.user.2fa` (default `false`;
whether 2FA is enabled) and `mandate.user.2fa` (default `false`; whether
2FA is mandatory — applies only when `enable.user.2fa` is true). *(maps to
§5a, §10)*
**Q19.** User-authenticator plugins — encoder selection is governed by
`user.password.encoders.order` (default
`PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT`) and
`user.password.encoders.exclude` (default `MD5,LDAP,PLAINTEXT`), so PBKDF2
is the effective hashing default and `MD5`/`PLAINTEXT` are retained only
for verifying legacy hashes *(maintainer: vishesh92)*. **Still open:**
confirm that a report against `md5`/`plain-text` being *used to hash new
passwords* in a greenfield install is `OUT-OF-MODEL: non-default-build`
(they are excluded by default), and confirm the supported greenfield
encoder set. *(maps to §5a, §10, §11)*
for verifying legacy hashes *(maintainer: vishesh92)*. **RESOLVED** *(maintainer: vishesh92)* — a
report against `md5`/`plain-text` hashing *new* passwords in a greenfield
install is `OUT-OF-MODEL: non-default-build`: the default
`user.password.encoders.exclude` (`MD5,LDAP,PLAINTEXT`) removes them from
the effective set, so the supported greenfield encoders are
`PBKDF2,SHA256SALT,SAML2`. *(maps to §5a, §10, §11)*
**Q20.** Integration API port `:8096` — proposed: closed (port-zero) by
default in production packaging, open only when explicitly configured;
@ -1134,7 +1153,7 @@ landing page. The de facto security-policy artifacts are scattered:
| `plugins/ca/root-ca/.../RootCACustomTrustManager.java` | `authStrictness` and `allowExpiredCertificate` semantics | §5a, §8 P5 |
| `plugins/acl/{static,dynamic,project}-role-based` | RBAC backends | §8 P4 |
| `plugins/user-authenticators/{md5,sha256salted,pbkdf2,plain-text,ldap,saml2,oauth2}` | pluggable user auth; selection via `user.password.encoders.order` / `user.password.encoders.exclude` | §2 caller-roles row, §5a "user.password.encoders.*" rows, §10 item 7 |
| `plugins/user-two-factor-authenticators/{static-pin,totp}` | 2FA backends | §5a "enable.2fa.*", §10 item 8 |
| `plugins/user-two-factor-authenticators/{static-pin,totp}` | 2FA backends | §5a "enable.user.2fa / mandate.user.2fa", §10 item 8 |
| `framework/security/.../KeysManager.java`, `KeystoreManager.java` | `security.encryption.key`, `security.encryption.iv` (Hidden), application-secret JaSypt encryption | §8 P8, §5a, §10 item 6 |
| `agent/src/main/java/com/cloud/agent/Agent.java` `setupAgentKeystore` (lines ~793916) | agent receives Root CA-signed cert via `SetupKeyStoreCommand` and imports it | §4 B5, §8 P5 |
| `server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java`, `ConsoleProxyPasswordBasedEncryptor.java` | signed encrypted console-proxy URL token | §4 B3, §8 P7 |