This is an automated email from the ASF dual-hosted git repository.
DaanHoogland pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 4beab943f2a Add draft project security threat-model document (#13293)
4beab943f2a is described below
commit 4beab943f2ae5d8705dbaf2f9b5e51702fbc58a8
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon Jul 6 14:51:05 2026 +0200
Add draft project security threat-model document (#13293)
---
draft-THREAT-MODEL.md | 1148 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1148 insertions(+)
diff --git a/draft-THREAT-MODEL.md b/draft-THREAT-MODEL.md
new file mode 100644
index 00000000000..4659b937ded
--- /dev/null
+++ b/draft-THREAT-MODEL.md
@@ -0,0 +1,1148 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+# Apache CloudStack Security Threat Model (draft)
+
+> **Document scope and PMC structural decision.** The CloudStack PMC owns
+> five repositories: `apache/cloudstack` (the management server, agent, and
+> systemvm), plus four satellite clients — `apache/cloudstack-cloudmonkey`
+> (CLI), `apache/cloudstack-go` (Go SDK),
`apache/cloudstack-terraform-provider`,
+> `apache/cloudstack-kubernetes-provider`. This document models
+> `apache/cloudstack` as the canonical threat model; the four satellite
+> models are short *deltas* that inherit §3 / §4 / §7 from this
+> document and add only what each satellite uniquely introduces (`§4 B1`
+> reachability, the credential file shape, the wrapper-of-SDK contract,
+> etc.). The deltas live at
`/tmp/claude/cloudstack-<repo>-threat-model-draft.md`.
+> The satellite clients' interfaces point **inward** at the management-server
+> API; some satellites additionally expose **outward** interfaces that are
+> designed to be safe to expose *(maintainer: DaanHoogland)*.
+> An umbrella model was rejected because the satellites are uniformly thin
+> "HMAC-SHA1-signing HTTP client" wrappers — a single document either
+> drowns them in CloudStack-server content or, worse, drowns the
+> CloudStack-server content in satellite caveats. Each satellite is small
+> enough that a 1–2 page delta works.
+
+## §1 Header
+
+- **Project:** Apache CloudStack (`apache/cloudstack`) — IaaS orchestration
+ platform. This document does **not** cover the four satellite repos, which
+ carry their own delta models.
+- **Commit:** `7308dad1` (HEAD of `main` at draft time).
+- **Date:** 2026-05-29.
+- **Authors:** ASF Security team draft, awaiting CloudStack PMC review.
+- **Status:** Draft — under maintainer review.
+- **Version binding:** This document describes the model as of the commit
+ above. A vulnerability report against CloudStack release *N* (currently
+ the 4.20.x line) should be triaged against the model as it stood at *N*'s
+ release tag, not against HEAD.
+- **Reporting:** vulnerabilities that fall under §8 (claimed properties)
+ should be reported per the project's published policy
+ (`[email protected]` per `README.md` and
+ `https://cloudstack.apache.org/security.html`); reports that fall under
+ §3 (out of scope), §9 (properties not provided), or §11a (known
+ non-findings) will be closed by CloudStack triagers citing this document.
+- **Provenance legend** —
+ *(documented)* = paraphrased from an in-repo source or the project website
+ with citation; *(maintainer)* = stated by a CloudStack PMC member in
+ 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)* / 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)*.
+
+**About the project.** Apache CloudStack is an open-source Infrastructure-as-a-
+Service (IaaS) orchestration platform *(documented: `README.md`,
+`https://cloudstack.apache.org/`)*. It deploys and manages large fleets of
+virtual machines across multiple hypervisors (KVM, VMware, XenServer/XCP-ng,
+Hyper-V, baremetal-bridge, OVM) and over object/block/file storage
+(NFS, Ceph/RBD, iSCSI, SMB, primary-storage plugins, S3-compatible secondary
+storage). A central **management server** (Java/Tomcat-style servlets,
+backed by MariaDB/MySQL) exposes a signed REST/JSON API to admins, end
+users, and integrations; runs system VMs (Secondary Storage VM, Console
+Proxy VM, virtual router); and orchestrates a fleet of **agents** running
+on each hypervisor host. Authorization is RBAC + multi-tenant
+domain/account/project hierarchy. The deployment shape is "operator-run
+private/public cloud control plane", not a hosted-as-a-service appliance.
+
+## §2 Scope and intended use
+
+### Intended use
+
+- A multi-tenant IaaS control plane deployed by an operator inside a
+ controlled datacenter or cloud, exposing compute / storage / network
+ orchestration to authenticated end users via APIs which return responses in
JSON or XML and a Vue.js Web
+ UI, with separately authenticated administrators *(documented: `README.md`,
+ `INSTALL.md`)*.
+- Used both for service-provider public clouds and for on-premises private
+ clouds; the trust model is the same in both *(documented: `README.md`)*.
+
+### Deployment shape
+
+CloudStack is **not** an in-process library, **not** a single-binary
+appliance, and **not** a hosted SaaS. It is a distributed control plane:
+one or more management-server instances — **a single management-server
+instance for smaller clouds, or a cluster behind a load balancer for
+larger deployments** *(maintainer: DaanHoogland)* — a MariaDB/MySQL
+database, one usage server, an optional
+SecondaryStorageVM/ConsoleProxyVM/VirtualRouter set of system VMs, and a
+per-hypervisor-host `cloudstack-agent` (for KVM/baremetal) or
+out-of-process resource bridges (for VMware / XenServer / XCP-ng / Hyper-V).
+The operator owns the surrounding L2/L3 network (the **management network**,
+the **public network**, the **guest network**, the **storage network**)
+and the physical hosts. The threat model is therefore that of a
+distributed service (single-instance or clustered), not a library
+*(maintainer: DaanHoogland — confirms the distributed control-plane shape;
+single-instance is also a supported topology)*.
+
+### Caller roles
+
+| Role | Trust level | Notes |
+| --- | --- | --- |
+| **End-user API client / Web UI user** | untrusted but authenticated |
Identity verified via Apache CloudStack-native (password + HMAC-SHA1 signed
request), LDAP, SAML2, OAuth2, or pluggable `APIAuthenticator` *(documented:
`plugins/user-authenticators/{ldap,saml2,oauth2,...}`,
`server/src/main/java/com/cloud/api/ApiServer.java` `verifyRequest`)*. |
+| **Domain / Project admin** | partial trust within their domain | Bounded by
RBAC (`plugins/acl/{static,dynamic,project}-role-based`) and the domain
hierarchy; can manage users / VMs / networks within a domain. |
+| **Root admin** | trusted control plane | Global RBAC role; can change global
configuration, upload templates/ISOs, run privileged orchestration. |
+| **Operator / cluster admin** | trusted | OS-level access to
management-server hosts, the MariaDB database, the keystore, and the agent
hosts. Sets `agent.properties`, manages `cloudstack-agent` packages, manages
the JCEKS keystore used by the agent for TLS *(documented:
`agent/conf/agent.properties`, `framework/security/.../KeystoreManager.java`)*.
|
+| **Hypervisor agent (cloudstack-agent on KVM/baremetal)** |
trusted-once-enrolled peer | Mutually authenticated via X.509 client cert
signed by the management server's Root CA *(documented: `framework/ca/`,
`plugins/ca/root-ca/`, `agent/src/main/java/com/cloud/agent/Agent.java`
`setupAgentKeystore`)*. |
+| **System VM (SSVM / CPVM / VR)** | trusted-once-enrolled peer | Same X.509
enrolment shape as the agent; carries the agent binary inside *(maintainer:
confirmed — same trust tier as agents, not a separate tier)*. |
+| **Hypervisor host (the underlying KVM/VMware/etc.)** | trusted by virtue of
operator-controlled provisioning | CloudStack expects to drive the hypervisor
via libvirt / VMware vSphere SDK / XenAPI as a privileged user *(documented:
`plugins/hypervisors/kvm/`, `plugins/hypervisors/vmware/`,
`plugins/hypervisors/xenserver/`)*. |
+| **Hypervisor-managed guest VM (end-user workload)** | **untrusted** | A
guest VM is an attacker's workload; the model defends against it. |
+| **Reverse proxy / load balancer in front of management server** | trusted
*(if `proxy.header.verify=true`)* | When the operator enables forward-header
processing, only requests whose `Remote_Addr` ∈ `proxy.cidr` have their
`proxy.header.names` header honoured *(documented:
`server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress`;
setting names maintainer: vishesh92)*. |
+| **Underlying storage (primary / secondary)** | trusted by virtue of
operator-granted credentials | CloudStack reads/writes via NFS / RBD / iSCSI /
S3 with operator-supplied credentials *(documented: primary/secondary storage
plugins under `plugins/storage/`)*. |
+| **External integrations (Tungsten, NSX, Netscaler, Palo Alto, …)** | trusted
control-plane peers | Operator-configured; CloudStack assumes truthful
responses *(inferred — §14 Q3)*. |
+
+### Component-family table
+
+| Management server JSON and XML APIs |
`server/src/main/java/com/cloud/api/ApiServlet.java`, HTTP on `:8080` (API +
UI), optional HTTPS on `:8443` when `https.enable=true`; user API path
`:8080/client/api` *(documented:
`server/src/main/java/com/cloud/api/ApiServlet.java`,
`client/conf/server.properties.in`)* | network (TCP, optionally TLS) | **yes** |
+| Management server cluster RPC (peer-to-peer) | NIO + TLS between
management-server replicas, `:9090` *(documented: `framework/cluster/`,
`utils/.../nio/`)* | network | **yes** (peer auth via Root CA) |
+| Management server → agent RPC | NIO + TLS on `:8250` (default
`agent.properties`) *(documented: `agent/conf/agent.properties` line 47,
`utils/.../nio/NioServer.java`)* | network | **yes** (mutually authenticated
via Root CA) |
+| `cloudstack-agent` (KVM/baremetal) | reverse-connects to management server,
runs commands via libvirt / hypervisor SDK *(documented: `agent/`,
`plugins/hypervisors/kvm/`)* | network + hypervisor + OS | **yes** |
+| System VMs — SecondaryStorageVM, ConsoleProxyVM, Virtual Router | shipped
images under `systemvm/`; agent binaries inside them *(documented:
`systemvm/`)* | network (storage / public / guest) | **yes** |
+| Console proxy data path | browser ↔ ConsoleProxyVM ↔ hypervisor VNC/SPICE
socket; signed token issued by management server *(documented:
`server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java`,
`server/src/main/java/com/cloud/servlet/ConsoleProxyPasswordBasedEncryptor.java`)*
| network | **yes** |
+| Secondary-storage HTTP (templates, ISO downloads, snapshot copies) |
download links are UUID-named symlinks served by an Apache httpd, with **no
auth on the link**; the UUID format prevents enumeration and the symlinks are
removed after a period *(maintainer: vishesh92, DaanHoogland)* | network |
**yes** |
+| Hypervisor plugins
(`plugins/hypervisors/{kvm,vmware,xenserver,hyperv,ovm,ovm3,baremetal,ucs,simulator}`)
| invoked by agent or by management server *(documented:
`plugins/hypervisors/`)* | hypervisor APIs | **yes** for the call shape;
**out-of-model** for the upstream hypervisor's own bugs |
+| Network plugins
(`plugins/network-elements/{netscaler,nsx,palo-alto,tungsten,nicira-nvp,...}`)
| management server outbound | external SDN/firewall APIs | **yes** for
credential handling and request construction; **out-of-model** for the external
endpoint |
+| Storage plugins (`plugins/storage/{volume,image,object}`) | management
server / agent | NFS, RBD, iSCSI, S3 endpoints | **yes** for credential
handling; **out-of-model** for the storage endpoint |
+| User authenticator plugins
(`plugins/user-authenticators/{md5,sha256salted,pbkdf2,plain-text,ldap,saml2,oauth2}`)
| management server | LDAP / SAML2 IdP / OAuth2 IdP | **yes** for the local
code; **out-of-model** for the IdP |
+| RootCA provider (`plugins/ca/root-ca/`) | self-signed CA generated by
management server at first boot, issues certs to agents *(documented:
`plugins/ca/root-ca/.../RootCAProvider.java`)* | none directly | **yes** |
+| Two-factor authenticators
(`plugins/user-two-factor-authenticators/{static-pin,totp}`) | management
server | none | **yes** |
+| Backup providers (`plugins/backup/`) | management server outbound | external
backup endpoints | **yes** for credential handling |
+| Quota / metrics / DRS / HA planners | internal | none | **yes** as
orchestration only; not a security boundary |
+| Database layer (MariaDB/MySQL, Jasypt-encrypted secrets) | management server
| network to DB | **yes** for credential handling; DB itself is trusted
*(documented: `README.md` "Notice of Cryptographic Software" — JaSypt, native
DB encryption)* |
+| `cloud-cli`, `tools/marvin`, `test/`, `developer/`, `quickcloud/` |
integration / test tooling | varies | **out of model** *(§3)* |
+| `systemvm/agent/noVNC` (a vendored fork of `github.com/novnc/novnc` with
CloudStack-specific changes on top *(maintainer: vishesh92)*), `…/vendor/pako`,
other vendored JS / shell scripts | vendored upstream | n/a | in-model only at
the wrapper boundary; upstream bugs go upstream. No automated
vendored-dependency update procedure exists today (dependabot does not produce
viable PRs); the PMC would prefer to have one *(maintainer: DaanHoogland)* |
+
+## §3 Out of scope (explicit non-goals)
+
+CloudStack is not, and does not aim to be, the following — reports
+requiring any of these will be closed with the cited disposition:
+
+1. **A defender against the operator.** Anyone with `root` on a
+ management-server host, `root` on a hypervisor host, raw MariaDB
+ credentials, the JCEKS keystore + `security.encryption.key` /
+ `security.encryption.iv` *(documented:
+ `framework/security/.../KeysManager.java`)*, or the Root CA private key
+ already has unbounded power. "The operator misconfigured X" is not a
+ vulnerability *(inferred — §14 Q6)*. → `OUT-OF-MODEL:
+ adversary-not-in-scope`.
+2. **A defender against a malicious external service the operator
+ configured.** A hostile LDAP server, SAML IdP, OAuth IdP, Tungsten /
+ NSX / Netscaler controller, S3 endpoint, Ceph cluster, or backup
+ provider is treated as a trusted control-plane peer. If the report
+ requires that peer to be hostile, it is out of model *(inferred —
+ §14 Q3)*. → `OUT-OF-MODEL: trusted-input`.
+3. **A defender against the hypervisor.** CloudStack drives KVM / VMware /
+ XenServer / XCP-ng / Hyper-V via their own admin APIs. A hypervisor
+ bug that allows guest escape, a vSphere SDK vulnerability, a libvirt
+ privilege escalation — all are upstream to the hypervisor project, not
+ to CloudStack *(inferred — §14 Q7)*. → `OUT-OF-MODEL:
+ unsupported-component` (upstream pointer).
+4. **An isolation boundary between an authorized administrator's API
+ call and the management server process.** Root admin can change global
+ 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 *(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
+ limit, sending arbitrary IP traffic within its assigned VLAN / VXLAN /
+ 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 *(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
+ user-uploaded template (via `registerTemplate`) is run by the
+ hypervisor with the privileges the system 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; 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/`,
+ `tools/devcloud4/`, `tools/devcloud-kvm/`, `tools/appliance/`,
+ `tools/checkstyle/`, `tools/transifex/`, `services/`-side simulators,
+ `simulator` hypervisor plugin, and IDE / build helpers under `tools/`.
+ *(inferred — §14 Q11)*. → `OUT-OF-MODEL: unsupported-component`.
+8. **Bundled / vendored upstream libraries** — JaSypt, Bouncy Castle,
+ JSch, OpenSwan, noVNC + `pako`, MariaDB Connector/J, Spring,
+ Apache Commons, log4j, etc. *(documented: `README.md` Cryptographic
+ Software notice)*. `systemvm/agent/noVNC` is specifically a **vendored
+ fork of `github.com/novnc/novnc`** carrying CloudStack-specific changes
+ *(maintainer: vishesh92)*. Where CloudStack vendors source, the vendored
+ code is modeled at the wrapper boundary; vulnerabilities intrinsic to the
+ upstream project should be reported upstream. There is currently **no
+ automated procedure** to pull upstream fixes into the vendored copies
+ (dependabot has not produced viable PRs); the PMC would prefer to
+ establish one *(maintainer: DaanHoogland)*.
+ → `OUT-OF-MODEL: unsupported-component` (with an upstream pointer).
+9. **The four satellite repos** (`apache/cloudstack-cloudmonkey`,
+ `apache/cloudstack-go`, `apache/cloudstack-terraform-provider`,
+ `apache/cloudstack-kubernetes-provider`) — covered by their own delta
+ threat models which inherit §3 / §4 / §7 from this document.
+10. **The CloudStack documentation site, Confluence wiki, downloads
+ mirrors, Docker Hub images outside `apache/cloudstack-*`, gem /
+ npm / PyPI packages with similar names, and other non-product
+ surfaces.** Out of scope.
+
+## §4 Trust boundaries and data flow
+
+CloudStack has at least nine distinct trust transitions; a finding is
+in-model only when it cleanly maps to one of them.
+
+| # | Transition | Authentication | Authorization |
+| --- | --- | --- | --- |
+| B1 | API client → management server JSON API (`:8080`/`:8443`) | per-user
API key + HMAC-SHA1 signature over query string, or session login + 2FA
*(documented: `server/src/main/java/com/cloud/api/ApiServer.java`
`verifyRequest`)*; signature version 3 has expiration enforcement *(documented:
same file line ~1053)* | RBAC (dynamic-role-based / static-role-based /
project-role-based) on the called API command name + domain/account ownership
of named resources |
+| B2 | Web UI → management server (`:8080`) | same as B1 plus session cookie |
same as B1 |
+| B3 | Browser → ConsoleProxyVM → hypervisor VNC socket | signed token issued
by management server, embedded in URL; encrypted with
`ConsoleProxyPasswordBasedEncryptor` *(documented:
`server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java`,
`ConsoleProxyPasswordBasedEncryptor.java`)* | implicit (signed-token
possession) |
+| B4 | Management server ↔ management server (cluster peers) | NIO + TLS, Root
CA-issued certs *(documented: `framework/cluster/`, `framework/ca/`)* |
peer-trust by valid cert |
+| B5 | Management server → `cloudstack-agent` (KVM/baremetal) | NIO + TLS on
`:8250`; agent uses X.509 client cert issued by Root CA on first connect; cert
provisioning is the `SetupKeyStoreCommand` shape *(documented:
`agent/src/main/java/com/cloud/agent/Agent.java` `setupAgentKeystore`,
`framework/ca/.../CAService.java`,
`plugins/ca/root-ca/.../RootCAProvider.java`)*; trust strictness governed by
`ca.plugin.root.auth.strictness` (**default `true` for new setups; `false` only
on upgrade [...]
+| B6 | Management server → external services (LDAP / SAML2 / OAuth2 IdP, NSX,
Netscaler, Tungsten, S3, backup providers) | per-provider (service account,
OAuth token, etc.) | external-service-side |
+| B7 | Agent → hypervisor (libvirt / vSphere SDK / XenAPI) | local Unix socket
(libvirt) or operator-supplied SDK credentials | hypervisor-side |
+| B8 | Management server / agent → primary/secondary storage (NFS, RBD, iSCSI,
S3) | OS-level (NFS), Ceph cephx, iSCSI CHAP, IAM key / static credential (S3)
| storage-side |
+| B9 | Operator → management server config (`db.properties`,
`server.properties`, JCEKS keystore, global config table) | filesystem
permissions on the host + DB access | OS-level + DB-level |
+
+### Reachability preconditions per family
+
+For each family in §2, a finding is in-model only if it is reachable as
+follows:
+
+- **Management server JSON API**: reachable from an *unauthenticated* network
+ peer who can reach `:8080` / `:8443`. Findings that require an
+ authenticated peer collapse to "authenticated user with RBAC privilege
+ X", and must additionally either clear RBAC for the harmful command or
+ bypass it.
+- **Web UI**: same shape as the JSON API; the Vue.js SPA is a presentation
+ layer over the API.
+- **Cluster RPC (B4)**: reachable from a peer that has cleared the Root CA
+ trust check. A flat "cluster RPC has no auth" finding is `OUT-OF-MODEL:
+ adversary-not-in-scope` because the model *requires* the Root CA to be
+ enrolled across peers; a *cleartext*/un-certed cluster RPC finding is
+ gated by `ca.plugin.root.auth.strictness`, which defaults to `true` on
+ new setups (see §5a).
+- **Management ↔ agent (B5)**: reachable from a peer that presents a
+ Root-CA-signed certificate the management server accepts. By default on
+ new setups `ca.plugin.root.auth.strictness = true`, so the management
+ server **does require** a client certificate from the connecting agent
+ *(maintainer: vishesh92 —
+ `https://github.com/apache/cloudstack/pull/2239`)*. The value remains
+ `false` only when upgrading from versions released before Aug 2017 that
+ predate the setting; that upgrade case is documented in the upgrade
+ instructions and is therefore not a concern *(maintainer: DaanHoogland)*
+ *(documented: `plugins/ca/root-ca/.../RootCAProvider.java`,
+ `RootCACustomTrustManager.java`)*.
+- **Console proxy (B3)**: reachable by anyone who holds a valid signed
+ token. The token is the entire authorization gate.
+- **Agent → hypervisor (B7)**: reachable only on the agent host, by code
+ the agent runs.
+- **External integrations (B6)**: reachable from the management server's
+ outbound posture; a hostile external service is `OUT-OF-MODEL:
+ trusted-input` (§3 item 2).
+
+## §5 Assumptions about the environment
+
+- **Operating system (management server / usage server)**: RHEL 8/9/10,
+ CentOS 8/9, Rocky 9/10, Ubuntu 22.04/24.04, SUSE 15, openSUSE Leap 15;
+ Java 17 (`README.md`, `INSTALL.md`,
`packaging/{el8,el9,el10,debian,suse15}`).
+- **Operating system (agent)**: same family on KVM/baremetal hosts;
+ agent ships as `cloudstack-agent` package *(documented: `debian/`,
+ `packaging/`)*.
+- **Database**: MariaDB or MySQL-compatible, accessible from each
+ management-server instance; CloudStack uses native DB encryption +
+ JaSypt for application-level secrets *(documented: `README.md`
+ "Notice of Cryptographic Software")*.
+- **Cryptography**: JaSypt (application-secret encryption), Bouncy Castle
+ (general-purpose crypto, X.509 issuance in the Root CA provider), JSch
+ (SSH client to system VMs), OpenSwan (optional VPN endpoint termination)
+ *(documented: `README.md` Cryptographic Software notice)*.
+- **Network**: operator-controlled L2/L3 with at least the management
+ network, public network, guest network, and storage network as logical
+ fabrics *(documented: CloudStack admin documentation; inferred —
+ §14 Q13)*. The management network is the trusted control-plane
+ network; the guest network carries untrusted guest VM traffic.
+- **Time**: signature version 3 enforces an `expires` parameter on signed
+ API requests *(documented: `ApiServer.java` line ~1054)*; this assumes
+ loosely-synchronized clocks between client and management server
+ *(inferred — §14 Q14)*.
+- **Filesystem**: the JCEKS keystore, `db.properties`, `server.properties`,
+ and Root CA private key are stored under `/etc/cloudstack/management/`
+ with OS-level permissions restricted to the `cloudstack` user
+ *(inferred — §14 Q15)*.
+- **Hypervisor**: each supported hypervisor is assumed to provide its own
+ guest isolation (memory, vCPU, disk, network) and to expose a stable
+ admin API (libvirt for KVM, vSphere SDK for VMware, XenAPI for
+ XenServer/XCP-ng, WinRM/Hyper-V API for Hyper-V).
+- **What CloudStack does to its host** (negative claims, awaiting
+ maintainer ratification):
+ - **does** open listening sockets on documented ports
+ (`:8080`/`:8443`/`:8250`/`:8096`/`:9090`/console-proxy ports)
*(documented)*;
+ - **does** maintain MariaDB connections from the management server;
+ - **does** issue X.509 certificates from its self-signed Root CA
*(documented:
+ `plugins/ca/root-ca/.../RootCAProvider.java`)*;
+ - **does** spawn child processes from the agent (`Script` invocations
+ against `/usr/share/cloudstack-common/scripts/`) *(documented:
+ `agent/src/main/java/com/cloud/agent/Agent.java` `keystoreSetupSetupPath`,
+ `keystoreCertImportScriptPath`)*;
+ - **does** write logs under operator-configured locations;
+ - **does** read a documented set of environment variables and the
+ `db.properties` file at startup *(inferred — §14 Q16)*;
+ - **does** install signal handlers / shutdown hooks only as
+ Tomcat/Jetty servlet container default *(inferred — §14 Q16)*.
+
+## §5a Build-time and configuration variants
+
+CloudStack ships as a family of packages: 'cloudstack-agent',
'cloudstack-baremetal-agent', 'cloudstack-common',
'cloudstack-integration-tests', 'cloudstack-management', 'cloudstack-marvin',
'cloudstack-mysql-ha', 'cloudstack-ui', 'cloudstack-usage'
+*(documented: `debian/`, `packaging/`)*. A sizable number of runtime
+configuration knobs materially change the security envelope. The
+security-relevant subset:
+
+| Knob | Default | Maintainer stance | Effect |
+| --- | --- | --- | --- |
+| `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 [...]
+| `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` | `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 *(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. |
+| `enforce.post.requests.and.timestamps` | per
`isPostRequestsAndTimestampsEnforced` *(documented: `ApiServer.java`; setting
name maintainer: vishesh92)* | bounds `expires` to a maximum future offset |
Prevents an attacker who steals a signed URL with a 10-year expiration from
using it forever. |
+| `integration.api.port` (`:8096`) | typically disabled *(inferred — §14 Q20)*
| When non-zero, exposes an *unauthenticated* admin API for integration testing
| An open integration port is a complete RBAC bypass on the management server. |
+| Hypervisor enablement (which `plugins/hypervisors/*` are installed and
configured) | per zone | operator-driven | An unused hypervisor plugin still
ships but is not connected to any host. |
+| Hostname / SAN of management-server cert
(`ca.framework.cert.management.custom.san`) | unset *(maintainer: vishesh92)* |
when set, included in the auto-generated cert SAN | governs which hostnames
clients can use to reach the management server. |
+| SAML2 / OAuth2 enablement (`plugins/user-authenticators/{saml2,oauth2}`) |
off *(inferred — §14 Q19)* | turning on adds an external IdP trust dependency |
adds B6 transitions. |
+| LDAP enablement (`plugins/user-authenticators/ldap`) | off *(inferred — §14
Q19)* | turning on adds an external LDAP trust dependency | adds B6
transitions. |
+
+**The Root-CA strictness default (resolved).** Earlier drafts treated
+`ca.plugin.root.auth.strictness = false` as the shipped default and the
+single highest-leverage open question. The PMC has clarified that **new
+setups default to `true`** — the management server *does* require a
+Root-CA-signed client cert on `:8250` and the cluster ports — and the
+value is `false` **only** when upgrading from versions released before
+Aug 2017 that predate the setting *(maintainer: vishesh92 —
+`https://github.com/apache/cloudstack/pull/2239`)*. That upgrade case is
+documented in the upgrade instructions, so a leftover `false` after such
+an upgrade is an operator-hardening/upgrade-hygiene item, not a shipped
+insecure default *(maintainer: DaanHoogland)*. A report against an open
+`:8250` accepting an un-certed peer on a **new** install is therefore
+`MODEL-GAP`/`VALID` (strictness should be on), whereas the same on an
+**upgraded** pre-2017 install is `OUT-OF-MODEL: non-default-build`
+(documented upgrade step not applied). `ca.plugin.root.allow.expired.cert`
+remains `true` as an operational concession to cert-rotation lag.
+
+## §6 Assumptions about inputs
+
+### Per-endpoint trust table (network surfaces)
+
+| Surface / route | Parameter | Attacker-controllable? | Caller must enforce |
+| --- | --- | --- | --- |
+| Management server `:8080`/`:8443` JSON API | command name + params | **yes**
| nothing — CloudStack parses, authenticates (B1), applies RBAC, dispatches |
+| Management server `:8080`/`:8443` JSON API | `signature` parameter | **yes**
| HMAC-SHA1 verified *constant-time* against expected signature *(documented:
`ApiServer.java` line 1137 `ConstantTimeComparator.compareStrings`)* |
+| Management server `:8080`/`:8443` JSON API | `expires` parameter (sig v3) |
**yes** | rejected if past, or beyond the
`enforce.post.requests.and.timestamps` ceiling *(documented: same file; setting
name maintainer: vishesh92)* |
+| Management server `:8080`/`:8443` JSON API | proxy-set forward headers
(`proxy.header.names`) | **yes** if `proxy.header.verify=true` | honoured
**only** if the connecting `Remote_Addr` ∈ `proxy.cidr` *(documented:
`ApiServlet.java` `getClientAddress`; setting names maintainer: vishesh92)* |
+| Management server `:8080`/`:8443` Web UI | session cookie | **yes** |
session-fixation / invalidation handled via `invalidateHttpSession` on auth
failure *(documented: `ApiServlet.java` line 418)* |
+| Integration API `:8096` (if enabled) | command name + params | **yes** |
**no signature check** — integration port is unauthenticated by design |
+| Management ↔ agent `:8250` | NIO Thrift-like payload | **only by a peer that
has cleared B5 trust** | client cert via `RootCACustomTrustManager` |
+| Management ↔ cluster peer | NIO payload | **only by a peer that has cleared
B4 trust** | client cert via `RootCACustomTrustManager` |
+| Console proxy URL | encrypted token (containing VM identity + endpoint +
duration) | **yes** | token MUST decrypt + verify with
`ConsoleProxyPasswordBasedEncryptor` keys *(documented:
`ConsoleProxyPasswordBasedEncryptor.java`)* |
+| Secondary-storage HTTP download URL | UUID-named symlink path | **yes** |
**no auth on the download link**; the UUID format is the anti-enumeration
control and the symlink is removed after a period — timed availability of the
download token is the mitigation *(maintainer: vishesh92, DaanHoogland)* |
+| Template / ISO upload | URL of remote source | **yes** within RBAC |
upload-gated by `registerTemplate` RBAC; bytes are then served to hypervisors
as image data |
+| User-data / metadata service (`169.254.169.254` from inside guests) |
guest-controlled bytes (the request) | **yes from the guest**, but the service
is reached *from the guest* and serves only that guest's data | guest-VM-side
isolation by virtual router |
+| Hypervisor agent log / event stream | bytes from hypervisor | trusted
operator surface | none — assumed truthful |
+| LDAP / SAML / OAuth response (B6) | bytes from IdP | trusted | LDAP queries
treat returned attributes as authoritative |
+| Storage response (B8) | bytes / metadata from storage | trusted | bytes are
object content; envelope is control-plane |
+
+### Size / shape / rate
+
+- CloudStack does not document a maximum signed-API request size; assumed
+ to be servlet-container default (Jetty / Tomcat) *(inferred — §14 Q21)*.
+- API rate limiting is per-account via the global config knobs
`api.throttling.*`
+ *(inferred — §14 Q22)*; an attacker with a valid API key can be rate-
+ limited at the application layer.
+- Template / ISO upload size is bounded by storage capacity and per-account
+ resource limits *(inferred — §14 Q22)*; pathological compressed-image
+ inputs (e.g. extremely compressible QCOW2 with sparse holes that expand
+ to TB on extraction) are robustness concerns *(inferred — §14 Q23)*.
+- Cluster-peer and agent RPC payload sizes: no documented application-layer
+ cap; NIO framing applies *(inferred — §14 Q21)*.
+
+## §7 Adversary model
+
+### Actors
+
+| Actor | In scope? | Capabilities granted |
+| --- | --- | --- |
+| Unauthenticated network peer reaching `:8080`/`:8443` | **yes** | TCP to the
listening ports; may attempt authentication; may attempt to violate the
protocol pre-auth |
+| Unauthenticated peer reaching `:8250` (agent port) | **only if**
`ca.plugin.root.auth.strictness = false`, which on new setups it is **not**
(default `true`); `false` arises only on un-remediated pre-Aug-2017 upgrades
(§5a) | TCP to the listening port; may attempt to connect as a peer without
presenting a cert |
+| Unauthenticated peer reaching `:8096` (integration port) | **yes** *if* the
port is open (typically not in production) | full unauthenticated admin API |
+| Authenticated end user with limited RBAC role | **yes** | call APIs their
role permits; manage VMs/networks/storage in their domain/account/project |
+| Authenticated end user with broad RBAC role | partial | only RBAC-envelope
escapes are in scope |
+| Authenticated domain admin | **yes** | full management within their domain;
cross-domain leakage is in scope |
+| Authenticated root admin | **out of scope** — see §3 item 4 | unbounded by
design |
+| Co-tenant (different account in same domain or different domain on same
CloudStack) | **yes** | cross-tenant leakage (VM ID guessing, network bleed,
storage bleed, template visibility) is in scope |
+| Guest VM workload | **partial** | hypervisor-mediated; out-of-scope for
hypervisor isolation bugs (§3 item 5), in-scope for the orchestration that
placed the VM (security-group rule application, VLAN tagging, public IP
routing) |
+| Browser holding a valid console-proxy URL | **yes** | the URL is a bearer
credential; scope of harm is one VM's console for the URL's lifetime |
+| Operator | **out of scope** | see §3 item 1 |
+| Hostile hypervisor | **out of scope** | see §3 item 3 |
+| Hostile LDAP / SAML / OAuth IdP, hostile NSX/Netscaler/Tungsten, hostile S3
endpoint | **out of scope** | see §3 item 2 |
+| Reverse proxy that should be trusted but is not in `proxy.cidr` | **out of
scope** | its forward headers are not honoured |
+| Local process on the management-server host running as a different UID |
**partial** *(inferred — §14 Q24)* | same-host attackers with non-cloudstack
UID can reach `:8080` unless host firewalling forbids; CloudStack does not
defend against same-host `root` |
+| Side-channel observer (cache timing, network timing, hypervisor side
channels) | **out of scope** *(inferred — §14 Q25)* | n/a |
+| Quantum adversary | **out of scope** | n/a |
+
+### Authenticated-but-Byzantine peer (distributed-systems threshold)
+
+CloudStack is **not** a Byzantine-fault-tolerant system. A compromised
+management-server cluster peer with a valid Root-CA-issued cert can
+schedule arbitrary work onto the agent fleet, read any guest's data, and
+hand out console-proxy tokens. The cluster trusts its own membership
+*(inferred — §14 Q26)*. Likewise, a compromised agent host can serve
+malicious data on the management network and produce wrong status. →
+reports requiring a Byzantine internal peer are `OUT-OF-MODEL:
+adversary-not-in-scope`.
+
+## §8 Security properties the project provides
+
+For each property: condition, violation symptom, severity tier, provenance.
+
+### P1 — Authentication of API clients via signed request
+
+- **Condition**: a request carries `apiKey` + `signature` (and, for
+ signature version 3, an `expires` parameter not in the past)
+ *(documented: `ApiServer.java` `verifyRequest`)*; the signature is
+ HMAC-SHA1 of the canonical parameter string under the per-user
+ secret key, base64-encoded, lowercased, URL-decoded, and compared
+ to the computed value using `ConstantTimeComparator.compareStrings`
+ *(documented: same file line 1137)*.
+- **Violation symptom**: a request executes API commands without a
+ valid `apiKey`+`signature` pair (and without a valid session
+ cookie / SAML / OAuth / LDAP login).
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P2 — Session authentication via password + optional 2FA
+
+- **Condition**: user logs in via the `login` API; 2FA is verified after
+ password if enabled for the user / domain *(documented: `ApiServlet.java`
+ lines 360–582)*.
+- **Violation symptom**: a session is created without a valid password, or
+ 2FA enforcement is bypassed for a user where it is mandated.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P3 — Constant-time signature comparison
+
+- **Condition**: applies to the API signature check.
+- **Violation symptom**: timing-side-channel measurement of signature
+ comparison reveals the expected signature byte-by-byte.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented: `ApiServer.java` line 1137)*
+
+### P4 — Authorization via RBAC + domain/account/project hierarchy
+
+- **Condition**: the authenticated principal calls an API command, and the
+ command name is permitted for their role *(documented:
`plugins/acl/{static,dynamic,project}-role-based`)*;
+ resources named in the request belong to the principal's
domain/account/project
+ or to a child within the principal's scope.
+- **Violation symptom**: a non-root principal successfully executes an
+ API command not licensed for their role, or operates on a resource
+ outside their domain/account/project scope.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P5 — Mutual TLS on management ↔ agent, management ↔ cluster peer, *when
configured*
+
+- **Condition**: `ca.plugin.root.auth.strictness = true` — **the default
+ on new setups** *(maintainer: vishesh92 —
+ `https://github.com/apache/cloudstack/pull/2239`)*. Pre-Aug-2017
+ upgrades may leave it `false` until the documented upgrade step is
+ applied *(maintainer: DaanHoogland)*. `ca.plugin.root.allow.expired.cert`
+ remains `true` (cert-rotation concession), so the property covers
+ *peer-cert presence and Root-CA chain*, not cert freshness.
+- **Violation symptom**: a peer without a Root-CA-issued cert successfully
+ completes a session on `:8250` or the cluster port on a setup where
+ strictness is on.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented; default resolved by maintainer.)*
+
+### P6 — Reverse-proxy IP-trust gating for forward headers
+
+- **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
+ names maintainer: vishesh92)*.
+- **Violation symptom**: a request from a source IP **outside**
+ `proxy.cidr` succeeds with an attacker-supplied forward header
+ taking effect.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P7 — Console-proxy token confidentiality and integrity
+
+- **Condition**: tokens are encrypted under the
+ `ConsoleProxyPasswordBasedEncryptor` keys *(documented:
+ `ConsoleProxyPasswordBasedEncryptor.java`)*; a token includes the VM
+ identity, the hypervisor endpoint, and a duration / expiry.
+- **Violation symptom**: a third party with no console-access RBAC
+ privilege forges or decrypts a token to gain console access; or a token
+ remains valid past its declared expiry.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P8 — Application-secret encryption at rest in the DB via JaSypt
+
+- **Condition**: `security.encryption.key` + `security.encryption.iv` are
+ initialised at first boot and kept under filesystem ACLs
+ *(documented: `framework/security/.../KeysManager.java`,
+ `README.md` Cryptographic Software notice)*.
+- **Violation symptom**: an attacker with read access to the DB but not
+ to the encryption key file recovers plaintext for secrets the model
+ claims are encrypted (typically: external service passwords, account
+ API secret keys when stored encrypted).
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P9 — Memory safety on well-formed inputs across documented surfaces
(JVM-bounded)
+
+- **Condition**: input matches the documented protocol on B1–B5; the JVM
+ is conformant; native code is invoked only via documented hypervisor
+ SDKs (libvirt / vSphere / XenAPI). CloudStack presumes **no limitation
+ on implementation language** — ocaml, python and bash run on hypervisors
+ and go is used on the management server (the set may grow); the
+ memory-safety claims here hold for the **JVM components**, to which the
+ JVM-conformance condition applies *(maintainer: DaanHoogland — §14 Q27)*.
+- **Violation symptom**: heap corruption, OOM-via-input-size attack on a
+ surface where the input source is `:8080` / `:8443` / B5; JVM-side
+ crashes from a request a normally-RBAC'd user could send.
+- **Severity**: **security-critical** when reachable from network input;
+ **`VALID-HARDENING`** when reachable only by a writer who already
+ controls the bytes (§3 item 5).
+- *(maintainer: DaanHoogland — §14 Q27)*
+
+### P10 — Bounded RBAC scope of cross-domain visibility (`SHOW`-equivalent
listing)
+
+- **Condition**: `list*` API commands filter responses to the principal's
+ domain/account/project scope per `plugins/acl/` policy.
+- **Violation symptom**: a `list*` response leaks resource IDs / names /
+ metadata for resources outside the principal's RBAC scope.
+- **Severity**: **security-critical** for resources whose existence is
+ itself confidential (typically: customer VM names, custom template
+ names); `VALID` per §13.
+- *(inferred — §14 Q28)*
+
+## §9 Security properties the project does *not* provide
+
+State each plainly so a triager can route an inbound report to the matching
+disclaimer.
+
+- **No defence against the operator.** Anyone with root on a
+ management-server host, the JCEKS keystore + `security.encryption.key`,
+ the Root CA private key, or the MariaDB credentials wins. See §3 item 1
+ *(inferred — §14 Q6)*.
+- **No defence against a malicious external service the operator
+ configured.** A hostile LDAP/SAML/OAuth IdP, NSX controller, Tungsten,
+ Netscaler, S3 endpoint, or backup provider is trusted. See §3 item 2.
+- **No defence against the hypervisor.** Guest VM escape via libvirt,
+ vSphere, XenAPI, Hyper-V is upstream. See §3 item 3.
+- **No isolation between a root admin's API call and the management-server
+ process.** Root admin can register arbitrary plugins, upload arbitrary
+ templates, run `runCustomAction`. See §3 item 4 *(inferred — §14 Q8)*.
+- **No sandbox for guest VM workloads beyond what the hypervisor provides.**
+ Side-channel leaks between co-tenant VMs (cache, branch, memory bus,
+ shared GPU) are out of scope. See §3 item 5 *(inferred — §14 Q9)*.
+- **No sandbox for user-data / templates / ISOs.** Templates run as their
+ own OS image with their own cloud-init; CloudStack does not parse or
+ reject user-data semantics. See §3 item 6 *(inferred — §14 Q10)*.
+- **No defence against decompression / decoding bombs in uploaded
+ templates / ISOs.** A pathological QCOW2 / RAW image can consume
+ arbitrary CPU / disk on extraction; per-account resource limits are the
+ bound *(inferred — §14 Q23)*.
+- **No defence against intra-cluster Byzantine failure.** A compromised
+ cluster peer with a valid Root-CA-issued cert can read any data the
+ cluster can read; see §7 *(inferred — §14 Q26)*. Likewise a compromised
+ agent host.
+- **No data-at-rest encryption beyond JaSypt for selected DB columns +
+ whatever storage layers provide.** Guest volumes are encrypted only if
+ the primary-storage plugin supports it (Ceph RBD encryption, LUKS at
+ hypervisor layer) and the operator has configured it *(inferred —
+ §14 Q29)*.
+- **No defence against side-channel observation** of API request timing,
+ agent RPC timing, or memory access patterns *(inferred — §14 Q25)*.
+- **No application-layer constant-time comparison of anything other than
+ the API signature.** Login password comparison, session cookie
+ comparison, console-token comparison — not documented constant-time
+ *(inferred — §14 Q30)*.
+- **No defender stance against an attacker on the same Linux host running
+ as a non-`cloudstack` UID** — CloudStack defends only across the
+ network surface; same-host attackers with shell access on the
+ management-server host already have many paths to win *(inferred —
+ §14 Q24)*.
+- **No supported posture for the integration API port (`:8096`).** When
+ open, it is an unauthenticated admin surface; closing it is the
+ operator's job *(inferred — §14 Q20)*.
+
+### False-friend properties (call out separately)
+
+- **The Root CA is self-signed and auto-generated** — it is *not* a
+ publicly-trusted CA. Browsers and external clients require manual trust
+ bootstrap. The Root CA private key resides on the management server; a
+ compromised management server compromises the entire agent fleet's
+ trust.
+- **`ca.plugin.root.auth.strictness = false` is not "TLS off" — it is
+ "client cert not required"** *(documented: `RootCAProvider.java`)*. TLS
+ on the wire is still there; what is missing is the peer-cert check.
+ Note the value is `true` on new setups *(maintainer: vishesh92)*; a
+ scanner that flags "client cert not requested" is only correct on an
+ un-remediated pre-Aug-2017 upgrade, and even then it identifies a
+ documented upgrade step, not a transport-encryption bug.
+- **`ca.plugin.root.allow.expired.cert = true` is the operational default
+ to survive cert-rotation lag** but is not a security boundary.
+- **The HMAC-SHA1 signature is request-integrity over the URL, not
+ request encryption.** Transport encryption is TLS; if the operator
+ serves the API over `http://`, the signature still validates but the
+ whole request (including the secret-derived signature) is visible to
+ the network.
+- **The console-proxy URL is a bearer credential.** Anyone who sees the
+ URL (in logs, in a proxy, in a shoulder-surf) holds the console for
+ the URL's lifetime.
+- **`list*` filtering is a per-call authorization view, not an
+ information-flow channel.** Existence of a resource that the principal
+ cannot see may leak through error messages, async-job status, event
+ logs, or by-ID lookup probing *(inferred — §14 Q28)*.
+- **The integration API port is not a "trusted" port in the sense of
+ Kerberos `auth-int` — it is *no authentication at all***. The name
+ invites confusion.
+- **JaSypt-encrypted DB columns are *(documented)* protected against a
+ DB-only read.** They are *not* protected against an attacker who
+ obtains both the DB and the encryption-key file.
+
+### Well-known attack classes the project does not defend against
+
+- **Cross-tenant VM-ID guessing / template-name enumeration**: §10 misuse,
+ not engine breakage.
+- **Decompression / decoding bombs in uploaded templates and ISOs**.
+- **Hypervisor side-channel attacks between co-tenant VMs**.
+- **Confused-deputy between RBAC role and resource ownership** — e.g. a
+ domain admin's role permits a command, but the resource named is in a
+ child domain they should not touch *(inferred — §14 Q28)*.
+- **Time-of-check-to-time-of-use** between RBAC check at API entry and
+ the actual orchestration on the agent fleet — policy revocations
+ mid-job are not retroactively enforced *(inferred — §14 Q31)*.
+
+## §10 Downstream responsibilities
+
+The operator deploying CloudStack in production **must**:
+
+1. Keep `ca.plugin.root.auth.strictness = true` (the default on new
+ setups). When **upgrading from a pre-Aug-2017 version**, follow the
+ documented upgrade step to turn strictness on — otherwise agent and
+ cluster-peer ports accept peers without a cert *(maintainer: vishesh92,
+ DaanHoogland — `https://github.com/apache/cloudstack/pull/2239`)*.
+ Consider tightening `ca.plugin.root.allow.expired.cert` (default `true`)
+ once cert rotation is reliable.
+2. Restrict the management network at L2/L3 so that `:8250` (agent),
+ `:9090` (cluster), and the MariaDB port are reachable only from the
+ intended peers *(inferred — §14 Q13)*.
+3. Restrict the integration API port `:8096` — either disable it entirely
+ or limit it to a localhost/management subnet *(inferred — §14 Q20)*.
+4. Terminate TLS for the JSON API and Web UI on `:8443` (not `:8080`); if
+ `:8080` is exposed at all, only behind a TLS-terminating reverse
+ proxy *(inferred — §14 Q32)*.
+5. When using a reverse proxy, set `proxy.header.verify = true`,
+ `proxy.header.names` to the forward header(s) the proxy sets, *and*
+ `proxy.cidr` to the proxy's CIDR — leaving `proxy.cidr` unset/empty
+ means the header is ignored (safe-default per P6), but a misconfigured
+ wide CIDR is a trust-bypass *(setting names maintainer: vishesh92)*.
+6. Protect the `security.encryption.key` / `security.encryption.iv`
+ files, the JaSypt-encrypted DB, the Root CA private key, and the
+ `cloudstack-management` Unix user's home directory at OS level.
+7. Keep the password-encoder configuration at safe defaults:
+ `user.password.encoders.order` defaults to
+ `PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT` (so PBKDF2 is used to hash
+ new passwords) and `user.password.encoders.exclude` defaults to
+ `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 — 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 — 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 —
+ scan / quarantine before allowing into the supported-template set.
+11. Apply per-account resource limits (vCPU / RAM / volume size / image
+ size) to bound decompression-bomb and orchestration-DoS attacks.
+12. Configure storage-layer encryption (Ceph RBD encryption, LUKS at KVM,
+ vSphere VM Encryption, etc.) if data-at-rest encryption is required.
+13. Secure each `cloudstack-agent` host: `cloudstack` Unix user, agent
+ keystore under `/etc/cloudstack/agent/`, root account, libvirt /
+ vSphere admin credentials.
+14. Restrict console-proxy URLs: do not log them, do not embed them in
+ public responses, set a short token lifetime.
+15. Audit API call logs (via the event-bus plugin) for anomalous patterns.
+
+## §11 Known misuse patterns
+
+- **Leaving `:8250` open to the world with
`ca.plugin.root.auth.strictness=false`
+ on an upgraded pre-Aug-2017 cluster.** New setups default to `true`;
+ the `false` value only survives an upgrade where the documented step
+ was skipped *(maintainer: vishesh92, DaanHoogland)*. In that state any
+ peer can connect as an agent — an upgrade-hygiene gap, dispositioned
+ `OUT-OF-MODEL: non-default-build` (documented upgrade step not applied).
+- **Exposing `:8096` (integration API) publicly.** Anyone reaching the
+ port executes admin API commands without auth.
+- **Exposing `:8080` (HTTP JSON API) publicly without a TLS-terminating
+ reverse proxy.** Signed-request integrity holds, but the API secret-
+ key-derived signature is visible to any wire observer; replay within
+ the `expires` window is trivial.
+- **Setting `proxy.header.verify=true` with `proxy.cidr` wider than
+ the actual reverse-proxy CIDR.** An attacker outside the proxy can
+ spoof a `proxy.header.names` header and claim any IP address for audit
+ logs and authentication-IP checks *(setting names maintainer: vishesh92)*.
+- **Removing `MD5`/`PLAINTEXT` from `user.password.encoders.exclude` (or
+ 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 — §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
+ chat.** Tokens are bearer credentials.
+- **Reusing `security.encryption.key` across environments of different
+ trust levels.** A staging-env leak becomes a production-env decrypt
+ primitive *(inferred — §14 Q33)*.
+- **Leaving `ca.plugin.root.auth.strictness=false` after a pre-Aug-2017
+ upgrade in a multi-management-server deployment.** A peer can join the
+ cluster without a cert until the documented upgrade step flips it to the
+ new-setup default of `true` *(maintainer: vishesh92, DaanHoogland)*.
+- **Uploading large or pathological templates and relying on hypervisor
+ to enforce size.** Per-account resource limits, not the engine, are the
+ enforcement.
+
+## §11a Known non-findings (recurring false positives)
+
+This section is the highest-leverage input for automated agentic security
+scans. Each entry: tool symptom, why it is safe under the model, the §
+that licenses the call.
+
+- **"Management ↔ agent port `:8250` accepts no client cert" reported
+ against a setup with `ca.plugin.root.auth.strictness=false`.** New setups
+ default to `true` and **do** require a Root-CA-signed client cert
+ *(maintainer: vishesh92 — `https://github.com/apache/cloudstack/pull/2239`)*.
+ The value is `false` only on an upgrade from a pre-Aug-2017 version that
+ predates the setting, and the upgrade instructions document turning it on
+ *(maintainer: DaanHoogland)*. → On a new install: `KNOWN-NON-FINDING`
+ (strictness is on). On an upgraded install with the step skipped:
+ `OUT-OF-MODEL: non-default-build` (documented upgrade step not applied).
+- **"Integration port `:8096` is unauthenticated."** The port is
+ unauthenticated by design; operator responsibility per §10 to close /
+ bind to localhost. → `OUT-OF-MODEL: non-default-build` once the PMC
+ confirms.
+- **"HMAC-SHA1 signature uses SHA1."** SHA1-HMAC is **not** broken for
+ HMAC use; collision attacks on SHA1 do not extend to HMAC-SHA1
+ *(documented: cryptographic literature; CloudStack uses
+ `Mac.getInstance("HmacSHA1")` — `ApiServer.java` line 1130)*. →
`KNOWN-NON-FINDING`.
+- **"Constant-time string compare for the signature."** Already done —
+ `ConstantTimeComparator.compareStrings` per `ApiServer.java` line 1137.
+ → `KNOWN-NON-FINDING` (a finding flagging this is wrong).
+- **"Root CA private key is on the management server."** By design — the
+ management server *is* the CA. → `BY-DESIGN: property-disclaimed`.
+- **"Self-signed Root CA cert."** By design — the CA is generated at
+ first boot per `RootCAProvider.java`. Browsers will warn until the
+ operator bootstraps trust. → `BY-DESIGN: property-disclaimed`.
+- **"Expired agent cert is accepted
(`ca.plugin.root.allow.expired.cert=true`)."**
+ Documented default — an operational concession to cert-rotation lag, paired
+ with the strictness default *(maintainer: vishesh92, DaanHoogland)*. →
+ `VALID-HARDENING` at most; tightening it is an operator choice per §10.
+- **"Hardcoded password / keytab in `tools/marvin/`, `test/`, `developer/`,
+ `quickcloud/`."** These directories are unsupported components per §3
+ item 7. → `OUT-OF-MODEL: unsupported-component`.
+- **"User-data / template contents execute arbitrary code in the guest
+ VM."** Templates are run as their own OS by the hypervisor; cloud-init
+ / user-data is intentionally a code-execution channel into the guest.
+ → `BY-DESIGN: property-disclaimed` per §9.
+- **"Root admin can change global config / register plugins / upload
+ arbitrary templates."** Documented and intentional. → `BY-DESIGN:
+ property-disclaimed` per §9 / §3 item 4.
+- **"DoS via expensive list call on a large CloudStack."** Pagination is
+ present; further bounds are admission-control / quota. → `BY-DESIGN:
+ property-disclaimed` per §9.
+- **"Decompression bomb in an uploaded QCOW2 / template."** Per-account
+ resource limits are the bound. → `VALID-HARDENING` at most, unless the
+ decompression reaches §8 P9 memory-safety violations.
+- **"Vendored Bouncy Castle / JaSypt / noVNC / `pako` has CVE-X."** Report
+ upstream; `systemvm/agent/noVNC` is a vendored fork of
+ `github.com/novnc/novnc` with CloudStack changes, and there is no
+ automated sync procedure today *(maintainer: vishesh92, DaanHoogland)*. →
+ `OUT-OF-MODEL: unsupported-component` (upstream pointer); a
+ CloudStack-introduced change *to* the fork is in-model.
+- **"Secondary-storage download URL has no authentication / can be replayed."**
+ By design: download links are UUID-named symlinks served by an Apache
+ httpd with no auth on the link; the UUID format defeats enumeration and
+ the symlink is removed after a period, so timed availability is the
+ mitigation *(maintainer: vishesh92, DaanHoogland)*. → `BY-DESIGN:
+ property-disclaimed` for the no-auth aspect; a link that is *not* removed
+ after its window, or a guessable (non-UUID) name, is `VALID-HARDENING`.
+- **"A proxy-set forward header is honoured without authentication."**
+ Honoured only if (a) `proxy.header.verify=true`, (b) the header is one of
+ `proxy.header.names`, *and* (c) the connecting `Remote_Addr` ∈
+ `proxy.cidr` *(setting names maintainer: vishesh92)*. → `KNOWN-NON-FINDING`.
+- **"Session-fixation: a session ID is reusable after failed login."**
+ `invalidateHttpSession` is called on each auth failure path per
+ `ApiServlet.java`. → `KNOWN-NON-FINDING` (verify the symptom; if
+ reproducible, escalate to `MODEL-GAP`).
+
+## §12 Conditions that would change this model
+
+Revise this document when any of the following lands:
+
+- A new authentication mechanism on a client-facing surface (e.g.
+ mTLS-as-API-auth on the JSON API, WebAuthn, OIDC).
+- A new RBAC backend beyond the three included ACL plugins (e.g. OPA
+ integration, policy-engine integration).
+- A new data-at-rest encryption story at the CloudStack layer (currently
+ delegated; see §9).
+- A change in the default of any §5a flag, *especially*
+ `ca.plugin.root.auth.strictness` and `ca.plugin.root.allow.expired.cert`.
+- Removal or change of the legacy `md5` / `plain-text` user-authenticator
+ plugins.
+- A change in the signing algorithm or signature scheme on the JSON API
+ (e.g. SHA1 → SHA256 by default).
+- A new hypervisor or system VM that adds a new trust boundary.
+- A change in the extension mechanisms implemented by CloudStack
+ *(maintainer: DaanHoogland — §14 Q36)*.
+- A new external-data surface (a new SDN controller integration, a new
+ storage provider, a new backup provider).
+- A vulnerability report that cannot be cleanly routed to one of the §13
+ dispositions: that is evidence the model is incomplete.
+
+## §13 Triage dispositions
+
+A report against `apache/cloudstack` receives exactly one of the
+following:
+
+| Disposition | Meaning | Licensed by |
+| --- | --- | --- |
+| `VALID` | Violates a §8 property via an in-scope §7 adversary using an
in-scope §6 input. | §8, §6, §7 |
+| `VALID-HARDENING` | No §8 property violated, but a §11 misuse pattern can be
made harder to fall into by code change. Fixed at maintainer discretion,
typically no CVE. | §11 |
+| `OUT-OF-MODEL: trusted-input` | Requires attacker control of a §6 parameter
the model marks trusted (e.g. operator-supplied config flag, hostile
LDAP/SAML/NSX/etc.). | §6 |
+| `OUT-OF-MODEL: adversary-not-in-scope` | Requires a §7 actor the model
excludes (operator, hostile hypervisor, hostile external IdP / SDN, Byzantine
peer, side-channel observer, same-host non-`cloudstack` `root`). | §7 |
+| `OUT-OF-MODEL: unsupported-component` | Lands in `tools/marvin/`, `test/`,
`developer/`, `quickcloud/`, vendored upstream code, `simulator` hypervisor,
etc. | §3 items 7–8 |
+| `OUT-OF-MODEL: non-default-build` | Only manifests under a §5a flag that is
not the new-setup default (e.g. `ca.plugin.root.auth.strictness=false`
surviving an un-remediated pre-Aug-2017 upgrade, integration port `:8096`
open). | §5a |
+| `OUT-OF-MODEL: equivalent-harm` | An actor already-authorized under the
model can cause the same harm via a documented path (root admin doing
root-admin things, RBAC-licensed user using their RBAC-licensed commands). | §3
items 4, 5 |
+| `BY-DESIGN: property-disclaimed` | Concerns a §9 property the project
explicitly does not provide (template sandboxing, side-channel resistance,
hypervisor isolation, etc.). | §9 |
+| `KNOWN-NON-FINDING` | Matches a §11a recurring false positive. | §11a |
+| `MODEL-GAP` | Cannot be cleanly routed to any of the above — triggers §12
model revision. | §12 |
+
+## §14 Open questions for the maintainers
+
+Every *(inferred)* tag in the body maps to one of these. Proposed answers
+are inline; please confirm, correct, or strike.
+
+### Wave 1 — scope, intended use, the two big insecure defaults
+
+**Q1.** ~~The model assumes CloudStack is "a clustered distributed
+control plane deployed inside an operator-controlled datacenter
+network", not a single-host appliance or a hosted SaaS. Confirm?~~
+**RESOLVED** *(maintainer: DaanHoogland)* — distributed control plane;
+**both** a single management-server instance (smaller clouds) and a
+clustered deployment are supported topologies. Folded into §2.
+
+**Q2.** ~~Are the SecondaryStorageVM, ConsoleProxyVM, and Virtual Router
+treated as trusted-once-enrolled peers, or do they get their own trust
+tier?~~ **RESOLVED** *(maintainer)* — **yes**, same trust tier as agents,
+not a separate tier. Folded into §2 caller-roles.
+
+**Q3.** ~~Are external integrations (LDAP, SAML2 IdP, OAuth2 IdP, NSX
+controller, Netscaler, Tungsten, S3-compatible storage, backup
+providers) modeled as trusted control-plane peers?~~ **RESOLVED**
+*(maintainer: DaanHoogland — yes)* — trusted control-plane peers; this
+licenses §3 item 2 and §11a trusted-input dispositions. *(maps to §2, §3,
§11a)*
+
+**Q4.** ~~SecondaryStorageVM HTTP download surface — is the URL token
+per-template ACL-checked, or is the SSVM URL itself a bearer credential?~~
+**RESOLVED** *(maintainer: vishesh92, DaanHoogland)* — download links are
+UUID-named symlinks served by an Apache httpd with **no auth on the link**;
+the UUID format defeats enumeration and the symlink is removed after a
+period (timed availability is the mitigation). The PMC noted this should
+be re-tested/confirmed in code. Folded into §6, §11a. *(Daan also asked
+why static code analysis did not surface this — a note for the scan
+agent, not a model gap.)*
+
+**Q5.** ~~Vendored upstream code under `systemvm/agent/noVNC` and bundled
+JaSypt / Bouncy Castle / JSch — is the policy "report upstream; we pick up
+fixes on next sync"?~~ **RESOLVED** *(maintainer: vishesh92, DaanHoogland)*
+— `systemvm/agent/noVNC` is a **vendored fork of `github.com/novnc/novnc`**
+with CloudStack changes; vendored bugs go upstream. There is **no automated
+update procedure today** (dependabot has not produced viable PRs); the PMC
+would prefer to establish one. Folded into §3 item 8, §11a.
+
+**Q6.** ~~Is "an operator with `root` on a management-server host, the
+JCEKS keystore + encryption keys, the Root CA private key, or MariaDB
+credentials" out of scope?~~ **RESOLVED** *(maintainer: DaanHoogland — yes)*
+— `OUT-OF-MODEL: adversary-not-in-scope`. *(maps to §3 item 1, §9)*
+
+**Q7.** ~~Hypervisor bugs (libvirt / vSphere SDK / XenAPI / Hyper-V API /
+KVM/QEMU itself) — out of scope, report upstream?~~ **RESOLVED**
+*(maintainer: DaanHoogland — yes, out of scope; report upstream)*. *(maps to
§3 item 3)*
+
+### Wave 2 — the two big insecure defaults
+
+**Q12.** ~~**Highest-leverage question in the model.** Are
+`ca.plugin.root.auth.strictness` and `ca.plugin.root.allow.expired.cert`
+shipped insecure-by-default?~~ **RESOLVED** *(maintainer: vishesh92,
+DaanHoogland — `https://github.com/apache/cloudstack/pull/2239`)*:
+
+- `ca.plugin.root.auth.strictness` defaults to **`true` on new setups** —
+ the management server **does** require a Root-CA-signed client cert on
+ `:8250` and the cluster ports. It is `false` **only** after upgrading
+ from a version released before Aug 2017 that predates the setting; the
+ upgrade instructions document turning it on, so a leftover `false` is an
+ upgrade-hygiene gap, not a shipped insecure default.
+- `ca.plugin.root.allow.expired.cert` defaults to `true` as an operational
+ concession to cert-rotation lag.
+
+This resolution reshaped §3 item 1, §5a, §7 (the un-certed peer row),
+§8 P5, §9 false-friends, §10, §11, §11a, and §13. The earlier
+"assumes operator must flip per §10" framing is withdrawn.
+
+### Wave 3 — adjacent insecure defaults and admin-only surfaces
+
+**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.~~ **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
+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)?~~ **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
+sandboxing of user-supplied OS images, and that user-data is intentionally
+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/`,
+`tools/{devcloud4,devcloud-kvm,appliance,checkstyle,transifex,bugs-wiki,...}`,
+`simulator` hypervisor plugin. Anything to add or remove? **RESOLVED**
*(maintainer: DaanHoogland)* — exclude `simulator` and `tools/appliance`
explicitly (out of scope for now; a future security-purpose tooling effort may
revisit). *(maps to §3 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). **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
+domain / per user via `enable.2fa.*`. Confirm; and is "2FA disabled in
+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)*. **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;
+when open, it is unauthenticated by design. A report of "integration
+port allows admin commands without auth" is `OUT-OF-MODEL:
+non-default-build` *if* the operator opened it, else `VALID`. Confirm the
default. **RESOLVED** *(maintainer: DaanHoogland)* — default is `0` (disabled);
`8096` is set only in test configurations. *(maps to §5a, §10, §11a)*
+
+### Wave 4 — environment, distributed model, false-friends
+
+**Q13.** Network-fabric assumptions — proposed: at least four logical
+networks (management, public, guest, storage), with the management
+network as the trusted control plane. Is that the canonical model, or
+do you support more compressed topologies (single-fabric) in production?
**RESOLVED** *(maintainer: DaanHoogland)* — there are four logical networks
(management, public, guest, storage); each may have multiple instances across
topologies (e.g. multiple zones) and may be combined within physical networks,
but all four logical types must be present for a functional system. *(maps to
§5, §10)*
+
+**Q14.** Clock-skew assumption for signature v3 `expires` enforcement —
+proposed: operator's responsibility to keep client + management-server clocks
roughly in sync. Confirm. **RESOLVED** *(maintainer: DaanHoogland)* —
confirmed; operator responsibility (PMC to add to the security model page).
*(maps to §5)*
+
+**Q15.** Confirm the filesystem-permissions inventory for sensitive
+files: JCEKS keystore, Root CA private key, JaSypt key + IV,
+`db.properties`. Who owns them, what mode? **RESOLVED** *(maintainer:
vishesh92)* — not a CSV inventory of every file in a running system; only the
four sensitive artifacts named here (JCEKS keystore, Root CA private key,
JaSypt key + IV, `db.properties`). Ownership is `root` (user) / `cloud`
(group), mode read+write for the owner and read for the `cloud` group (i.e.
`0640`, `root:cloud`). *(maps to §5, §10)*
+
+**Q16.** Confirm the "what CloudStack does not do to its host" inventory
+in §5: no child processes besides agent `Script` invocations / system
+VM provisioning; signal-handlers via servlet container default;
+environment-variable consumption confined to documented set. Anything to add?
**RESOLVED** *(maintainer: DaanHoogland)* — confirmed; nothing to add. *(maps
to §5)*
+
+**Q21.** API request size cap and cluster/agent RPC payload size cap —
+are these explicitly bounded, or "whatever Jetty / NIO defaults give"?
**RESOLVED** *(maintainer: DaanHoogland)* — the UI server sets an explicit cap,
`org.apache.cloudstack.ServerDaemon.DEFAULT_REQUEST_CONTENT_SIZE = 1048576` (1
MiB); for other components the sizes are capped by the upstream components
used. *(maps to §6, §9)*
+
+**Q22.** `api.throttling.*` and per-account resource limits — proposed:
+these are the entire DoS-protection surface, with no engine-level guard.
Confirm. **RESOLVED** *(maintainer: DaanHoogland)* — confirmed; enforced at the
API access check, and `api.throttling.enabled` is **`false` by default**.
*(maps to §6, §9, §10)*
+
+**Q23.** Decompression behaviour on uploaded QCOW2 / RAW / OVA — proposed:
+no engine-side cap; per-account storage limits + hypervisor limits are the
bound. Confirm. **RESOLVED** *(maintainer: DaanHoogland)* — correct. *(maps to
§6, §9)*
+
+**Q24.** Same-host non-`cloudstack` UID — proposed: game-over, no defence
+claimed. Confirm. **RESOLVED** *(maintainer: DaanHoogland, vishesh92)* —
same-host non-`cloudstack` UID is game-over (no defence claimed; an operator at
that level already controls the host, §3). On the related host-registration
question: re-adding a host with the same IP **updates the existing host
record** rather than creating a spoofed peer, and is gated by
root-admin/operator access plus the keys/certs required to connect to the
management server — adding a host directly without thos [...]
+
+**Q25.** Side-channel observers (CPU cache timing, branch-predictor /
speculative-execution channels e.g. Spectre-class, hypervisor-shared
microarchitectural channels) — out of scope (proposed). **RESOLVED**
*(maintainer: DaanHoogland)* — agreed, out of scope. *("branch" =
branch-predictor / speculative-execution side channels — clarified by
producer.)* *(maps to §7, §9)*
+
+**Q26.** Byzantine-internal-peer threshold — confirm CloudStack makes no
+BFT claim, so any compromised cluster peer or agent with a valid
+Root-CA-issued cert is unbounded (proposed). **RESOLVED** *(maintainer:
DaanHoogland)* — agreed; no BFT claim. (A quorum-style mitigation would only be
meaningful in larger clusters, not single/dual-node — possible future feature
proposal.) *(maps to §7, §9)*
+
+**Q27.** §8 P9 memory-safety — JVM-bounded; is the reachability
+boundary correctly "in-model for the JSON API + B5 input; out-of-model
+for native hypervisor SDK bugs that surface as `Throwable`"? **RESOLVED**
*(maintainer: DaanHoogland)* — the reachability boundary is right, but **§8 P9
must not imply CloudStack is Java-only** — no implementation-language
limitation is presumed (ocaml, python, bash run on hypervisors; go is used on
the management server; the set may grow). The memory-safety claims hold for the
JVM components only. *(reflected in §8 P9.)* *(maps to §8 P9, §9)*
+
+**Q28.** §8 P10 listing-scope — confirm the §10 invariant "`list*`
+responses are scoped to the principal's domain/account/project". And:
+is information leak via error messages / async-job status / event log an
in-model concern, or accepted? **RESOLVED** *(maintainer: DaanHoogland)* —
in-model: regular system logs (e.g. log4j) are exempt, but other than those,
information leaks (via error messages, async-job status, event log) are a
concern. *(maps to §8 P10, §9, §11)*
+
+**Q29.** Data-at-rest encryption — confirm CloudStack delegates entirely
+to storage layer / hypervisor (LUKS, Ceph encryption, vSphere VM
+Encryption); no CloudStack-layer encryption of guest volumes. **RESOLVED**
*(maintainer: DaanHoogland, vishesh92)* — correct; delegated entirely to the
storage layer / hypervisor. *(maps to §9)*
+
+**Q30.** Constant-time comparison — confirm that *only* the API
+signature path uses `ConstantTimeComparator`. Login password compare,
+session cookie compare, console-token compare — none documented
+constant-time. Is that intentional? **RESOLVED** *(maintainer: DaanHoogland)*
— not intentional — the absence of constant-time comparison on the
login-password / session-cookie / console-token paths is a lack of feature
(hardening opportunity), not a by-design decision. *(maps to §8, §9)*
+
+**Q31.** Time-of-check-to-time-of-use between RBAC check at API entry
+and orchestration on agent fleet — confirm mid-job RBAC revocation is
+**not** retroactively enforced (proposed). **RESOLVED** *(maintainer:
DaanHoogland)* — agreed/confirmed. *(maps to §9)*
+
+**Q32.** TLS posture on `:8080` vs `:8443` — confirm production deploys
+behind TLS on `:8443` or behind a TLS-terminating reverse proxy; a bare
+`:8080` HTTP API is dev-only. **RESOLVED** *(maintainer: DaanHoogland)* —
confirmed. *(maps to §5a, §10)*
+
+**Q33.** `security.encryption.key` reuse across environments — confirm
+that reusing the JaSypt key + IV across staging and production is a
+documented misuse. **RESOLVED** *(maintainer: DaanHoogland)* — indeed —
confirmed misuse. *(maps to §11)*
+
+### Wave 5 — meta
+
+**Q34.** Should this document live at `docs/threat-model.md` in
+`apache/cloudstack`, or as a page on `cloudstack.apache.org/security/`?
+Or both, with one canonical and the other linked? **RESOLVED** *(maintainer:
DaanHoogland, vishesh92)* — both: this document is the source of truth, and
`cloudstack.apache.org/security` carries an excerpt plus a link to it. *(meta)*
+
+**Q35.** Is there an existing CloudStack threat-model document
+(Confluence, internal, or a `[SECURITY]`-tagged dev@ thread) that this
+should reconcile against rather than supersede? **RESOLVED** *(maintainer:
DaanHoogland)* — `cloudstack.apache.org/security/` is the only existing
security model today; this document becomes its source of truth, with the page
linking to it. *(meta — §3.1a of the rubric)*
+
+**Q36.** What kind of change should trigger a revision (proposed list in
+§12 — confirm or correct)? **RESOLVED** *(maintainer: DaanHoogland)* —
confirmed, plus add: a change in the extension mechanisms implemented by
CloudStack (now reflected in §12). *(meta, §12)*
+
+**Q37.** §11a is the highest-leverage section for the scan agent's
+suppression list. The current draft has 15 patterns; could the PMC
+populate §11a from recurring "not a vuln" closures on the
+`[email protected]` ↔ CloudStack triage queue and on
+`https://cloudstack.apache.org/security.html`? Concrete asks: 3–5
+patterns the PMC sees recur in inbound reports (e.g. "SSL bare on
+`:8080` in a dev cluster", "agent port open without strictness flipped",
+"`md5` authenticator left enabled after upgrade", "console URL appears
+in support ticket"). *(meta — §11a)*
+
+**Q38.** Confirm the structural decision to keep the four satellite repos
+as separate delta models (`cloudstack-go-threat-model-draft.md`,
+`cloudstack-cloudmonkey-threat-model-draft.md`,
+`cloudstack-terraform-provider-threat-model-draft.md`,
+`cloudstack-kubernetes-provider-threat-model-draft.md`) inheriting §3
+/ §4 / §7 from this document. **RESOLVED** *(maintainer: DaanHoogland)* —
confirmed; the satellites are not the system core (the core runs without them,
they cannot run without the core), and there is an added hierarchy —
`cloudstack-go` is a dependency of the other three. *(meta, §3 item 9)*
+
+---
+
+## Appendix: SECURITY.md → §x back-map
+
+CloudStack does not currently ship an in-repo `SECURITY.md`; the `README.md`
+section "Reporting Security Vulnerabilities" points to
+`https://cloudstack.apache.org/security.html` as the canonical disclosure
+landing page. The de facto security-policy artifacts are scattered:
+
+| Source | Claim | Lands in |
+| --- | --- | --- |
+| `README.md` "Reporting Security Vulnerabilities" | report to
`[email protected]`; canonical page at `cloudstack.apache.org/security.html`
| §1 reporting cross-reference |
+| `README.md` "Notice of Cryptographic Software" | JaSypt, Bouncy Castle,
JSch, OpenSwan, MySQL native encryption | §5 cryptography assumption, §8 P8 |
+| `agent/conf/agent.properties` (`host`, `port`, `ssl.handshake.timeout`, …) |
agent ↔ management server transport on `:8250` | §2 component table, §4 B5 |
+| `server/src/main/java/com/cloud/api/ApiServer.java` `verifyRequest` (lines
~980–1156) | HMAC-SHA1 signature + `expires` enforcement
(`enforce.post.requests.and.timestamps`) + constant-time compare | §8 P1, §8
P3, §5a "enforce.post.requests.and.timestamps" row, §11a "SHA1 / constant-time"
entries |
+| `server/src/main/java/com/cloud/api/ApiServlet.java` `getClientAddress`
(lines 700–725) | forward-header gating by `proxy.cidr` / `proxy.header.names`
when `proxy.header.verify=true` | §8 P6, §5a "proxy.header.verify" row |
+| `server/src/main/java/com/cloud/api/ApiServlet.java` 2FA path (lines
360–582) | password + 2FA flow | §8 P2 |
+| `framework/ca/.../CAService.java`,
`plugins/ca/root-ca/.../RootCAProvider.java` | Root CA generated at first boot;
agent enrolment via `SetupKeyStoreCommand` | §4 B5, §8 P5, §5a
strictness/allow-expired rows |
+| `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.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
~793–916) | 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 |
+| `https://cloudstack.apache.org/security.html` (website) | canonical
disclosure landing page | §1 reporting cross-reference (note: not accessible
from the producer's network at draft time; verify content with PMC) |