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 modelsapache/cloudstackas 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 B1reachability, 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.
apache/cloudstack) — IaaS orchestration platform. This document does not cover the four satellite repos, which carry their own delta models.7308dad1 (HEAD of main at draft time).security@apache.org 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.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.
README.md, INSTALL.md).README.md).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).
| 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). |
| 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) |
CloudStack is not, and does not aim to be, the following — reports requiring any of these will be closed with the cited disposition:
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.OUT-OF-MODEL: trusted-input.OUT-OF-MODEL: unsupported-component (upstream pointer).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.OUT-OF-MODEL: adversary-not-in-scope for the side-channel case, BY-DESIGN: property-disclaimed for the resource-limit case.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.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.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).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.apache/cloudstack-*, gem / npm / PyPI packages with similar names, and other non-product surfaces. Out of scope.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 from pre-Aug-2017 versions — see §5a) and ca.plugin.root.allow.expired.cert | peer-trust by valid cert |
| 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 |
For each family in §2, a finding is in-model only if it is reachable as follows:
: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.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).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).OUT-OF-MODEL: trusted-input (§3 item 2).README.md, INSTALL.md, packaging/{el8,el9,el10,debian,suse15}).cloudstack-agent package (documented: debian/, packaging/).README.md “Notice of Cryptographic Software”).README.md Cryptographic Software notice).expires parameter on signed API requests (documented: ApiServer.java line ~1054); this assumes loosely-synchronized clocks between client and management server (inferred — §14 Q14).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).:8080/:8443/:8250/:8096/:9090/console-proxy ports) (documented);plugins/ca/root-ca/.../RootCAProvider.java);Script invocations against /usr/share/cloudstack-common/scripts/) (documented: agent/src/main/java/com/cloud/agent/Agent.java keystoreSetupSetupPath, keystoreCertImportScriptPath);db.properties file at startup (inferred — §14 Q16);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 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 | 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.
| 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 |
api.throttling.* (inferred — §14 Q22); an attacker with a valid API key can be rate- limited at the application layer.| 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 |
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.
For each property: condition, violation symptom, severity tier, provenance.
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).apiKey+signature pair (and without a valid session cookie / SAML / OAuth / LDAP login).VALID per §13.login API; 2FA is verified after password if enabled for the user / domain (documented: ApiServlet.java lines 360–582).VALID per §13.VALID per §13.ApiServer.java line 1137)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.VALID per §13.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.:8250 or the cluster port on a setup where strictness is on.VALID per §13.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).proxy.cidr succeeds with an attacker-supplied forward header taking effect.VALID per §13.ConsoleProxyPasswordBasedEncryptor keys (documented: ConsoleProxyPasswordBasedEncryptor.java); a token includes the VM identity, the hypervisor endpoint, and a duration / expiry.VALID per §13.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).VALID per §13.:8080 / :8443 / B5; JVM-side crashes from a request a normally-RBAC'd user could send.VALID-HARDENING when reachable only by a writer who already controls the bytes (§3 item 5).SHOW-equivalent listing)list* API commands filter responses to the principal's domain/account/project scope per plugins/acl/ policy.list* response leaks resource IDs / names / metadata for resources outside the principal's RBAC scope.VALID per §13.State each plainly so a triager can route an inbound report to the matching disclaimer.
security.encryption.key, the Root CA private key, or the MariaDB credentials wins. See §3 item 1 (inferred — §14 Q6).runCustomAction. See §3 item 4 (inferred — §14 Q8).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).:8096). When open, it is an unauthenticated admin surface; closing it is the operator's job (inferred — §14 Q20).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.http://, the signature still validates but the whole request (including the secret-derived signature) is visible to the network.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).auth-int — it is no authentication at all. The name invites confusion.The operator deploying CloudStack in production must:
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.:8250 (agent), :9090 (cluster), and the MariaDB port are reachable only from the intended peers (inferred — §14 Q13).:8096 — either disable it entirely or limit it to a localhost/management subnet (inferred — §14 Q20).:8443 (not :8080); if :8080 is exposed at all, only behind a TLS-terminating reverse proxy (inferred — §14 Q32).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).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.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).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).cloudstack-agent host: cloudstack Unix user, agent keystore under /etc/cloudstack/agent/, root account, libvirt / vSphere admin credentials.: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).:8096 (integration API) publicly. Anyone reaching the port executes admin API commands without auth.: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.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).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).security.encryption.key across environments of different trust levels. A staging-env leak becomes a production-env decrypt primitive (inferred — §14 Q33).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).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.
: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).: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.Mac.getInstance("HmacSHA1") — ApiServer.java line 1130). → KNOWN-NON-FINDING.ConstantTimeComparator.compareStrings per ApiServer.java line 1137. → KNOWN-NON-FINDING (a finding flagging this is wrong).BY-DESIGN: property-disclaimed.RootCAProvider.java. Browsers will warn until the operator bootstraps trust. → BY-DESIGN: property-disclaimed.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.tools/marvin/, test/, developer/, quickcloud/.” These directories are unsupported components per §3 item 7. → OUT-OF-MODEL: unsupported-component.BY-DESIGN: property-disclaimed per §9.BY-DESIGN: property-disclaimed per §9 / §3 item 4.BY-DESIGN: property-disclaimed per §9.VALID-HARDENING at most, unless the decompression reaches §8 P9 memory-safety violations.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.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.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.invalidateHttpSession is called on each auth failure path per ApiServlet.java. → KNOWN-NON-FINDING (verify the symptom; if reproducible, escalate to MODEL-GAP).Revise this document when any of the following lands:
ca.plugin.root.auth.strictness and ca.plugin.root.allow.expired.cert.md5 / plain-text user-authenticator plugins.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 |
Every (inferred) tag in the body maps to one of these. Proposed answers are inline; please confirm, correct, or strike.
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 RESOLVED (maintainer: vishesh92, DaanHoogland) — systemvm/agent/noVNC and bundled JaSypt / Bouncy Castle / JSch — is the policy “report upstream; we pick up fixes on next sync”?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 RESOLVED (maintainer: DaanHoogland — yes) — root on a management-server host, the JCEKS keystore + encryption keys, the Root CA private key, or MariaDB credentials” out of scope?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)
Q12. Highest-leverage question in the model. Are RESOLVED (maintainer: vishesh92, DaanHoogland — ca.plugin.root.auth.strictness and ca.plugin.root.allow.expired.cert shipped insecure-by-default?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.
Q8. Is “a root admin with full RBAC role causes harm Y via a documented path Z” out of scope (proposed: yes, RESOLVED (maintainer: vishesh92) — yes; a root admin generally has direct access to most of these resources anyway → OUT-OF-MODEL: equivalent-harm)? In particular: runCustomAction, template upload, plugin registration, global config change, system-VM patching, system-VM console access.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 RESOLVED (maintainer: vishesh92) — deployment choice, not a §10 violation. Two domain-configurable global settings: enable.2fa.*. Confirm; and is “2FA disabled in production” a §10 violation or a deployment choice?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)
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 those fails (refinement tracked in apache/cloudstack#13182). So this is not an unauthenticated identity-spoof path. (maps to §7, §9)
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)
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 security@apache.org ↔ 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)
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 security@apache.org; 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) |