Ensure VM disks for critical vms are encrypted with customer-supplied encryption keys (CSEK)

GCP
GCP

Compute Engine encrypts every persistent disk at rest with a key that Google generates, stores, and applies without the owner of the disk doing anything. The benchmark recommends that the disks attached to business critical VMs should instead be encrypted with a customer-supplied encryption key, a 256-bit key that you hand to the service when the disk is created, through --csek-key-file, and that Google does not keep.

What the benchmark asks for

A disk meets the control when its diskEncryptionKey property is present and carries a sha256 value. The audit treats a diskEncryptionKey that is null or missing as Google-managed encryption, and Google-managed encryption is the default for a new disk.

The benchmark applies the requirement to business critical VMs and leaves the definition of business critical to the reader. The check itself is per disk and binary, so the scope decision is the only judgment in the control. Write that scope down before the audit, because a list of failing disks is useless until someone knows which of them were meant to be in scope.

How it works

A customer-supplied encryption key does not add a second pass of encryption over the disk contents. Compute Engine still generates the key that encrypts the data, and the customer-supplied key protects that generated key. Only a caller that presents the correct key can use the resource. Google cannot recover the key and cannot read the data without it, which is the point of the control and also its largest operating risk.

Compute Engine does not store the key on its servers, so the key has to be presented on every operation that touches the disk. A sha256 value is all that diskEncryptionKey exposes afterward, and that hash is what the audit reads. Lose the key and neither you nor Google can read the disk again.

The restrictions are narrow enough to decide the rollout. Only a new persistent disk can be encrypted this way, so an existing disk has to be rebuilt rather than converted. Local SSD cannot use a supplied key at all. An instance with a CSEK-protected disk attached cannot be suspended. Compute Engine does not store the key with an instance template either, so a managed instance group needs Cloud KMS instead. The feature is also unavailable to billing accounts in Brazil and India.

Key material runs to 256 bits, and the tooling takes it as base64 text under RFC 4648. The gcloud CLI reads it from a key file, a JSON list where each entry names the resource by uri and gives the key and a key-type of either raw or rsa-encrypted.

Deleting a disk discards the cipher keys and the data cannot be read afterward. That is true of Google-managed disks too, and it is not a benefit this control adds.

What the control does not cover

The control reads one property on one resource. A principal with permission to use the instance reads the file system through the guest operating system, where the disk is already decrypted and no key is required.

A disk passes while its key file sits in a git repository or in a build system's environment variables, and at that point the key is held by Google-managed infrastructure in all but name.

It does not stop new disks from failing. Compute Engine takes the key per disk at creation time, and Google-managed encryption is what a new disk gets when no key is passed. Every disk created after the remediation needs the key passed explicitly or it comes back Google-managed.

Resource metadata stays outside the key. A disk's name, size, zone, and labels come back from the list and describe commands below with no key in the request.

What it costs to comply

An existing disk cannot have its encryption changed after the fact. The platform offers only a new disk. Moving a failing disk onto a compliant one is then an ordinary migration that you run yourself.

Losing the key loses the data, with no recovery path at Google. The key store now needs the same durability and the same disaster recovery treatment as the data it protects, and a key file that only exists on one engineer's workstation is a single point of total loss.

Every process that creates or attaches a disk has to carry the key file. That includes instance templates, image pipelines, and any automation that provisions capacity. A process that cannot be given the key cannot create a compliant disk.

Rotation is expensive and manual. Changing a customer-supplied key is never a metadata update, it is a full rewrite of the encrypted data, and for a persistent disk that means building a replacement disk. If a laptop or file holding the key is compromised, Google's guidance is to rotate the key on every resource encrypted with it immediately, because nothing re-encrypts on its own. Snapshots taken before the rotation stay under the retired key as well, so the old key has to live until those are gone.

The case against paying this cost is strong for most fleets. What a customer-supplied key buys, beyond the encryption every disk already has, is that Google never holds the key material at any moment. Spend the operational cost where that specific property is a stated requirement for the data in question, and leave the rest of the fleet on the default.

How to check your environment

List the disks in a project and read the encryption property on each one:

gcloud compute disks list --format="json(name,zone,diskEncryptionKey)"

For a single disk, read it directly:

gcloud compute disks describe <DISK_NAME> --zone <ZONE> --format="json(diskEncryptionKey,name)"

A disk passes when diskEncryptionKey is present and contains a sha256 value. A null or absent diskEncryptionKey is a Google-managed disk and fails the control. The sha256 value is the hash the service retained, not the key, and it cannot be reversed into one.

In the console, open Compute Engine and then Disks, select a disk, and read the Encryption type field on its details page. Customer supplied is the passing state.

Run the list against every project in scope, not only the ones holding production VMs. Disks detached from any instance still hold their data and still appear in the list.

How to implement it

Generate a 256-bit key and encode it in RFC 4648 base64. Put it in a key file, one JSON entry per resource, each naming the resource uri, the key, and the key-type.

Create a standalone persistent disk with the key:

gcloud compute disks create <DISK_NAME> --csek-key-file <example-file.json>

Create an instance whose disks use the key:

gcloud compute instances create <INSTANCE_NAME> --csek-key-file <example-file.json>

For disks that fail today, the only route is a new disk created with the key file.

For disks created tomorrow, the flag has to live in whatever creates them. Put --csek-key-file into the Terraform modules and the provisioning scripts for the projects in scope, and keep the audit command above in a scheduled job, because nothing in the platform enforces the key as a default. An instance template is the one place it cannot go. Compute Engine does not store a supplied key with a template, so a managed instance group has to encrypt with Cloud KMS instead. Plan key distribution before the first disk, since the build systems that create disks need the key at create time and holding it there is the weakest link in the design.

Keep the retired keys. A disk created under an earlier key still needs that key, because rotation builds a replacement disk rather than re-pointing the old one. The key file maps each resource URI to its own key, so a rotation is survivable only while the file still carries an entry for every disk that has not been rebuilt yet. Drop an entry too early and the disk it named is unreadable.

Further reading

Found it. Fixed it. Done.

Start free. Understand your cloud in under an hour.