Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions samples/getBucketEncryptionEnforcementConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2026 Google LLC
//
// Licensed 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.

'use strict';

// sample-metadata:
// title: Get Bucket Encryption Enforcement
// description: Retrieves the current encryption enforcement configurations for a bucket.
// usage: node getBucketEncryptionEnforcementConfig.js <BUCKET_NAME>

function main(bucketName = 'my-bucket') {
// [START storage_get_encryption_enforcement_config]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// The ID of your GCS bucket
// const bucketName = 'your-unique-bucket-name';

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

async function getBucketEncryptionEnforcementConfig() {
const [metadata] = await storage.bucket(bucketName).getMetadata();

console.log(
`Encryption enforcement configuration for bucket ${bucketName}.`
);
const enc = metadata.encryption;
if (!enc) {
console.log(
'No encryption configuration found (Default GMEK is active).'
);
return;
}
console.log(`Default KMS Key: ${enc.defaultKmsKeyName || 'None'}`);

const printConfig = (label, config) => {
if (config) {
console.log(`${label}:`);
console.log(` Mode: ${config.restrictionMode}`);
console.log(` Effective: ${config.effectiveTime}`);
}
};

printConfig(
'Google Managed (GMEK) Enforcement',
enc.googleManagedEncryptionEnforcementConfig
);
printConfig(
'Customer Managed (CMEK) Enforcement',
enc.customerManagedEncryptionEnforcementConfig
);
printConfig(
'Customer Supplied (CSEK) Enforcement',
enc.customerSuppliedEncryptionEnforcementConfig
);
}

getBucketEncryptionEnforcementConfig().catch(console.error);
// [END storage_get_encryption_enforcement_config]
}
main(...process.argv.slice(2));
58 changes: 58 additions & 0 deletions samples/removeAllBucketEncryptionEnforcementConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2026 Google LLC
//
// Licensed 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.

'use strict';

// sample-metadata:
// title: Remove All Bucket Encryption Enforcement
// description: Removes all encryption enforcement configurations and resets to default behavior.
// usage: node removeAllBucketEncryptionEnforcementConfig.js <BUCKET_NAME>

function main(bucketName = 'my-bucket') {
// [START storage_remove_all_encryption_enforcement_config]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// The ID of your GCS bucket
// const bucketName = 'your-unique-bucket-name';

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

// Setting these to null explicitly removes the enforcement policy.
// We also include defaultKmsKeyName: null to fully reset the bucket encryption state.
async function removeAllBucketEncryptionEnforcementConfig() {
const options = {
encryption: {
defaultKmsKeyName: null,
googleManagedEncryptionEnforcementConfig: null,
customerSuppliedEncryptionEnforcementConfig: null,
customerManagedEncryptionEnforcementConfig: null,
},
};

await storage.bucket(bucketName).setMetadata(options);

console.log(
`Encryption enforcement configuration removed from bucket ${bucketName}.`
);
}

removeAllBucketEncryptionEnforcementConfig().catch(console.error);
// [END storage_remove_all_encryption_enforcement_config]
}
main(...process.argv.slice(2));
93 changes: 93 additions & 0 deletions samples/setBucketEncryptionEnforcementConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2026 Google LLC
//
// Licensed 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.

'use strict';

// sample-metadata:
// title: Set Bucket Encryption Enforcement
// description: Configures a bucket to enforce specific encryption types (e.g., CMEK-only).
// usage: node setBucketEncryptionEnforcementConfig.js <BUCKET_NAME> <KMS_KEY_NAME>

function main(
bucketName = 'my-bucket',
defaultKmsKeyName = process.env.GOOGLE_CLOUD_KMS_KEY_ASIA
) {
// [START storage_set_encryption_enforcement_config]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// The ID of your GCS bucket
// const bucketName = 'your-unique-bucket-name';

// The name of the KMS key to be used as the default
// const defaultKmsKeyName = 'my-key';

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

async function setBucketEncryptionEnforcementConfig() {
const options = {
encryption: {
defaultKmsKeyName: defaultKmsKeyName,
googleManagedEncryptionEnforcementConfig: {
restrictionMode: 'FullyRestricted',
},
customerSuppliedEncryptionEnforcementConfig: {
restrictionMode: 'FullyRestricted',
},
customerManagedEncryptionEnforcementConfig: {
restrictionMode: 'NotRestricted',
},
},
};

const [metadata] = await storage.bucket(bucketName).setMetadata(options);

console.log(
`Encryption enforcement configuration updated for bucket ${bucketName}.`
);
const enc = metadata.encryption;
if (enc) {
console.log(`Default KMS Key: ${enc.defaultKmsKeyName}`);

const logEnforcement = (label, config) => {
if (config) {
console.log(`${label}:`);
console.log(` Mode: ${config.restrictionMode}`);
console.log(` Effective: ${config.effectiveTime}`);
}
};

logEnforcement(
'Google Managed (GMEK) Enforcement',
enc.googleManagedEncryptionEnforcementConfig
);
logEnforcement(
'Customer Managed (CMEK) Enforcement',
enc.customerManagedEncryptionEnforcementConfig
);
logEnforcement(
'Customer Supplied (CSEK) Enforcement',
enc.customerSuppliedEncryptionEnforcementConfig
);
}
}

setBucketEncryptionEnforcementConfig().catch(console.error);
// [END storage_set_encryption_enforcement_config]
}
main(...process.argv.slice(2));
59 changes: 59 additions & 0 deletions samples/system-test/buckets.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,65 @@ it('should remove a buckets default KMS key', async () => {
assert.ok(!metadata.encryption);
});

