vSphere Key Provider security architecture¶
This design record defines the security contract for the appliance-native vSphere Key Provider. It applies to the implementation tracked by issues #169 through #172. The appliance-native daemon and wrapped store are implemented, but they must remain described as experimental until the VCF 9.1 promotion gate is complete.
Architecture decision¶
Atlaso implements a small Python service with an internal, bounded KMIP 1.4 TTLV codec and operation dispatcher. It does not embed a general-purpose KMIP request engine, policy engine, storage model, or compatibility wrapper.
Python keeps the service inside Atlaso's existing build, packaging, logging, testing, and Photon lifecycle. The security boundary comes from the narrow checked-in protocol allowlist rather than the language. The adapter must:
- decode only the required TTLV types and structures;
- reject unknown, duplicate, oversized, deeply nested, or malformed input before dispatch;
- enforce a maximum request size, batch size, nesting depth, connection count, and idle timeout;
- bind a verified client certificate fingerprint to exactly one provider UUID;
- authorize every batch item independently inside the provider namespace;
- never include raw TTLV, key bytes, wrapped blobs, credentials, or private material in logs and errors; and
- use deterministic protocol errors without falling back to a broader KMIP implementation.
The service runs as the unprivileged atlaso-kmip identity. Only /ui/management/appliance-apply may ask the constrained
atlaso-helper to install configuration, certificates, systemd units, ownership, and firewall state.
Assets and trust boundaries¶
| Asset | Boundary and protection |
|---|---|
| Plaintext data-encryption key | Exists only in service memory while creating or returning one key; zeroed on a best-effort basis after use |
| Wrapped key blob | Stored under /var/lib/atlaso/kmip with provider ID and authenticated metadata |
| Runtime key-encryption key | Wrapped under ATLASO_SECRETS_KEY; never stored or logged in plaintext |
| Provider server identity | CA-managed certificate and private key readable only by the KMIP service |
| vCenter client identity | Canonical public X.509 PEM and exact SHA-256 fingerprint mapped to one provider UUID; no client private key is accepted or generated |
| Recovery bundle | Encrypted with a user-supplied passphrase and produced only through an authenticated, audited workflow |
| Vault-assisted credential | Read only for the selected vCenter operation; never copied into provider records, tasks, or traces |
| Interop evidence | Metadata-only JSONL validated against the bounded protocol contract |
The FastAPI control plane owns desired state and authenticated administration. The KMIP daemon owns protocol handling and wrapped operational keys. The root helper owns host installation. None may silently inherit another component's authority.
Provider and identity isolation¶
Every provider receives an immutable UUID and an independent key namespace. Every key receives a stable random UUID whose lookup is always qualified by provider ID. Database constraints and service-layer queries both enforce the qualification.
A provider-scoped trusted-vCenter record contains one or more normalized SHA-256 certificate fingerprints and canonical public X.509 certificates. Fingerprints are unique appliance-wide, so the same certificate cannot authorize another provider. Multiple vCenters may be trusted by one provider, but a successful TLS handshake alone grants no key access. Missing, disabled, expired, ambiguous, or unmapped identities fail closed. LDAP organization membership is irrelevant.
The handshake trust bundle contains the internal CA public root and current imported public leaf certificates. X.509 partial-chain verification permits explicit leaf trust, but authorization still requires the peer's exact fingerprint. Certificate replacement uses an add-apply-verify-retire overlap. The administrator imports the replacement public certificate, applies it globally, moves vCenter to that identity, and uses the audited Retire certificate action. The service never trusts a renewed certificate merely because its subject or CA matches.
Key creation and storage¶
The service generates AES-256 key material with the operating system CSPRNG. It wraps each key with AES-256-GCM under the runtime key-encryption key (KEK). The authenticated additional data binds at least:
- storage schema version;
- provider UUID;
- key UUID;
- algorithm and length;
- optional bounded KMIP name;
- creation and activation times; and
- lifecycle state.
Replacing a provider ID, moving a wrapped blob into another namespace, editing metadata, or rolling a row backward must
make decryption fail. Every metadata-only read authenticates the same wrapped ciphertext and additional data as
Get. The encrypted KEK envelope also carries a current store generation and commitment over the complete database.
Mutations first persist a pending generation, commit SQLite, and then promote it, so restart can distinguish an
interrupted write from a restored row or database snapshot. A database state matching neither encrypted commitment
fails closed. Restoring both the database and KEK envelope from the same full-appliance snapshot remains equivalent to
rolling back the whole appliance and requires an external recovery/acceptance check; the appliance has no external
monotonic counter.
Files and database pages use service-only permissions. Successful startup requires both the operational store and the
KEK protected by the current ATLASO_SECRETS_KEY. The store is SQLite with full synchronous commits; only wrapped
ciphertext, nonce, and authenticated metadata are persisted. The KEK envelope and database use mode 0600, their
parent directory uses 0700, and systemd confines writes to the KMIP state and log directories. The daemon receives
only ATLASO_SECRETS_KEY through a dedicated root-managed mode-0600, machine-encrypted systemd credential. Systemd
decrypts it into the service's private runtime credential directory; the daemon does not receive the shared web-session
or bootstrap-administrator secrets.
Atlaso never exports plaintext keys through its UI or management API. KMIP Get is the sole plaintext release path
and is restricted to an authenticated client mapped to the same provider. Mutable buffers are cleared where Python
permits, but encoded protocol bytes and immutable Python objects cannot be guaranteed to be overwritten in place; the
unprivileged process and short request lifetime are part of the memory-exposure boundary.
Recovery and disaster recovery¶
The recovery implementation and final recovery gate belong to issue #172. The requirements below are the retained security contract, not a claim that provider recovery is currently available.
The normal Atlaso settings archive excludes operational keys, the runtime KEK, private certificates, and Vault passwords. A separate recovery workflow produces a passphrase-encrypted bundle containing the minimum material needed to restore provider identity, wrapped keys, the runtime KEK, and integrity metadata.
Recovery requirements:
- The passphrase is accepted through a no-log input and is never stored in Atlaso.
- Key derivation uses a memory-hard, salted construction with parameters recorded in the bundle header.
- Bundle encryption is authenticated, versioned, and binds a manifest digest.
- Export and restore create audit events without filenames, passphrases, key IDs, or secret contents.
- Restore is allowed only into an empty new store or an explicitly verified matching store.
- A dry-run verifies format, authentication, version compatibility, provider IDs, row counts, and store emptiness.
- Restore is atomic; any validation or write failure leaves the previous store and service state intact.
- Success is not claimed until an existing encrypted workload retrieves its original key after restart.
Losing both the running store and a valid recovery bundle is unrecoverable. Losing only
ATLASO_SECRETS_KEY is also unrecoverable unless the recovery bundle contains a separately passphrase-protected KEK.
Abuse cases and mitigations¶
| Abuse case | Required mitigation |
|---|---|
| Untrusted client requests another provider's key | Exact fingerprint mapping plus provider-qualified lookup |
| Captured trace or task leaks a key | Metadata-only structured events; forbidden-field tests and redaction |
| Malformed TTLV consumes memory or CPU | Pre-dispatch size, depth, batch, connection, and timeout limits |
| Root helper is used as a general service control | Fixed paths, exact verbs, ownership checks, and staged desired state |
| Wrapped blob is copied or rolled back | AES-GCM authenticated metadata and store integrity checks |
| Vault credential escapes vCenter automation | One-operation decrypt, no subprocess arguments, no logging, audited presence only |
| Certificate rotation creates an outage | Import-apply-verify-retire public-certificate overlap with health proof before removal |
| Provider deletion loses operational keys | Disabled and detached state plus authenticated exact zero-key evidence; unavailable evidence fails closed |
| Recovery overwrites a healthy store | Empty/matching-store precondition, dry-run, atomic install, explicit confirmation |
| Broad KMIP behavior is accidentally exposed | Checked-in allowlist, fail-closed dispatch, and live evidence promotion gate |
Security review gates¶
The implementation cannot be called supported until tests and live evidence cover:
- cross-provider key and identity isolation;
- malformed TTLV, oversized frames, deep nesting, batch limits, and connection exhaustion;
- storage tamper, wrong
ATLASO_SECRETS_KEY, wrong recovery passphrase, and rollback; - service and appliance restart without identifier drift;
- client certificate add, replacement overlap, disable, expiration, and ambiguity;
- helper path, owner, mode, symlink, service, and firewall constraints;
- redaction across service logs, task results, audits, API responses, UI, recovery, and trace output; and
- VCF 9.1 create, retrieve, encrypted workload, restart, and restore acceptance.
Security-sensitive code requires the normal Atlaso review and CI gates. Any discovery of exposed key material or a cross-provider authorization failure is critical and blocks release.