CMEK & Encryption
HatiData enforces encryption at every layer: TLS 1.3 for data in transit, AES-256 for data at rest in object storage, and LUKS AES-256-XTS for the ephemeral local SSD cache. Customer-Managed Encryption Keys (CMEK) are required for all Enterprise deployments — HatiData never holds your key material.
Encryption Overview
| Layer | Mechanism | Customer Controls Key? |
|---|---|---|
| In transit (client to proxy) | TLS 1.3 | No (certificate managed by NLB) |
| In transit (proxy to object storage) | TLS 1.3 (AWS/GCP/Azure SDK default) | No |
| At rest (object storage) | AES-256 via CMEK | Yes |
| At rest (local SSD cache) | LUKS AES-256-XTS | Yes (derived from KMS key) |
| Column-level (query results) | Cryptographic hash masking (optional) | Yes (policy-defined) |
Encryption is mandatory. There is no option to disable TLS or CMEK for Enterprise deployments.
CMEK: Customer-Managed Encryption Keys
CMEK lets you supply the root encryption key from your own KMS. HatiData's proxy retrieves a data encryption key (DEK) from your KMS at instance boot and uses it to seal the LUKS volume. Object storage uses envelope encryption: AWS S3 / GCS / Azure Blob encrypts each object using a data key that is itself encrypted with your KMS key.
Key material never leaves your KMS. If you revoke or disable your key, HatiData cannot decrypt the local SSD cache or new object storage writes.
AWS KMS Configuration
# terraform/aws/environments/production.tfvars
kms_key_arn = "arn:aws:kms:eu-west-1:123456789012:key/mrk-abc12345"
cloud_provider = "aws"
cloud_region = "eu-west-1"
The proxy's IAM role requires the following KMS permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:eu-west-1:123456789012:key/mrk-abc12345"
}
]
}
Multi-Region Keys (MRK prefix mrk-) are supported and recommended for Enterprise deployments with cross-region replication.
GCP Cloud KMS Configuration
# terraform/gcp/environments/production.tfvars
kms_key_name = "projects/my-project/locations/europe-west1/keyRings/hatidata/cryptoKeys/data-key"
cloud_provider = "gcp"
cloud_region = "europe-west1"
The proxy's service account requires the roles/cloudkms.cryptoKeyEncrypterDecrypter role on the key resource.
Azure Key Vault Configuration
# terraform/azure/environments/production.tfvars
kms_key_vault_url = "https://my-vault.vault.azure.net/"
kms_key_name = "hatidata-data-key"
kms_key_version = "abc123def456"
cloud_provider = "azure"
cloud_region = "westeurope"
The proxy's managed identity requires the Key Vault Crypto User role on the vault.
Key Rotation
AWS KMS automatic annual rotation is enabled by default for HatiData-managed keys. For customer-supplied keys, rotation is the customer's responsibility. After rotating the KMS key:
- The local SSD LUKS volume is re-keyed on next instance boot (the DEK is re-encrypted with the new KMS key version)
- New S3 object uploads use the new KMS key version automatically
- Existing S3 objects are re-encrypted on next access via S3's server-side re-encryption API
API keys support a 72-hour rotation grace period — both the old and new key are accepted during the overlap window.
TLS 1.3
All network connections to and from HatiData use TLS 1.3. TLS 1.2 is not negotiated — connections attempting a TLS 1.2 handshake are rejected.
Cipher Suites
TLS 1.3 cipher suites in use:
| Cipher Suite | Key Exchange | AEAD |
|---|---|---|
TLS_AES_256_GCM_SHA384 | ECDHE (X25519) | AES-256-GCM |
TLS_CHACHA20_POLY1305_SHA256 | ECDHE (X25519) | ChaCha20-Poly1305 |
TLS_AES_128_GCM_SHA256 | ECDHE (X25519) | AES-128-GCM |
Certificate Management
The Network Load Balancer (NLB) terminates TLS. Certificates are managed via:
- AWS: ACM (AWS Certificate Manager) — automatic renewal
- GCP: Google-managed certificates via Cloud Load Balancing
- Azure: App Gateway managed certificates or Azure Key Vault certificates
The proxy's port 5439 (Postgres wire protocol) uses TLS with the NLB certificate. Clients using standard Postgres drivers (psycopg2, asyncpg, SQLAlchemy, libpq) should set sslmode=require or sslmode=verify-full.
# Python (psycopg2)
conn = psycopg2.connect(
host="proxy.eu-west-1.hatidata.yourcompany.com",
port=5439,
user="agent-data-analyst",
password="hd_live_...",
sslmode="require"
)
// TypeScript (pg)
const client = new Client({
host: "proxy.eu-west-1.hatidata.yourcompany.com",
port: 5439,
user: "agent-data-analyst",
password: "hd_live_...",
ssl: { rejectUnauthorized: true }
});
Local SSD Cache Encryption (LUKS)
The HatiData proxy caches frequently accessed data on high-performance local SSDs attached to the compute instance. This cache is encrypted at the block device level using LUKS (Linux Unified Key Setup) with AES-256-XTS.
Boot Sequence
Instance starts
→ Proxy calls KMS: GenerateDataKey
→ KMS returns plaintext DEK + ciphertext DEK
→ Proxy formats local SSD as LUKS with plaintext DEK
→ Proxy discards plaintext DEK from memory
→ Proxy stores ciphertext DEK on disk (cannot be used without KMS)
→ Proxy mounts LUKS volume
→ Query cache is available
The plaintext DEK exists in memory only for the duration of the LUKS format operation (sub-second). After the LUKS volume is mounted, the DEK is not retained in process memory.
Termination and Erasure
When an instance is terminated (auto-scaling, manual shutdown, or contract cancellation):
# Executed by the shutdown hook before instance termination
cryptsetup luksErase /dev/nvme1n1
luksErase overwrites all LUKS key slots with random data, rendering the encrypted volume permanently unreadable — even if the physical SSD is recovered.
Cache Parameters
| Parameter | Default | Notes |
|---|---|---|
| LUKS cipher | aes-xts-plain64 | NIST-approved for disk encryption |
| Key size | 512 bits (256-bit AES-XTS) | XTS mode requires double-width key |
| Hash | Cryptographic hash | PBKDF2 for key derivation from DEK |
| LRU eviction threshold | 80% capacity | Least-recently-used eviction above threshold |
Column-Level Masking (Encryption-Adjacent)
Column masking operates at the proxy layer after query execution. While masking is not encryption in the classical sense, the cryptographic hash masking function provides a one-way transformation that preserves referential integrity without exposing the original value.
-- Original value: alice@example.com
-- After hash masking: a1b2c3d4e5f6...
SELECT
customer_id,
email -- masked to cryptographic hash for analyst role
FROM customers;
Four masking modes are available:
| Mode | Output | Use Case |
|---|---|---|
full | *** | Fields where no information should be inferred |
partial | ***1111 (last 4) | Card numbers, phone numbers (last 4 for verification) |
hash | Cryptographic hex digest | PII fields where join consistency is needed |
null | NULL | Fields where no value should be returned at all |
See Security Model for full masking policy configuration.
Encryption FAQ
Q: Is encryption optional? A: No. TLS 1.3 for in-transit and CMEK for at-rest are mandatory for Enterprise deployments. There is no configuration option to disable encryption.
Q: What happens if my KMS key is revoked? A: The local SSD LUKS volume cannot be unlocked on next boot. New S3 object writes cannot be encrypted. Existing objects encrypted with the revoked key version cannot be decrypted. The proxy will fail to start until a valid key is available.
Q: Does HatiData ever see my KMS key material?
A: No. KMS API calls (GenerateDataKey, Decrypt) are made from within your VPC using your IAM role. The KMS response (plaintext DEK) exists briefly in the proxy process memory during LUKS initialization and is not logged or transmitted.
Q: Can I use a different encryption algorithm for the local SSD cache?
A: The LUKS configuration (aes-xts-plain64) is fixed in the Terraform modules. Contact sales@hatidata.com if you require a specific cipher configuration for regulatory compliance.
Related Concepts
- SOC 2 Architecture — How encryption controls map to CC6 criteria
- Data Residency — Region-local KMS and storage
- Audit Guarantees — Encrypted audit log storage
- PrivateLink & VPC — Network-level isolation complementing encryption