it('should set bucket encryption enforcement configuration', async () => {
const output = execSync(
`node setBucketEncryptionEnforcementConfig.js ${bucketName} ${defaultKmsKeyName}`
);

assert.include(
output,
`Encryption enforcement configuration updated for bucket ${bucketName}.`
);

assert.include(output, `Default KMS Key: ${defaultKmsKeyName}`);

assert.include(output, 'Google Managed (GMEK) Enforcement:');
assert.include(output, 'Mode: FullyRestricted');

assert.include(output, 'Customer Managed (CMEK) Enforcement:');
assert.include(output, 'Mode: NotRestricted');

assert.include(output, 'Customer Supplied (CSEK) Enforcement:');
assert.include(output, 'Mode: FullyRestricted');

assert.match(output, new RegExp('Effective:'));

const [metadata] = await bucket.getMetadata();
assert.strictEqual(
metadata.encryption.googleManagedEncryptionEnforcementConfig
.restrictionMode,
'FullyRestricted'
);
});

it('should get bucket encryption enforcement configuration', async () => {
const output = execSync(
`node getBucketEncryptionEnforcementConfig.js ${bucketName}`
);

assert.include(
output,
`Encryption enforcement configuration for bucket ${bucketName}.`
);
assert.include(output, `Default KMS Key: ${defaultKmsKeyName}`);

assert.include(output, 'Google Managed (GMEK) Enforcement:');
assert.include(output, 'Mode: FullyRestricted');
assert.match(output, /Effective:/);
});

it('should remove all bucket encryption enforcement configuration', async () => {
const output = execSync(
`node removeAllBucketEncryptionEnforcementConfig.js ${bucketName}`
);
assert.include(
output,
`Encryption enforcement configuration removed from bucket ${bucketName}`
);
await bucket.getMetadata();
assert.ok(!bucket.metadata.encryption);
});

it("should enable a bucket's uniform bucket-level access", async () => {
const output = execSync(
`node enableUniformBucketLevelAccess.js ${bucketName}`
Expand Down
26 changes: 26 additions & 0 deletions src/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ export interface RestoreOptions {
generation: string;
projection?: 'full' | 'noAcl';
}
export interface EncryptionEnforcementConfig {
restrictionMode?: 'NotRestricted' | 'FullyRestricted';
readonly effectiveTime?: string;
}
export interface BucketMetadata extends BaseMetadata {
acl?: AclMetadata[] | null;
autoclass?: {
Expand All @@ -316,6 +320,9 @@ export interface BucketMetadata extends BaseMetadata {
defaultObjectAcl?: AclMetadata[];
encryption?: {
defaultKmsKeyName?: string;
googleManagedEncryptionEnforcementConfig?: EncryptionEnforcementConfig;
customerManagedEncryptionEnforcementConfig?: EncryptionEnforcementConfig;
customerSuppliedEncryptionEnforcementConfig?: EncryptionEnforcementConfig;
} | null;
hierarchicalNamespace?: {
enabled?: boolean;
Expand Down Expand Up @@ -1193,6 +1200,25 @@ class Bucket extends ServiceObject<Bucket, BucketMetadata> {
* }, function(err, apiResponse) {});
*
* //-
* // Enforce CMEK-only encryption for new objects.
* // This blocks Google-Managed and Customer-Supplied keys.
* //-
* bucket.setMetadata({
* encryption: {
* defaultKmsKeyName: 'projects/grape-spaceship-123/...',
* googleManagedEncryptionEnforcementConfig: {
* restrictionMode: 'FullyRestricted'
* },
* customerSuppliedEncryptionEnforcementConfig: {
* restrictionMode: 'FullyRestricted'
* },
* customerManagedEncryptionEnforcementConfig: {
* restrictionMode: 'NotRestricted'
* }
* }
* }, function(err, apiResponse) {});
*
* //-
* // Set the default event-based hold value for new objects in this
* // bucket.
* //-
Expand Down
47 changes: 47 additions & 0 deletions system-test/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2992,6 +2992,53 @@ describe('storage', function () {
`${metadata!.encryption!.defaultKmsKeyName}/cryptoKeyVersions/1`
);
});

describe('encryption enforcement', () => {
it('should enforce FullyRestricted CSEK policy', async () => {
await bucket.setMetadata({
encryption: {
defaultKmsKeyName: kmsKeyName,
customerSuppliedEncryptionEnforcementConfig: {
restrictionMode: 'FullyRestricted',
},
},
});

await new Promise(res =>
setTimeout(res, BUCKET_METADATA_UPDATE_WAIT_TIME)
);

const encryptionKey = crypto.randomBytes(32);
const file = bucket.file('csek-attempt', {encryptionKey});

await assert.rejects(
file.save(FILE_CONTENTS, {resumable: false}),
(err: ApiError) => {
const failureMessage =
"Requested encryption type for object is not compliant with the bucket's encryption enforcement configuration.";
assert.strictEqual(err.code, 412);
assert.ok(err.message.includes(failureMessage));
return true;
}
);
});

it('should allow uploads that comply with enforcement', async () => {
await bucket.setMetadata({
encryption: {
googleManagedEncryptionEnforcementConfig: {
restrictionMode: 'NotRestricted',
},
},
});

const file = bucket.file('compliant-file');
await file.save(FILE_CONTENTS);

const [metadata] = await file.getMetadata();
assert.ok(metadata.kmsKeyName || metadata.customerEncryption);
});
});
});
});

Expand Down
Loading
Loading