From 97e2c302326a312ab105d5584fb983ad56967ee4 Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 12 Jun 2026 11:54:39 +0000 Subject: [PATCH 1/4] feat(storage): add samples and system tests for GCS bucket IP filtering operations --- storage/deleteBucketIpFilterRules.js | 63 +++++++++++++++++++++++ storage/disableBucketIpFilter.js | 48 ++++++++++++++++++ storage/enableBucketIpFilter.js | 68 +++++++++++++++++++++++++ storage/getBucketIpFilter.js | 47 ++++++++++++++++++ storage/listBucketIpFilters.js | 74 ++++++++++++++++++++++++++++ storage/system-test/buckets.test.js | 36 ++++++++++++++ 6 files changed, 336 insertions(+) create mode 100644 storage/deleteBucketIpFilterRules.js create mode 100644 storage/disableBucketIpFilter.js create mode 100644 storage/enableBucketIpFilter.js create mode 100644 storage/getBucketIpFilter.js create mode 100644 storage/listBucketIpFilters.js diff --git a/storage/deleteBucketIpFilterRules.js b/storage/deleteBucketIpFilterRules.js new file mode 100644 index 0000000000..cc419069da --- /dev/null +++ b/storage/deleteBucketIpFilterRules.js @@ -0,0 +1,63 @@ +// 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'; + +function main(bucketName = 'my-bucket') { + // [START storage_delete_ip_filtering_rules] + /** + * 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 deleteBucketIpFilterRules() { + // Note: To delete specific rules, you fetch the existing config, filter out the rules, and update. + const [metadata] = await storage.bucket(bucketName).getMetadata(); + + if (!metadata.ipFilter) { + console.log(`No IP Filter configuration found for bucket ${bucketName}.`); + return; + } + + const updatedIpRanges = ( + metadata.ipFilter.publicNetworkSource?.allowedIpCidrRanges || [] + ).filter(range => range !== '8.8.8.8/32'); + + const updatedIpFilter = { + ...metadata.ipFilter, + publicNetworkSource: { + allowedIpCidrRanges: updatedIpRanges, + }, + }; + + const [updatedMetadata] = await storage.bucket(bucketName).setMetadata({ + ipFilter: updatedIpFilter, + }); + + console.log(`Specific IP Filter rules deleted for bucket ${bucketName}.`); + console.log(JSON.stringify(updatedMetadata.ipFilter, null, 2)); + } + + deleteBucketIpFilterRules().catch(console.error); + // [END storage_delete_ip_filtering_rules] +} + +main(...process.argv.slice(2)); diff --git a/storage/disableBucketIpFilter.js b/storage/disableBucketIpFilter.js new file mode 100644 index 0000000000..63bbff6e84 --- /dev/null +++ b/storage/disableBucketIpFilter.js @@ -0,0 +1,48 @@ +// 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'; + +function main(bucketName = 'my-bucket') { + // [START storage_disable_ip_filtering] + /** + * 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 disableBucketIpFilter() { + const ipFilter = { + mode: 'Disabled', + }; + + const [updatedMetadata] = await storage.bucket(bucketName).setMetadata({ + ipFilter, + }); + + console.log(`IP Filter disabled for bucket ${bucketName}.`); + console.log(JSON.stringify(updatedMetadata.ipFilter, null, 2)); + } + + disableBucketIpFilter().catch(console.error); + // [END storage_disable_ip_filtering] +} + +main(...process.argv.slice(2)); diff --git a/storage/enableBucketIpFilter.js b/storage/enableBucketIpFilter.js new file mode 100644 index 0000000000..060fca0f99 --- /dev/null +++ b/storage/enableBucketIpFilter.js @@ -0,0 +1,68 @@ +// 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'; + +function main(bucketName = 'my-bucket', filterMode = 'Enabled') { + // [START storage_enable_ip_filtering] + /** + * 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 enableBucketIpFilter() { + // Note: IP Filter configurations cannot be partially updated. + // We must fetch the existing configuration and modify it, or set a completely new one. + const [metadata] = await storage.bucket(bucketName).getMetadata(); + const existingIpFilter = metadata.ipFilter || { + mode: filterMode, + publicNetworkSource: {allowedIpCidrRanges: ['0.0.0.0/0']}, + allowCrossOrgVpcs: false, + allowAllServiceAgentAccess: false, + }; + + // Add a new IP range to publicNetworkSource + const updatedIpRanges = + existingIpFilter.publicNetworkSource?.allowedIpCidrRanges || []; + if (!updatedIpRanges.includes('8.8.8.8/32')) { + updatedIpRanges.push('8.8.8.8/32'); + } + + const updatedIpFilter = { + ...existingIpFilter, + publicNetworkSource: { + allowedIpCidrRanges: updatedIpRanges, + }, + }; + + const [updatedMetadata] = await storage.bucket(bucketName).setMetadata({ + ipFilter: updatedIpFilter, + }); + + console.log(`IP Filter enabled for bucket ${bucketName}.`); + console.log(JSON.stringify(updatedMetadata.ipFilter, null, 2)); + } + + enableBucketIpFilter().catch(console.error); + // [END storage_enable_ip_filtering] +} + +main(...process.argv.slice(2)); diff --git a/storage/getBucketIpFilter.js b/storage/getBucketIpFilter.js new file mode 100644 index 0000000000..f8a67a9fe7 --- /dev/null +++ b/storage/getBucketIpFilter.js @@ -0,0 +1,47 @@ +// 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'; + +function main(bucketName = 'my-bucket') { + // [START storage_get_ip_filtering] + /** + * 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 getBucketIpFilter() { + const [metadata] = await storage.bucket(bucketName).getMetadata(); + + if (metadata.ipFilter) { + console.log(`IP Filter Mode: ${metadata.ipFilter.mode}`); + console.log('IP Filter Configuration:'); + console.log(JSON.stringify(metadata.ipFilter, null, 2)); + } else { + console.log(`No IP Filter configuration found for bucket ${bucketName}.`); + } + } + + getBucketIpFilter().catch(console.error); + // [END storage_get_ip_filtering] +} + +main(...process.argv.slice(2)); diff --git a/storage/listBucketIpFilters.js b/storage/listBucketIpFilters.js new file mode 100644 index 0000000000..ce8b4083a4 --- /dev/null +++ b/storage/listBucketIpFilters.js @@ -0,0 +1,74 @@ +// 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'; + +function main(projectId = 'my-project-id') { + // [START storage_list_buckets_ip_filtering] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // The ID of the project to which the service account belongs + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage({projectId}); + + async function listBucketsIpFiltering() { + const [buckets] = await storage.getBuckets(); + + for (const bucket of buckets) { + if (bucket.metadata.ipFilter) { + try { + const [metadata] = await storage.bucket(bucket.name).getMetadata(); + const ipFilter = metadata.ipFilter; + console.log(`${bucket.name}: IP Filter Mode - ${ipFilter.mode}`); + + const publicNetworkSource = ipFilter.publicNetworkSource; + if (publicNetworkSource && publicNetworkSource.allowedIpCidrRanges) { + console.log(' Public Network Allowed IP Ranges:'); + publicNetworkSource.allowedIpCidrRanges.forEach(range => { + console.log(` - ${range}`); + }); + } + + const vpcNetworkSources = ipFilter.vpcNetworkSources; + if (vpcNetworkSources && vpcNetworkSources.length > 0) { + console.log(' VPC Network Sources:'); + vpcNetworkSources.forEach(source => { + console.log(` - Network: ${source.network}`); + if (source.allowedIpCidrRanges) { + source.allowedIpCidrRanges.forEach(range => { + console.log(` - ${range}`); + }); + } + }); + } + } catch (err) { + console.log( + `${bucket.name}: Error fetching IP filter - ${err.message}` + ); + } + } + } + } + + listBucketsIpFiltering().catch(console.error); + // [END storage_list_buckets_ip_filtering] +} + +main(...process.argv.slice(2)); diff --git a/storage/system-test/buckets.test.js b/storage/system-test/buckets.test.js index d5055007d8..575ae3d837 100644 --- a/storage/system-test/buckets.test.js +++ b/storage/system-test/buckets.test.js @@ -141,3 +141,39 @@ it('should update and then remove bucket encryption enforcement configuration', const [metadata] = await bucket.getMetadata(); assert.ok(!metadata.encryption); }); + +it('should enable the bucket IP filter', () => { + const output = execSync( + `node enableBucketIpFilter.js ${bucketName} Disabled` + ); + assert.include(output, `IP Filter enabled for bucket ${bucketName}.`); + assert.include(output, '8.8.8.8/32'); +}); + +it('should get the bucket IP filter', () => { + const output = execSync(`node getBucketIpFilter.js ${bucketName}`); + assert.include(output, 'IP Filter Mode: Disabled'); + assert.include(output, '8.8.8.8/32'); +}); + +it('should list the bucket IP filters', () => { + const projectId = process.env.GCLOUD_PROJECT; + const output = execSync(`node listBucketIpFilters.js ${projectId}`); + assert.include(output, `${bucketName}: IP Filter Mode - Disabled`); + assert.include(output, 'Public Network Allowed IP Ranges:'); + assert.include(output, '- 8.8.8.8/32'); +}); + +it('should delete specific bucket IP filter rules', () => { + const output = execSync(`node deleteBucketIpFilterRules.js ${bucketName}`); + assert.include( + output, + `Specific IP Filter rules deleted for bucket ${bucketName}.` + ); +}); + +it('should disable the bucket IP filter', () => { + const output = execSync(`node disableBucketIpFilter.js ${bucketName}`); + assert.include(output, `IP Filter disabled for bucket ${bucketName}.`); + assert.include(output, '"mode": "Disabled"'); +}); From 832736b8502ea427a80c1512cfbb30c13cba92cd Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 12 Jun 2026 12:09:36 +0000 Subject: [PATCH 2/4] feat: update storage bucket IP filter samples to preserve existing metadata and optimize list retrieval --- storage/deleteBucketIpFilterRules.js | 1 + storage/enableBucketIpFilter.js | 2 ++ storage/listBucketIpFilters.js | 49 ++++++++++++---------------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/storage/deleteBucketIpFilterRules.js b/storage/deleteBucketIpFilterRules.js index cc419069da..26ce449739 100644 --- a/storage/deleteBucketIpFilterRules.js +++ b/storage/deleteBucketIpFilterRules.js @@ -44,6 +44,7 @@ function main(bucketName = 'my-bucket') { const updatedIpFilter = { ...metadata.ipFilter, publicNetworkSource: { + ...metadata.ipFilter.publicNetworkSource, allowedIpCidrRanges: updatedIpRanges, }, }; diff --git a/storage/enableBucketIpFilter.js b/storage/enableBucketIpFilter.js index 060fca0f99..7c78e83b3e 100644 --- a/storage/enableBucketIpFilter.js +++ b/storage/enableBucketIpFilter.js @@ -48,7 +48,9 @@ function main(bucketName = 'my-bucket', filterMode = 'Enabled') { const updatedIpFilter = { ...existingIpFilter, + mode: filterMode, publicNetworkSource: { + ...existingIpFilter.publicNetworkSource, allowedIpCidrRanges: updatedIpRanges, }, }; diff --git a/storage/listBucketIpFilters.js b/storage/listBucketIpFilters.js index ce8b4083a4..c51bcdd065 100644 --- a/storage/listBucketIpFilters.js +++ b/storage/listBucketIpFilters.js @@ -32,36 +32,29 @@ function main(projectId = 'my-project-id') { const [buckets] = await storage.getBuckets(); for (const bucket of buckets) { - if (bucket.metadata.ipFilter) { - try { - const [metadata] = await storage.bucket(bucket.name).getMetadata(); - const ipFilter = metadata.ipFilter; - console.log(`${bucket.name}: IP Filter Mode - ${ipFilter.mode}`); + const ipFilter = bucket.metadata?.ipFilter; + if (ipFilter) { + console.log(`${bucket.name}: IP Filter Mode - ${ipFilter.mode}`); - const publicNetworkSource = ipFilter.publicNetworkSource; - if (publicNetworkSource && publicNetworkSource.allowedIpCidrRanges) { - console.log(' Public Network Allowed IP Ranges:'); - publicNetworkSource.allowedIpCidrRanges.forEach(range => { - console.log(` - ${range}`); - }); - } + const publicNetworkSource = ipFilter.publicNetworkSource; + if (publicNetworkSource && publicNetworkSource.allowedIpCidrRanges) { + console.log(' Public Network Allowed IP Ranges:'); + publicNetworkSource.allowedIpCidrRanges.forEach(range => { + console.log(` - ${range}`); + }); + } - const vpcNetworkSources = ipFilter.vpcNetworkSources; - if (vpcNetworkSources && vpcNetworkSources.length > 0) { - console.log(' VPC Network Sources:'); - vpcNetworkSources.forEach(source => { - console.log(` - Network: ${source.network}`); - if (source.allowedIpCidrRanges) { - source.allowedIpCidrRanges.forEach(range => { - console.log(` - ${range}`); - }); - } - }); - } - } catch (err) { - console.log( - `${bucket.name}: Error fetching IP filter - ${err.message}` - ); + const vpcNetworkSources = ipFilter.vpcNetworkSources; + if (vpcNetworkSources && vpcNetworkSources.length > 0) { + console.log(' VPC Network Sources:'); + vpcNetworkSources.forEach(source => { + console.log(` - Network: ${source.network}`); + if (source.allowedIpCidrRanges) { + source.allowedIpCidrRanges.forEach(range => { + console.log(` - ${range}`); + }); + } + }); } } } From f86128dcec474ae91e6e4fc1da9d9e633d30c409 Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 12 Jun 2026 12:13:45 +0000 Subject: [PATCH 3/4] feat: fetch fresh bucket metadata to display detailed IP filter information with error handling --- storage/listBucketIpFilters.js | 49 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/storage/listBucketIpFilters.js b/storage/listBucketIpFilters.js index c51bcdd065..3f10b0ff6c 100644 --- a/storage/listBucketIpFilters.js +++ b/storage/listBucketIpFilters.js @@ -32,29 +32,36 @@ function main(projectId = 'my-project-id') { const [buckets] = await storage.getBuckets(); for (const bucket of buckets) { - const ipFilter = bucket.metadata?.ipFilter; - if (ipFilter) { - console.log(`${bucket.name}: IP Filter Mode - ${ipFilter.mode}`); + if (bucket.metadata?.ipFilter) { + try { + const [metadata] = await storage.bucket(bucket.name).getMetadata(); + const ipFilter = metadata.ipFilter; + console.log(`${bucket.name}: IP Filter Mode - ${ipFilter.mode}`); - const publicNetworkSource = ipFilter.publicNetworkSource; - if (publicNetworkSource && publicNetworkSource.allowedIpCidrRanges) { - console.log(' Public Network Allowed IP Ranges:'); - publicNetworkSource.allowedIpCidrRanges.forEach(range => { - console.log(` - ${range}`); - }); - } + const publicNetworkSource = ipFilter.publicNetworkSource; + if (publicNetworkSource && publicNetworkSource.allowedIpCidrRanges) { + console.log(' Public Network Allowed IP Ranges:'); + publicNetworkSource.allowedIpCidrRanges.forEach(range => { + console.log(` - ${range}`); + }); + } - const vpcNetworkSources = ipFilter.vpcNetworkSources; - if (vpcNetworkSources && vpcNetworkSources.length > 0) { - console.log(' VPC Network Sources:'); - vpcNetworkSources.forEach(source => { - console.log(` - Network: ${source.network}`); - if (source.allowedIpCidrRanges) { - source.allowedIpCidrRanges.forEach(range => { - console.log(` - ${range}`); - }); - } - }); + const vpcNetworkSources = ipFilter.vpcNetworkSources; + if (vpcNetworkSources && vpcNetworkSources.length > 0) { + console.log(' VPC Network Sources:'); + vpcNetworkSources.forEach(source => { + console.log(` - Network: ${source.network}`); + if (source.allowedIpCidrRanges) { + source.allowedIpCidrRanges.forEach(range => { + console.log(` - ${range}`); + }); + } + }); + } + } catch (err) { + console.log( + `${bucket.name}: Error fetching IP filter - ${err.message}` + ); } } } From 9fc66274f1ca8b7a75252b04ad62bacc9ead7747 Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 12 Jun 2026 15:08:37 +0000 Subject: [PATCH 4/4] feat: update bucket IP filter logging to display current mode and safely clone IP ranges --- storage/enableBucketIpFilter.js | 7 ++++--- storage/system-test/buckets.test.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/storage/enableBucketIpFilter.js b/storage/enableBucketIpFilter.js index 7c78e83b3e..45ee063e23 100644 --- a/storage/enableBucketIpFilter.js +++ b/storage/enableBucketIpFilter.js @@ -40,8 +40,9 @@ function main(bucketName = 'my-bucket', filterMode = 'Enabled') { }; // Add a new IP range to publicNetworkSource - const updatedIpRanges = - existingIpFilter.publicNetworkSource?.allowedIpCidrRanges || []; + const updatedIpRanges = [ + ...(existingIpFilter.publicNetworkSource?.allowedIpCidrRanges || []), + ]; if (!updatedIpRanges.includes('8.8.8.8/32')) { updatedIpRanges.push('8.8.8.8/32'); } @@ -59,7 +60,7 @@ function main(bucketName = 'my-bucket', filterMode = 'Enabled') { ipFilter: updatedIpFilter, }); - console.log(`IP Filter enabled for bucket ${bucketName}.`); + console.log(`IP Filter mode set to ${updatedMetadata.ipFilter.mode} for bucket ${bucketName}.`); console.log(JSON.stringify(updatedMetadata.ipFilter, null, 2)); } diff --git a/storage/system-test/buckets.test.js b/storage/system-test/buckets.test.js index 575ae3d837..2020c06519 100644 --- a/storage/system-test/buckets.test.js +++ b/storage/system-test/buckets.test.js @@ -146,7 +146,7 @@ it('should enable the bucket IP filter', () => { const output = execSync( `node enableBucketIpFilter.js ${bucketName} Disabled` ); - assert.include(output, `IP Filter enabled for bucket ${bucketName}.`); + assert.include(output, `IP Filter mode set to Disabled for bucket ${bucketName}.`); assert.include(output, '8.8.8.8/32'); });