├── .gitignore ├── .rubocop.yml ├── AUTHORS ├── CONTRIBUTING.md ├── Dockerfile ├── Gemfile ├── LICENSE ├── README.md ├── cloudbuild.yml ├── controls ├── 1.01-iam.rb ├── 1.02-iam.rb ├── 1.03-iam.rb ├── 1.04-iam.rb ├── 1.05-iam.rb ├── 1.06-iam.rb ├── 1.07-iam.rb ├── 1.08-iam.rb ├── 1.09-iam.rb ├── 1.10-iam.rb ├── 1.11-iam.rb ├── 1.12-iam.rb ├── 1.13-iam.rb ├── 1.14-iam.rb ├── 1.15-iam.rb ├── 2.01-logging.rb ├── 2.02-logging.rb ├── 2.03-logging.rb ├── 2.04-logging.rb ├── 2.05-logging.rb ├── 2.06-logging.rb ├── 2.07-logging.rb ├── 2.08-logging.rb ├── 2.09-logging.rb ├── 2.10-logging.rb ├── 2.11-logging.rb ├── 3.01-networking.rb ├── 3.02-networking.rb ├── 3.03-networking.rb ├── 3.04-networking.rb ├── 3.05-networking.rb ├── 3.06-networking.rb ├── 3.07-networking.rb ├── 3.08-networking.rb ├── 3.09-networking.rb ├── 4.01-vms.rb ├── 4.02-vms.rb ├── 4.03-vms.rb ├── 4.04-vms.rb ├── 4.05-vms.rb ├── 4.06-vms.rb ├── 4.07-vms.rb ├── 4.08-vms.rb ├── 5.01-storage.rb ├── 5.02-storage.rb ├── 6.01-db.rb ├── 6.02-db.rb ├── 6.03-db.rb ├── 6.04-db.rb ├── 6.05-db.rb ├── 6.06-db.rb ├── 6.07-db.rb ├── 7.01-bq.rb ├── 7.02-bq.rb └── 7.03-bq.rb ├── inspec.yml ├── renovate.json └── walkthrough.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/.tmp 2 | inspec.lock 3 | attrs.yml 4 | Gemfile.lock 5 | inputs.yml 6 | vendor 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AllCops: 3 | TargetRubyVersion: 2.6.3p62 4 | Exclude: 5 | - Gemfile 6 | 7 | Layout/LineLength: 8 | Enabled: false 9 | 10 | Naming/FileName: 11 | Enabled: true 12 | Regex: !ruby/regexp '/^.{3,100}$/' 13 | 14 | Metrics/BlockLength: 15 | Enabled: false 16 | 17 | Style/FrozenStringLiteralComment: 18 | Enabled: false 19 | 20 | Style/IdenticalConditionalBranches: 21 | Enabled: false 22 | 23 | Layout/EmptyLineAfterGuardClause: 24 | Enabled: false -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the list of inspec-gcp-cis-benchmark authors for copyright purposes. 2 | # 3 | # This does not necessarily list everyone who has contributed code, since in 4 | # some cases, their employer may be the copyright holder. To see the full list 5 | # of contributors, see the revision history in source control. 6 | Google LLC 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows 28 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM cincproject/auditor:4.22.0 16 | 17 | COPY . /share/. 18 | 19 | RUN gem install rubocop 20 | 21 | ENTRYPOINT ["cinc-auditor"] 22 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | source 'https://rubygems.org' 16 | 17 | gem 'inspec', '>= 3', '< 5' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GCP CIS 1.2.0 Benchmark Inspec Profile 2 | 3 | This repository holds the [Google Cloud Platform (GCP)](https://cloud.google.com/) [Center for Internet Security (CIS)](https://www.cisecurity.org) [version 1.2 Benchmark](https://www.cisecurity.org/benchmark/google_cloud_computing_platform/) [Inspec](https://www.inspec.io/) Profile. 4 | 5 | ## Required Disclaimer 6 | 7 | This is not an officially supported Google product. This code is intended to help users assess their security posture on the Google Cloud against the CIS Benchmark. This code is not certified by CIS. 8 | 9 | ## Coverage 10 | 11 | The following GCP CIS v1.2.0 Benchmark Controls are not covered: 12 | 13 | - Identity and Access Management 1.2 - "Ensure that multi-factor authentication is enabled for all non-service accounts" 14 | - Identity and Access Management 1.3 - "Ensure that Security Key Enforcement is enabled for all admin accounts" 15 | - Identity and Access Management 1.12 - "Ensure API keys are not created for a project" 16 | - Identity and Access Management 1.13 - "Ensure API keys are restricted to use by only specified Hosts and Apps" 17 | - Identity and Access Management 1.14 - "Ensure API keys are restricted to only APIs that application needs access" 18 | - Identity and Access Management 1.15 - "Ensure API keys are rotated every 90 days" 19 | - Cloud SQL Database Services 6.3 - "Ensure that MySql database instance does not allow anyone to connect with administrative privileges" 20 | - Cloud SQL Database Services 6.4 - "Ensure that MySQL Database Instance does not allows root login from any Host" 21 | 22 | ## Usage 23 | 24 | ### Profile Inputs (see `inspec.yml` file) 25 | 26 | This profile uses InSpec Inputs to make the tests more flexible. You are able to provide inputs at runtime either via the `cli` or via `YAML files` to help the profile work best in your deployment. 27 | 28 | **pro tip**: Do not change the inputs in the `inspec.yml` file directly, either: 29 | 30 | - update them via the cli - via the `--input` flag 31 | - pass them in via a YAML file as shown in the `Example` - via the `--input-file` flag 32 | 33 | Further details can be found here: 34 | 35 | ### (Required) User Provided Inputs - via the CLI or Input Files 36 | 37 | - **gcp_project_id** - (Default: null, type: String) - The target GCP Project you are scanning. 38 | 39 | ### (Optional) User Provided Inputs 40 | 41 | - **sa_key_older_than_seconds** - (Default: 7776000, type: int, CIS IAM 1.15) - The maximum allowed age of GCP User-managed Service Account Keys (90 days in seconds). 42 | - **kms_rotation_period_seconds** - (Default: 7776000, type: int, CIS IAM 1.10) - The maximum allowed age of KMS keys (90 days in seconds). 43 | 44 | ### Cloud Shell Walkthrough 45 | 46 | Use this Cloud Shell Walkthrough for a hands-on example. 47 | 48 | [![Open this project in Cloud Shell](http://gstatic.com/cloudssh/images/open-btn.png)](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/inspec-gcp-cis-benchmark&page=editor&tutorial=walkthrough.md) 49 | 50 | ### CLI Example 51 | 52 | #### Ruby Gem 53 | 54 | ``` 55 | #install inspec 56 | $ gem install inspec-bin -v 4.26.15 --no-document --quiet 57 | ``` 58 | 59 | ``` 60 | # make sure you're authenticated to GCP 61 | $ gcloud auth list 62 | 63 | # acquire credentials to use with Application Default Credentials 64 | $ gcloud auth application-default login 65 | 66 | ``` 67 | 68 | ``` 69 | # scan a project with this profile, replace {{project-id}} with your project ID 70 | $ inspec exec https://github.com/GoogleCloudPlatform/inspec-gcp-cis-benchmark.git -t gcp:// --input gcp_project_id={{project-id}} --reporter cli json:{{project-id}}_scan.json 71 | ...snip... 72 | Profile Summary: 48 successful controls, 5 control failures, 7 controls skipped 73 | Test Summary: 166 successful, 7 failures, 7 skipped 74 | ``` 75 | 76 | #### Docker 77 | ``` 78 | # pull inspec image 79 | $ docker pull chef/inspec:4.26.15 80 | ``` 81 | 82 | ``` 83 | # make sure you're authenticated to GCP 84 | $ gcloud auth list 85 | 86 | # acquire credentials to use with Application Default Credentials 87 | $ gcloud auth application-default login 88 | 89 | ``` 90 | 91 | ``` 92 | # create function for convenience 93 | $ function inspec-docker { docker run -it -e GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS=true --rm -v ~/.config:/root/.config -v $(pwd):/share chef/inspec:4.26.15 "$@"; } 94 | 95 | # scan a project with this profile, replace {{project-id}} with your project ID 96 | $ inspec-docker exec https://github.com/GoogleCloudPlatform/inspec-gcp-cis-benchmark.git -t gcp:// --input gcp_project_id={{project-id}} --reporter cli json:{{project-id}}_scan.json 97 | ...snip... 98 | Profile Summary: 48 successful controls, 5 control failures, 7 controls skipped 99 | Test Summary: 166 successful, 7 failures, 7 skipped 100 | ``` 101 | 102 | ### Required APIs 103 | 104 | Consider these GCP projects, which may all be the same or different: 105 | 106 | - the project of the Service Account that's used to authenticate the scan 107 | - the project from which the benchmark is called 108 | - the project to be scanned 109 | 110 | The following GCP APIs should be enabled in **all** of these projects: 111 | 112 | - cloudkms.googleapis.com 113 | - cloudresourcemanager.googleapis.com 114 | - compute.googleapis.com 115 | - dns.googleapis.com 116 | - iam.googleapis.com 117 | - logging.googleapis.com 118 | - monitoring.googleapis.com 119 | - sqladmin.googleapis.com 120 | - storage-api.googleapis.com 121 | 122 | ### Required Permissions 123 | 124 | The following permissions are required to run the CIS benchmark profile: 125 | 126 | On organization level: 127 | 128 | - resourcemanager.organizations.get 129 | - resourcemanager.projects.get 130 | - resourcemanager.projects.getIamPolicy 131 | - resourcemanager.folders.get 132 | 133 | On project level: 134 | 135 | - cloudkms.cryptoKeys.get 136 | - cloudkms.cryptoKeys.getIamPolicy 137 | - cloudkms.cryptoKeys.list 138 | - cloudkms.keyRings.list 139 | - cloudsql.instances.get 140 | - cloudsql.instances.list 141 | - compute.firewalls.get 142 | - compute.firewalls.list 143 | - compute.instances.get 144 | - compute.instances.list 145 | - compute.networks.get 146 | - compute.networks.list 147 | - compute.projects.get 148 | - compute.regions.list 149 | - compute.sslPolicies.get 150 | - compute.sslPolicies.list 151 | - compute.subnetworks.get 152 | - compute.subnetworks.list 153 | - compute.targetHttpsProxies.get 154 | - compute.targetHttpsProxies.list 155 | - compute.zones.list 156 | - dns.managedZones.get 157 | - dns.managedZones.list 158 | - iam.serviceAccountKeys.list 159 | - iam.serviceAccounts.list 160 | - logging.logMetrics.list 161 | - logging.sinks.get 162 | - logging.sinks.list 163 | - monitoring.alertPolicies.list 164 | - resourcemanager.projects.get 165 | - resourcemanager.projects.getIamPolicy 166 | - storage.buckets.get 167 | - storage.buckets.getIamPolicy 168 | - storage.buckets.list 169 | -------------------------------------------------------------------------------- /cloudbuild.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | substitutions: 16 | _GCP_PROJECT_ID: '' 17 | _REPORTS_BUCKET: '' 18 | 19 | timeout: 3000s 20 | steps: 21 | - id: 'Build Docker container' 22 | name: 'gcr.io/cloud-builders/docker' 23 | args: [ 'build', '-t', 'gcr.io/${_GCP_PROJECT_ID}/inspec-gcp-cis-benchmark:${_PR_NUMBER}', '.' ] 24 | 25 | - id: 'Verify InSpec controls' 26 | waitFor: ['Build Docker container'] 27 | name: gcr.io/${_GCP_PROJECT_ID}/inspec-gcp-cis-benchmark:${_PR_NUMBER} 28 | entrypoint: '/bin/sh' 29 | args: 30 | - '-c' 31 | - | 32 | cinc-auditor vendor /share/. 33 | cinc-auditor check /share/. 34 | 35 | - id: 'Rubocop' 36 | waitFor: ['Verify InSpec controls'] 37 | name: gcr.io/${_GCP_PROJECT_ID}/inspec-gcp-cis-benchmark:${_PR_NUMBER} 38 | entrypoint: '/bin/sh' 39 | args: 40 | - '-c' 41 | - | 42 | rubocop /share/. 43 | 44 | - id: 'Write input file' 45 | waitFor: ['Rubocop'] 46 | name: gcr.io/${_GCP_PROJECT_ID}/inspec-gcp-cis-benchmark:${_PR_NUMBER} 47 | entrypoint: '/bin/sh' 48 | args: 49 | - '-c' 50 | - | 51 | cat < /workspace/inputs.yml 52 | gcp_project_id: "${_GCP_PROJECT_ID}" 53 | gcp_gke_locations: 54 | - 'us-central1' 55 | gce_zones: 56 | - 'us-central1' 57 | - 'us-central1-a' 58 | - 'us-central1-b' 59 | - 'us-central1-c' 60 | - 'us-central1-d' 61 | - 'us-central1-e' 62 | - 'us-central1-f' 63 | EOF 64 | cat /workspace/inputs.yml 65 | 66 | - id: 'Run CIS Profile on in-scope project' 67 | waitFor: ['Write input file'] 68 | name: gcr.io/${_GCP_PROJECT_ID}/inspec-gcp-cis-benchmark:${_PR_NUMBER} 69 | entrypoint: '/bin/sh' 70 | args: 71 | - '-c' 72 | - | 73 | cinc-auditor exec /share/. -t gcp:// \ 74 | --input-file /workspace/inputs.yml \ 75 | --reporter cli json:/workspace/cis_report.json html:/workspace/cis_report.html | tee out.json 76 | 77 | - id: 'Test Compliance' 78 | waitFor: ['Run CIS Profile on in-scope project'] 79 | name: mitre/inspec_tools 80 | args: ['compliance', '-j', '/workspace/cis_report.json', '-i', '{failed.total.max: 1}'] 81 | 82 | - id: 'Store json Report' 83 | waitFor: ['Test Compliance'] 84 | name: gcr.io/cloud-builders/gsutil 85 | args: 86 | - cp 87 | - /workspace/cis_report.json 88 | - gs://${_REPORTS_BUCKET}/cis_report-${BUILD_ID}.json -------------------------------------------------------------------------------- /controls/1.01-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that corporate login credentials are used' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.1' 21 | control_abbrev = 'iam' 22 | 23 | iam_bindings_cache = IAMBindingsCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'high' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that corporate login credentials are used" 29 | 30 | desc 'Use corporate login credentials instead of personal accounts, such as Gmail accounts.' 31 | desc 'rationale', "It is recommended fully-managed corporate Google accounts be used for increased visibility, auditing, and controlling access to Cloud Platform resources. Email accounts based outside of the user's organization, such as personal accounts, should not be used for business purposes." 32 | 33 | tag cis_scored: true 34 | tag cis_level: 1 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: ['AC-2'] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/docs/enterprise/best-practices-for-enterprise-organizations#use_corporate_login_credentials' 42 | 43 | # determine the organization's email domain 44 | case google_project(project: gcp_project_id).parent.type 45 | when 'organization' 46 | org_domain = google_organization(name: "organizations/#{google_project(project: gcp_project_id).parent.id}").display_name 47 | when 'folder' 48 | parent = 'folder' 49 | folder_id = google_project(project: gcp_project_id).parent.id 50 | while parent == 'folder' 51 | if google_resourcemanager_folder(name: "folders/#{folder_id}").parent.include?('folders') 52 | folder_id = google_resourcemanager_folder(name: "folders/#{folder_id}").parent.sub('folders/', '') 53 | else 54 | parent = 'organization' 55 | org_domain = google_organization(name: google_resourcemanager_folder(name: "folders/#{folder_id}").parent.to_s).display_name 56 | end 57 | end 58 | end 59 | 60 | iam_bindings_cache.iam_binding_roles.each do |role| 61 | iam_bindings_cache.iam_bindings[role].members.each do |member| 62 | next if member.to_s.end_with?('.gserviceaccount.com') 63 | describe "[#{gcp_project_id}] [Role:#{role}] Its member #{member}" do 64 | subject { member.to_s } 65 | it { should match(/@#{org_domain}/) } 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /controls/1.02-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that multi-factor authentication is enabled for all non-service accounts' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.2' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that multi-factor authentication is enabled for all non-service accounts" 27 | 28 | desc 'Setup multi-factor authentication for Google Cloud Platform accounts.' 29 | desc 'rationale', 'Multi-factor authentication requires more than one mechanism to authenticate a user. This secures your logins from attackers exploiting stolen or weak credentials.' 30 | 31 | tag cis_scored: false 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['IA-2'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/solutions/securing-gcp-account-u2f' 40 | 41 | describe 'This control is not scored' do 42 | skip 'This control is not scored' 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /controls/1.03-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Security Key Enforcement is enabled for all admin accounts' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.3' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that Security Key Enforcement is enabled for all admin accounts" 27 | 28 | desc 'Setup Security Key Enforcement for Google Cloud Platform admin accounts.' 29 | desc 'rationale', 'Google Cloud Platform users with Organization Administrator roles have the highest level of privilege in the organization. These accounts should be protected with the strongest form of two-factor authentication: Security Key Enforcement. Ensure that admins use Security Keys to log in instead of weaker second factors like SMS or one-time passwords (OTP). Security Keys are actual physical keys used to access Google Organization Administrator Accounts. They send an encrypted signature rather than a code, ensuring that logins cannot be phished.' 30 | 31 | tag cis_scored: false 32 | tag cis_level: 2 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['IA-2'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/security-key/' 40 | 41 | describe 'This control is not scored' do 42 | skip 'This control is not scored' 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /controls/1.04-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that there are only GCP-managed service account keys for each service account' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.4' 21 | control_abbrev = 'iam' 22 | 23 | service_account_cache = ServiceAccountCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'none' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that there are only GCP-managed service account keys for each service account" 29 | 30 | desc 'User managed service account should not have user managed keys.' 31 | desc 'rationale', 'Anyone who has access to the keys will be able to access resources through the service account. GCP-managed keys are used by Cloud Platform services such as App Engine and Compute Engine. These keys cannot be downloaded. Google will keep the keys and automatically rotate them on an approximately weekly basis. User-managed keys are created, downloadable, and managed by users. They expire 10 years from creation. 32 | 33 | For user-managed keys, user have to take ownership of key management activities which includes: 34 | - Key storage 35 | - Key distribution 36 | - Key revocation 37 | - Key rotation 38 | - Protecting the keys from unauthorized users 39 | - Key recovery 40 | 41 | Even after owners precaution, keys can be easily leaked by common development malpractices like checking keys into the source code or leaving them in Downloads directory, or accidentally leaving them on support blogs/channels. It is recommended to prevent use of User-managed service account keys.' 42 | 43 | tag cis_scored: true 44 | tag cis_level: 1 45 | tag cis_gcp: control_id.to_s 46 | tag cis_version: cis_version.to_s 47 | tag project: gcp_project_id.to_s 48 | tag nist: ['AC-2'] 49 | 50 | ref 'CIS Benchmark', url: cis_url.to_s 51 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-service-accounts#managing_service_account_keys' 52 | 53 | service_account_cache.service_account_emails.each do |sa_email| 54 | if service_account_cache.service_account_keys[sa_email].key_names.count > 1 55 | impact 'medium' 56 | describe "[#{gcp_project_id}] Service Account: #{sa_email}" do 57 | subject { service_account_cache.service_account_keys[sa_email] } 58 | its('key_types') { should_not include 'USER_MANAGED' } 59 | end 60 | else 61 | describe "[#{gcp_project_id}] ServiceAccount [#{sa_email}] does not have user-managed keys. This test is Not Applicable." do 62 | skip "[#{gcp_project_id}] ServiceAccount [#{sa_email}] does not have user-managed keys." 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /controls/1.05-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that ServiceAccount has no Admin privileges.' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.5' 21 | control_abbrev = 'iam' 22 | 23 | iam_bindings_cache = IAMBindingsCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'medium' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that ServiceAccount has no Admin privileges." 29 | 30 | desc "A service account is a special Google account that belongs to your application or a VM, instead of to an individual end user. Your application uses the service account to call the Google API of a service, so that the users aren't directly involved. It's recommended not to use admin access for ServiceAccount." 31 | desc 'rationale', "Service accounts represent service-level security of the Resources (application or a VM) which can be determined by the roles assigned to it. Enrolling ServiceAccount with Admin rights gives full access to assigned application or a VM, ServiceAccount Access holder can perform critical actions like delete, update change settings etc. without the intervention of user, so It's recommended not to have Admin rights. 32 | This recommendation is applicable only for User-Managed user created service account (Service account with nomenclature: SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com)." 33 | 34 | tag cis_scored: true 35 | tag cis_level: 1 36 | tag cis_gcp: control_id.to_s 37 | tag cis_version: cis_version.to_s 38 | tag project: gcp_project_id.to_s 39 | tag nist: ['AC-2'] 40 | 41 | ref 'CIS Benchmark', url: cis_url.to_s 42 | ref 'GCP Docs', url: 'https://cloud.google.com/sdk/gcloud/reference/iam/service-accounts/' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-roles' 44 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-service-accounts' 45 | 46 | iam_bindings_cache.iam_bindings.keys.grep(/admin/i).each do |role| 47 | role_bindings = iam_bindings_cache.iam_bindings[role] 48 | if role_bindings.members.nil? 49 | impact 'none' 50 | describe "[#{gcp_project_id}] Role bindings for role [#{role}] do not contain any members. This test is Not Applicable." do 51 | skip "[#{gcp_project_id}] role bindings for role [#{role}] do not contain any members." 52 | end 53 | else 54 | describe "[#{gcp_project_id}] Admin role [#{role}]" do 55 | subject { role_bindings } 56 | its('members') { should_not include(/@[a-z][a-z0-9|-]{4,28}[a-z].iam.gserviceaccount.com/) } 57 | end 58 | end 59 | end 60 | 61 | iam_bindings_cache.iam_bindings.keys.grep(%r{roles/editor}).each do |role| 62 | members_in_scope = [] 63 | iam_bindings_cache.iam_bindings[role].members.each do |member| 64 | next if member.include? '@containerregistry.iam.gserviceaccount.com' 65 | members_in_scope.push(member) 66 | end 67 | describe "[#{gcp_project_id}] Project Editor Role" do 68 | subject { members_in_scope } 69 | it { should_not include(/@[a-z][a-z0-9|-]{4,28}[a-z].iam.gserviceaccount.com/) } 70 | end 71 | end 72 | 73 | iam_bindings_cache.iam_bindings.keys.grep(%r{roles/owner}).each do |role| 74 | describe "[#{gcp_project_id}] Project Owner Role" do 75 | subject { iam_bindings_cache.iam_bindings[role] } 76 | its('members') { should_not include(/@[a-z][a-z0-9|-]{4,28}[a-z].iam.gserviceaccount.com/) } 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /controls/1.06-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that IAM users are not assigned Service Account User role at project level' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.6' 21 | control_abbrev = 'iam' 22 | 23 | iam_bindings_cache = IAMBindingsCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'medium' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that IAM users are not assigned Service Account User role at project level" 29 | 30 | desc "It is recommended to assign Service Account User (iam.serviceAccountUser) role to a 31 | user for a specific service account rather than assigning the role to a user at project level." 32 | desc 'rationale', "A service account is a special Google account that belongs to application or a virtual machine (VM), instead of to an individual end user. Application/VM-Instance uses the service account to call the Google API of a service, so that the users aren't directly involved. In addition to being an identity, a service account is a resource which has IAM policies attached to it. These policies determine who can use the service account. 33 | 34 | Users with IAM roles to update the App Engine and Compute Engine instances (such as App Engine Deployer or Compute Instance Admin) can effectively run code as the service accounts used to run these instances, and indirectly gain access to all the resources for which the service accounts has access. Similarly, SSH access to a Compute Engine instance may also provide the ability to execute code as that instance/Service account. 35 | 36 | As per business needs, there could be multiple user-managed service accounts configured for a project. Granting the iam.serviceAccountUser role to a user for a project gives the user access to all service accounts in the project, including service accounts that may be created in the future. This can result into elevation of privileges by using service accounts and corresponding Compute Engine instances. 37 | 38 | In order to implement least privileges best practices, IAM users should not be assigned Service Account User role at project level. Instead iam.serviceAccountUser role should be assigned to a user for a specific service account giving a user access to the service account." 39 | 40 | tag cis_scored: true 41 | tag cis_level: 1 42 | tag cis_gcp: control_id.to_s 43 | tag cis_version: cis_version.to_s 44 | tag project: gcp_project_id.to_s 45 | tag nist: %w[AC-2 AC-3] 46 | 47 | ref 'CIS Benchmark', url: cis_url.to_s 48 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/service-accounts' 49 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/granting-roles-to-service-accounts' 50 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-roles' 51 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/granting-changing-revoking-access' 52 | 53 | describe "[#{gcp_project_id}] A project-level binding of ServiceAccountUser" do 54 | subject { iam_bindings_cache.iam_bindings['roles/iam.serviceAccountUser'] } 55 | it { should eq nil } 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /controls/1.07-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure user-managed/external keys for service accounts are rotated every 90 days or less' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.7' 21 | control_abbrev = 'iam' 22 | sa_key_older_than_seconds = input('sa_key_older_than_seconds') 23 | 24 | service_account_cache = ServiceAccountCache(project: gcp_project_id) 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'none' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure user-managed/external keys for service accounts are rotated every 90 days or less" 30 | 31 | desc 'Service Account keys consist of a key ID (Private_key_Id) and Private key, which are used to sign programmatic requests that you make to Google cloud services accessible to that particular Service account. It is recommended that all Service Account keys are regularly rotated.' 32 | desc 'rationale', "Rotating Service Account keys will reduce the window of opportunity for an access key that is associated with a compromised or terminated account to be used. Service Account keys should be rotated to ensure that data cannot be accessed with an old key which might have been lost, cracked, or stolen. 33 | 34 | Each service account is associated with a key pair, which is managed by Google Cloud Platform (GCP). It is used for service-to-service authentication within GCP. Google rotates the keys daily. 35 | 36 | GCP provides option to create one or more user-managed (also called as external key pairs) key pairs for use from outside GCP (for example, for use with Application Default Credentials). When a new key pair is created, user is enforced download the private key (which is not retained by Google). With external keys, users are responsible for security of the private key and other management operations such as key rotation. External keys can be managed by the IAM API, gcloud command-line tool, or the Service Accounts page in the Google Cloud Platform Console. GCP facilitates up to 10 external service account keys per service account to facilitate key rotation." 37 | 38 | tag cis_scored: true 39 | tag cis_level: 1 40 | tag cis_gcp: control_id.to_s 41 | tag cis_version: cis_version.to_s 42 | tag project: gcp_project_id.to_s 43 | tag nist: ['AC-2'] 44 | 45 | ref 'CIS Benchmark', url: cis_url.to_s 46 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-service-accounts#managing_service_account_keys' 47 | ref 'GCP Docs', url: 'https://cloud.google.com/sdk/gcloud/reference/iam/service-accounts/keys/list' 48 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/service-accounts' 49 | 50 | service_account_cache.service_account_emails.each do |sa_email| 51 | if service_account_cache.service_account_keys[sa_email].key_names.count > 1 52 | impact 'medium' 53 | describe "[#{gcp_project_id}] ServiceAccount Keys for #{sa_email} older than #{sa_key_older_than_seconds} seconds" do 54 | subject { service_account_cache.service_account_keys[sa_email].where { (Time.now - sa_key_older_than_seconds > valid_after_time) } } 55 | it { should_not exist } 56 | end 57 | else 58 | describe "[#{gcp_project_id}] ServiceAccount [#{sa_email}] does not have user-managed keys. This test is Not Applicable." do 59 | skip "[#{gcp_project_id}] ServiceAccount [#{sa_email}] does not have user-managed keys." 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /controls/1.08-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Separation of duties is enforced while assigning service account related roles to users' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.8' 21 | control_abbrev = 'iam' 22 | 23 | iam_bindings_cache = IAMBindingsCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'none' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that Separation of duties is enforced while assigning service account related roles to users" 29 | 30 | desc "It is recommended that the principle of 'Separation of Duties' is enforced while assigning service account related roles to users." 31 | desc 'rationale', "Built-in/Predefined IAM role Service Account admin allows user/identity to create, delete, manage service account(s). Built-in/Predefined IAM role Service Account User allows user/identity (with adequate privileges on Compute and App Engine) to assign service account(s) to Apps/Compute Instances. 32 | 33 | Separation of duties is the concept of ensuring that one individual does not have all necessary permissions to be able to complete a malicious action. In Cloud IAM - service accounts, this could be an action such as using a service account to access resources that user should not normally have access to. Separation of duties is a business control typically used in larger organizations, meant to help avoid security or privacy incidents and errors. It is considered best practice. 34 | 35 | Any user(s) should not have Service Account Admin and Service Account User, both roles assigned at a time." 36 | 37 | tag cis_scored: false 38 | tag cis_level: 2 39 | tag cis_gcp: control_id.to_s 40 | tag cis_version: cis_version.to_s 41 | tag project: gcp_project_id.to_s 42 | tag nist: %w[AC-2 AC-3] 43 | 44 | ref 'CIS Benchmark', url: cis_url.to_s 45 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/service-accounts' 46 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-roles' 47 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/granting-roles-to-service-accounts' 48 | 49 | sa_admins = iam_bindings_cache.iam_bindings['roles/iam.serviceAccountAdmin'] 50 | if sa_admins.nil? || sa_admins.members.count.zero? 51 | describe "[#{gcp_project_id}] does not contain users with roles/serviceAccountAdmin. This test is Not Applicable." do 52 | skip "[#{gcp_project_id}] does not contain users with roles/serviceAccountAdmin" 53 | end 54 | elsif iam_bindings_cache.iam_bindings['roles/iam.serviceAccountUser'].nil? 55 | describe "[#{gcp_project_id}] does not contain users with roles/serviceAccountUser. This test is Not Applicable." do 56 | skip "[#{gcp_project_id}] does not contain users with roles/serviceAccountUser" 57 | end 58 | else 59 | impact 'medium' 60 | describe "[#{gcp_project_id}] roles/serviceAccountUser" do 61 | subject { iam_bindings_cache.iam_bindings['roles/iam.serviceAccountUser'] } 62 | sa_admins.members.each do |sa_admin| 63 | its('members.to_s') { should_not match sa_admin } 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /controls/1.09-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud KMS cryptokeys are not anonymously or publicly accessible' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.9' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'none' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that Cloud KMS cryptokeys are not anonymously or publicly accessible" 27 | 28 | desc 'It is recommended that the IAM policy on Cloud KMS cryptokeys should restrict anonymous and/or public access.' 29 | 30 | desc 'rationale', 'Granting permissions to allUsers or allAuthenticatedUsers allows anyone to access the dataset. Such access might not be desirable if sensitive data is stored at the location. In this case, ensure that anonymous and/or public access to a Cloud KMS cryptokey is not allowed.' 31 | 32 | tag cis_scored: true 33 | tag cis_level: 1 34 | tag cis_gcp: control_id.to_s 35 | tag cis_version: cis_version.to_s 36 | tag project: gcp_project_id.to_s 37 | tag nist: ['AC-3'] 38 | 39 | ref 'CIS Benchmark', url: cis_url.to_s 40 | ref 'GCP Docs', url: 'https://cloud.google.com/kms/docs/key-rotation#frequency_of_key_rotation' 41 | 42 | # Get all "normal" regions and add dual/multi regions 43 | locations = google_compute_regions(project: gcp_project_id).region_names 44 | locations << 'global' << 'asia' << 'europe' << 'us' << 'eur4' << 'nam4' 45 | kms_cache = KMSKeyCache(project: gcp_project_id, locations: locations) 46 | 47 | # Ensure that keys aren't publicly accessible 48 | locations.each do |location| 49 | if kms_cache.kms_key_ring_names[location].empty? 50 | describe "[#{gcp_project_id}] does not contain any key rings in [#{location}]. This test is Not Applicable." do 51 | skip "[#{gcp_project_id}] does not contain any key rings in [#{location}]" 52 | end 53 | else 54 | kms_cache.kms_key_ring_names[location].each do |keyring| 55 | if kms_cache.kms_crypto_keys[location][keyring].empty? 56 | describe "[#{gcp_project_id}] key ring [#{keyring}] does not contain any cryptographic keys. This test is Not Applicable." do 57 | skip "[#{gcp_project_id}] key ring [#{keyring}] does not contain any cryptographic keys" 58 | end 59 | else 60 | kms_cache.kms_crypto_keys[location][keyring].each do |keyname| 61 | if google_kms_crypto_key_iam_policy(project: gcp_project_id, location: location, key_ring_name: keyring, crypto_key_name: keyname).bindings.nil? 62 | describe "[#{gcp_project_id}] key ring [#{keyring}] key [#{keyname}] does not have any IAM bindings. This test is Not Applicable." do 63 | skip "[#{gcp_project_id}] key ring [#{keyring}] key [#{keyname}] does not have any IAM bindings" 64 | end 65 | else 66 | impact 'medium' 67 | google_kms_crypto_key_iam_policy(project: gcp_project_id, location: location, key_ring_name: keyring, crypto_key_name: keyname).bindings.each do |binding| 68 | describe binding do 69 | its('members') { should_not include 'allUsers' } 70 | its('members') { should_not include 'allAuthenticatedUsers' } 71 | end 72 | end 73 | end 74 | end 75 | end 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /controls/1.10-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure Encryption keys are rotated within a period of 90 days' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.10' 21 | control_abbrev = 'iam' 22 | kms_rotation_period_seconds = input('kms_rotation_period_seconds') 23 | 24 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 25 | impact 'none' 26 | 27 | title "[#{control_abbrev.upcase}] Ensure Encryption keys are rotated within a period of 90 days" 28 | 29 | desc "Google Cloud Key Management Service (KMS) stores cryptographic keys in a hierarchical structure designed for useful and elegant access control management. 30 | 31 | Automatic cryptographic key rotation is only available for symmetric keys. Cloud KMS does not support automatic rotation of asymmetric keys so such keys are out of scope for this control. More information can be found in the GCP documentation references of this control. 32 | 33 | The format for the rotation schedule depends on the client library that is used. For the gcloud command-line tool, the next rotation time must be in ISO or RFC3339 format, and the rotation period must be in the form INTEGER[UNIT], where units can be one of seconds (s), minutes (m), hours (h) or days (d)." 34 | 35 | desc 'rationale', "Set a key rotation period and starting time. A key can be created with a specified rotation period, which is the time between when new key versions are generated automatically. A key can also be created with a specified next rotation time. A key is a named object representing a cryptographic key used for a specific purpose. The key material, the actual bits used for encryption, can change over time as new key versions are created. 36 | 37 | A key is used to protect some corpus of data. You could encrypt a collection of files with the same key, and people with decrypt permissions on that key would be able to decrypt those files. Hence it's necessary to make sure rotation period is set to specific time." 38 | 39 | tag cis_scored: true 40 | tag cis_level: 1 41 | tag cis_gcp: control_id.to_s 42 | tag cis_version: cis_version.to_s 43 | tag project: gcp_project_id.to_s 44 | tag nist: ['AC-2'] 45 | 46 | ref 'CIS Benchmark', url: cis_url.to_s 47 | ref 'GCP Docs', url: 'https://cloud.google.com/kms/docs/key-rotation#frequency_of_key_rotation' 48 | ref 'GCP Docs', url: 'https://cloud.google.com/kms/docs/key-rotation#asymmetric' 49 | 50 | # Get all "normal" regions and add dual/multi regions 51 | locations = google_compute_regions(project: gcp_project_id).region_names 52 | locations << 'global' << 'asia' << 'europe' << 'us' << 'eur4' << 'nam4' 53 | 54 | kms_cache = KMSKeyCache(project: gcp_project_id, locations: locations) 55 | 56 | # Ensure KMS keys autorotate 90d or less 57 | locations.each do |location| 58 | if kms_cache.kms_key_ring_names[location].empty? 59 | describe "[#{gcp_project_id}] does not contain any key rings in [#{location}]. This test is Not Applicable." do 60 | skip "[#{gcp_project_id}] does not contain any key rings in [#{location}]" 61 | end 62 | else 63 | kms_cache.kms_key_ring_names[location].each do |keyring| 64 | if kms_cache.kms_crypto_keys[location][keyring].empty? 65 | describe "[#{gcp_project_id}] key ring [#{keyring}] does not contain any cryptographic keys. This test is Not Applicable." do 66 | skip "[#{gcp_project_id}] key ring [#{keyring}] does not contain any cryptographic keys" 67 | end 68 | else 69 | kms_cache.kms_crypto_keys[location][keyring].each do |keyname| 70 | key = google_kms_crypto_key(project: gcp_project_id, location: location, key_ring_name: keyring, name: keyname) 71 | next unless key.purpose == 'ENCRYPT_DECRYPT' && key.primary_state == 'ENABLED' 72 | impact 'medium' 73 | describe "[#{gcp_project_id}] #{key.crypto_key_name}" do 74 | subject { key } 75 | its('rotation_period') { should_not eq nil } 76 | unless key.rotation_period.nil? 77 | rotation_period_int = key.rotation_period.delete_suffix('s').to_i 78 | it "should have a lower or equal rotation period than #{kms_rotation_period_seconds}" do 79 | expect(rotation_period_int).to be <= kms_rotation_period_seconds 80 | end 81 | its('next_rotation_time') { should be <= (Time.now + kms_rotation_period_seconds) } 82 | end 83 | end 84 | end 85 | end 86 | end 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /controls/1.11-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Separation of duties is enforced while assigning KMS related roles to users' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.11' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that Separation of duties is enforced while assigning KMS related roles to users" 27 | 28 | desc "It is recommended that the principle of 'Separation of Duties' is enforced while assigning KMS related roles to users." 29 | 30 | desc 'rationale', "Built-in/Predefined IAM role Cloud KMS Admin allows user/identity to create, delete, and manage service account(s). Built-in/Predefined IAM role Cloud KMS CryptoKey Encrypter/Decrypter allows user/identity (with adequate privileges on concerned resources) to encrypt and decrypt data at rest using encryption key(s). Built-in/Predefined IAM role Cloud KMS CryptoKey Encrypter allows user/identity (with adequate privileges on concerned resources) to encrypt data at rest using encryption key(s). Builtin/Predefined IAM role Cloud KMS CryptoKey Decrypter allows user/identity (with adequate privileges on concerned resources) to decrypt data at rest using encryption key(s). 31 | 32 | Separation of duties is the concept of ensuring that one individual does not have all necessary permissions to be able to complete a malicious action. In Cloud KMS, this could be an action such as using a key to access and decrypt data that that user should not normally have access to. Separation of duties is a business control typically used in larger organizations, meant to help avoid security or privacy incidents and errors. It is considered best practice. 33 | 34 | Any user(s) should not have Cloud KMS Admin and any of the Cloud KMS CryptoKey Encrypter/Decrypter, Cloud KMS CryptoKey Encrypter, Cloud KMS CryptoKey Decrypter roles assigned at a time." 35 | 36 | tag cis_scored: true 37 | tag cis_level: 2 38 | tag cis_gcp: control_id.to_s 39 | tag cis_version: cis_version.to_s 40 | tag project: gcp_project_id.to_s 41 | tag nist: %w[AC-2 AC-3 AC-6] 42 | 43 | ref 'CIS Benchmark', url: cis_url.to_s 44 | ref 'GCP Docs', url: 'https://cloud.google.com/kms/docs/separation-of-duties' 45 | 46 | kms_admins = google_project_iam_binding(project: gcp_project_id, role: 'roles/cloudkms.admin') 47 | 48 | if kms_admins.members.nil? || kms_admins.members.count.zero? 49 | impact 'none' 50 | describe "[#{gcp_project_id}] does not have users with roles/CloudKMSAdmin. This test is Not Applicable." do 51 | skip "[#{gcp_project_id}] does not have users with roles/CloudKMSAdmin" 52 | end 53 | else 54 | describe "[#{gcp_project_id}] roles/cloudkms.cryptoKeyEncrypter" do 55 | subject { google_project_iam_binding(project: gcp_project_id, role: 'roles/cloudkms.cryptoKeyEncrypter') } 56 | kms_admins.members.each do |kms_admin| 57 | its('members.to_s') { should_not match kms_admin } 58 | end 59 | end 60 | describe "[#{gcp_project_id}] roles/cloudkms.cryptoKeyDecrypter" do 61 | subject { google_project_iam_binding(project: gcp_project_id, role: 'roles/cloudkms.cryptoKeyDecrypter') } 62 | kms_admins.members.each do |kms_admin| 63 | its('members.to_s') { should_not match kms_admin } 64 | end 65 | end 66 | describe "[#{gcp_project_id}] roles/cloudkms.cryptoKeyEncrypterDecrypter" do 67 | subject { google_project_iam_binding(project: gcp_project_id, role: 'roles/cloudkms.cryptoKeyEncrypterDecrypter') } 68 | kms_admins.members.each do |kms_admin| 69 | its('members.to_s') { should_not match kms_admin } 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /controls/1.12-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure API keys are not created for a project' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.12' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure API keys are not created for a project" 27 | 28 | desc 'Keys are insecure because they can be viewed publicly, such as from within a browser, or they can be accessed on a device where the key resides. It is recommended to use standard authentication flow instead.' 29 | desc 'rationale', "Security risks involved in using API-Keys are below: 30 | 31 | - API keys are a simple encrypted strings 32 | - API keys do not identify the user or the application making the API request 33 | - API keys are typically accessible to clients, making it easy to discover and steal an API key 34 | 35 | To avoid security risk by using API keys, it is recommended to use standard authentication 36 | flow instead." 37 | 38 | tag cis_scored: false 39 | tag cis_level: 2 40 | tag cis_gcp: control_id.to_s 41 | tag cis_version: cis_version.to_s 42 | tag project: gcp_project_id.to_s 43 | tag nist: ['AC-2'] 44 | 45 | ref 'CIS Benchmark', url: cis_url.to_s 46 | ref 'GCP Docs', url: 'https://cloud.google.com/docs/authentication/api-keys' 47 | 48 | describe 'This control is not scored' do 49 | skip 'This control is not scored' 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /controls/1.13-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure API keys are restricted to use by only specified Hosts and Apps' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.13' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure API keys are restricted to use by only specified Hosts and Apps" 27 | 28 | desc 'Unrestricted keys are insecure because they can be viewed publicly, such as from within a browser, or they can be accessed on a device where the key resides. It is recommended to restrict API key usage only from trusted hosts, HTTP referrers and apps.' 29 | desc 'rationale', "Security risks involved in using API-Keys are below: 30 | 31 | - API keys are a simple encrypted strings 32 | - API keys do not identify the user or the application making the API request 33 | - API keys are typically accessible to clients, making it easy to discover and steal an API key 34 | 35 | Because of this Google recommend using the standard authentication flow instead. However, there are limited cases where API keys are more appropriate. For example, if there is a mobile application that needs to use the Google Cloud Translation API, but doesn't otherwise need a back-end server, API keys are the simplest way to authenticate to that API. 36 | 37 | In order to reduce attack vector, API-Keys can be restricted only to the trusted hosts, HTTP referrers and applications." 38 | 39 | tag cis_scored: false 40 | tag cis_level: 1 41 | tag cis_gcp: control_id.to_s 42 | tag cis_version: cis_version.to_s 43 | tag project: gcp_project_id.to_s 44 | tag nist: ['AC-2'] 45 | 46 | ref 'CIS Benchmark', url: cis_url.to_s 47 | ref 'GCP Docs', url: 'https://cloud.google.com/docs/authentication/api-keys' 48 | 49 | describe 'This control is not scored' do 50 | skip 'This control is not scored' 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /controls/1.14-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure API keys are restricted to only APIs that application needs access' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.14' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure API keys are restricted to only APIs that application needs access" 27 | 28 | desc 'API keys are insecure because they can be viewed publicly, such as from within a browser, or they can be accessed on a device where the key resides. It is recommended to restrict API keys to use (call) only APIs required by an application.' 29 | desc 'rationale', "Security risks involved in using API-Keys are below: 30 | 31 | - API keys are a simple encrypted strings 32 | - API keys do not identify the user or the application making the API request 33 | - API keys are typically accessible to clients, making it easy to discover and steal an API key 34 | 35 | Because of this Google recommend using the standard authentication flow instead. However, there are limited cases where API keys are more appropriate. For example, if there is a mobile application that needs to use the Google Cloud Translation API, but doesn't otherwise need a back-end server, API keys are the simplest way to authenticate to that API. 36 | 37 | In order to reduce attack surface by providing least privileges, API-Keys can be 38 | restricted to use (call) only APIs required by an application." 39 | 40 | tag cis_scored: false 41 | tag cis_level: 1 42 | tag cis_gcp: control_id.to_s 43 | tag cis_version: cis_version.to_s 44 | tag project: gcp_project_id.to_s 45 | tag nist: ['AC-2'] 46 | 47 | ref 'CIS Benchmark', url: cis_url.to_s 48 | ref 'GCP Docs', url: 'https://cloud.google.com/docs/authentication/api-keys' 49 | ref 'GCP Docs', url: 'https://cloud.google.com/apis/docs/overview' 50 | 51 | describe 'This control is not scored' do 52 | skip 'This control is not scored' 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /controls/1.15-iam.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure API keys are rotated every 90 days' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '1.15' 21 | control_abbrev = 'iam' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure API keys are rotated every 90 days" 27 | 28 | desc 'It is recommended to rotate API keys every 90 days.' 29 | desc 'rationale', "Security risks involved in using API-Keys are below: 30 | 31 | - API keys are a simple encrypted strings 32 | - API keys do not identify the user or the application making the API request 33 | - API keys are typically accessible to clients, making it easy to discover and steal an API key 34 | 35 | Because of this Google recommend using the standard authentication flow instead. However, there are limited cases where API keys are more appropriate. For example, if there is a mobile application that needs to use the Google Cloud Translation API, but doesn't otherwise need a backend server, API keys are the simplest way to authenticate to that API. 36 | 37 | Once the key is stolen, it has no expiration, so it may be used indefinitely, unless the project owner revokes or regenerates the key. Rotating API keys will reduce the window of opportunity for an access key that is associated with a compromised or terminated account to be used. API keys should be rotated to ensure that data cannot be accessed with an old key which might have been lost, cracked, or stolen." 38 | 39 | tag cis_scored: false 40 | tag cis_level: 1 41 | tag cis_gcp: control_id.to_s 42 | tag cis_version: cis_version.to_s 43 | tag project: gcp_project_id.to_s 44 | tag nist: ['AC-2'] 45 | 46 | ref 'CIS Benchmark', url: cis_url.to_s 47 | 48 | describe 'This control is not scored' do 49 | skip 'This control is not scored' 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /controls/2.01-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud Audit Logging is configured properly across all services and all users from a project ' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.1' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that Cloud Audit Logging is configured properly across all services and a 27 | ll users from a project " 28 | 29 | desc "It is recommended that Cloud Audit Logging is configured to track all Admin activities and 30 | read, write access to user data." 31 | desc 'rationale', "Cloud Audit Logging maintains two audit logs for each project and organization: Admin Activity 32 | nd Data Access. 33 | 34 | 1. Admin Activity logs contain log entries for API calls or other administrative actions that modify the configurati 35 | n or metadata of resources. Admin Activity audit logs are enabled for all services and cannot be configured. 36 | 2. Data Access audit logs record API calls that create, modify, or read user-provided data. These are disabled by de 37 | ault and should be enabled. There are three kinds of Data Access audit log information: 38 | - Admin read: Records operations that read metadata or configuration information. Admin Activity audit logs recor 39 | writes of metadata and configuration information which cannot be disabled. 40 | - Data read: Records operations that read user-provided data. 41 | - Data write: Records operations that write user-provided data. 42 | 43 | It is recommended to have effective default audit config configured in such a way that: 44 | 1. logtype is set to DATA_READ (to logs user activity tracking) and DATA_WRITES (to log changes/tampering to user data) 45 | 2. audit config is enabled for all the services supported by Data Access audit logs feature 46 | 3. Logs should be captured for all users. i.e. there are no exempted users in any of the audit config section. This will ensure overriding audit config will not contradict the requirement." 47 | 48 | tag cis_scored: true 49 | tag cis_level: 1 50 | tag cis_gcp: control_id.to_s 51 | tag cis_version: cis_version.to_s 52 | tag project: gcp_project_id.to_s 53 | tag nist: %w[AU-6 AU-12] 54 | 55 | ref 'CIS Benchmark', url: cis_url.to_s 56 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/audit/' 57 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/audit/configure-data-access' 58 | 59 | describe google_project_logging_audit_config(project: gcp_project_id) do 60 | its('default_types') { should include 'DATA_READ' } 61 | its('default_types') { should include 'DATA_WRITE' } 62 | it { should_not have_default_exempted_members } 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /controls/2.02-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that sinks are configured for all Log entries' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.2' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that sinks are configured for all Log entries" 27 | 28 | desc 'It is recommended to create sink which will export copies of all the log entries.' 29 | desc 'rationale', 'Log entries are held in Stackdriver Logging for a limited time known as the retention period. After that, the entries are deleted. To keep log entries longer, sink can export them outside of Stackdriver Logging. Exporting involves writing a filter that selects the log entries to export, and choosing a destination in Cloud Storage, BigQuery, or Cloud Pub/Sub. The filter and destination are held in an object called a sink. To ensure all log entries are exported using sink ensure that there is no filter configured for a sink. Sinks can be created in projects, organizations, folders, and billing accounts.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[AU-4 AU-12] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/quotas' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/export/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/export/using_exported_logs' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/export/configure_export_v2' 44 | 45 | empty_filter_sinks = [] 46 | google_logging_project_sinks(project: gcp_project_id).names.each do |sink_name| 47 | empty_filter_sinks.push(sink_name) if google_logging_project_sink(project: gcp_project_id, 48 | name: sink_name).filter.nil? 49 | end 50 | describe "[#{gcp_project_id}] Project level Log sink with an empty filter" do 51 | subject { empty_filter_sinks } 52 | it 'is expected to exist' do 53 | expect(empty_filter_sinks.count).to be_positive 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /controls/2.03-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that retention policies on log buckets are configured using Bucket Lock' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.3' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that retention policies on log buckets are configured using Bucket Lock" 27 | 28 | desc 'It is recommended to set up retention policies and configure Bucket Lock on all storage buckets that are used as log sinks.' 29 | desc 'rationale', "Logs can be exported by creating one or more sinks that include a log filter and a destination. As Stackdriver Logging receives new log entries, they are compared against each sink. If a log entry matches a sink's filter, then a copy of the log entry is written to the destination. 30 | 31 | Sinks can be configured to export logs in storage buckets. It is recommended to configure a data retention policy for these cloud storage buckets and to lock the data retention policy; thus permanently preventing the policy from being reduced or removed. This way, if the system is ever compromised by an attacker or a malicious insider who wants to cover their tracks, the activity logs are definitely preserved for forensics and security investigations." 32 | 33 | tag cis_scored: true 34 | tag cis_level: 1 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: ['AU-6'] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/bucket-lock' 42 | 43 | if google_logging_project_sinks(project: gcp_project_id).where(destination: /storage.googleapis.com/).destinations.empty? 44 | describe "[#{gcp_project_id}] does not have logging sinks configured." do 45 | subject { google_logging_project_sinks(project: gcp_project_id).where(destination: /storage.googleapis.com/).destinations } 46 | it { should_not be_empty } 47 | end 48 | else 49 | google_logging_project_sinks(project: gcp_project_id).where(destination: /storage.googleapis.com/).destinations.each do |sink| 50 | bucket = sink.split('/').last 51 | describe "[#{gcp_project_id}] Logging bucket #{bucket} retention policy Bucket Lock status" do 52 | subject { google_storage_bucket(name: bucket).retention_policy } 53 | its('is_locked') { should be true } 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /controls/2.04-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for Project Ownership assignments/changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.4' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for Project Ownership assignments/changes" 27 | 28 | desc "In order to prevent unnecessarily project ownership assignments to users/serviceaccounts and further misuses of project and resources, all roles/Owner assignments should be monitored. 29 | 30 | Members (users/Service-Accounts) with role assignment to primitive role roles/owner are Project Owners. 31 | 32 | Project Owner has all the privileges on a project it belongs to. These can be summarized as below: 33 | 34 | - All viewer permissions on All GCP Services part within the project 35 | - Permissions for actions that modify state of All GCP Services within the 36 | project 37 | - Manage roles and permissions for a project and all resources within the 38 | project 39 | - Set up billing for a project 40 | 41 | Granting owner role to a member (user/Service-Account) will allow members to modify the IAM policy. Therefore grant the owner role only if the member has a legitimate purpose to manage the IAM policy. This is because as project IAM policy contains sensitive access control data and having a minimal set of users manage it will simplify any auditing that you may have to do." 42 | desc 'rationale', "Project Ownership Having highest level of privileges on a project, to avoid misuse of project resources project ownership assignment/change actions mentioned should be monitored and alerted to concerned recipients. 43 | 44 | - Sending project ownership Invites 45 | - Acceptance/Rejection of project ownership invite by user 46 | - Adding `role\owner` to a user/service-account 47 | - Removing a user/Service account from `role\owner`" 48 | 49 | tag cis_scored: true 50 | tag cis_level: 1 51 | tag cis_gcp: control_id.to_s 52 | tag cis_version: cis_version.to_s 53 | tag project: gcp_project_id.to_s 54 | tag nist: ['AU-12'] 55 | 56 | ref 'CIS Benchmark', url: cis_url.to_s 57 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 58 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 59 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 60 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 61 | 62 | log_filter = 'resource.type=audited_resource AND (protoPayload.serviceName="cloudresourcemanager.googleapis.com") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action="REMOVE" AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action="ADD" AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner")' 63 | describe "[#{gcp_project_id}] Project Ownership changes filter" do 64 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 65 | it { should exist } 66 | end 67 | 68 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 69 | describe.one do 70 | filter = "metric.type=\"#{metrictype}\" resource.type=\"audited_resource\"" 71 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 72 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 73 | describe "[#{gcp_project_id}] Project Ownership changes alert policy" do 74 | subject { condition } 75 | it { should exist } 76 | end 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /controls/2.05-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for Audit Configuration Changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.5' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for Audit Configuration Changes" 27 | 28 | desc "Google Cloud Platform services write audit log entries to Admin Activity and Data Access logs to helps answer the questions of 'who did what, where, and when?' within Google Cloud Platform projects. Cloud Audit logging records information includes the identity of the API caller, the time of the API call, the source IP address of the API caller, the request parameters, and the response elements returned by the GCP services. Cloud Audit logging provides a history of AWS API calls for an account, including API calls made via the Console, SDKs, command line tools, and other GCP services." 29 | desc 'rationale', 'Admin activity and Data access logs produced by Cloud audit logging enables security analysis, resource change tracking, and compliance auditing. Configuring metric filter and alerts for Audit Configuration Changes ensures recommended state of audit configuration and hence, all the activities in project are audit-able at any point in time.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[AU-3 AU-12] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/audit/configure-data-access#getiampolicy-setiampolicy' 44 | 45 | log_filter = 'resource.type=audited_resource AND protoPayload.methodName="SetIamPolicy" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*' 46 | describe "[#{gcp_project_id}] Audit configuration changes filter" do 47 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 48 | it { should exist } 49 | end 50 | 51 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 52 | describe.one do 53 | filter = "metric.type=\"#{metrictype}\" resource.type=\"audited_resource\"" 54 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 55 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 56 | describe "[#{gcp_project_id}] Audit configuration changes alert policy" do 57 | subject { condition } 58 | it { should exist } 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /controls/2.06-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for Custom Role changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.6' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for Custom Role changes" 27 | 28 | desc 'It is recommended that a metric filter and alarm be established for changes IAM Role creation, deletion and updating activities.' 29 | desc 'rationale', 'Google Cloud Identity and Access Management (Cloud IAM) provides predefined roles that give granular access to specific Google Cloud Platform resources and prevent unwanted access to other resources. However to cater organization specific needs, Cloud IAM also provides ability to create custom roles. Project Owner and administrators with Organization Role Administrator role or the IAM Role Administrator role can create custom roles. Monitoring role creation, deletion and updating activities will help in identifying over-privileged role at early stages.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[AU-3 AU-12] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/iam/docs/understanding-custom-roles' 44 | 45 | log_filter = 'resource.type=audited_resource AND protoPayload.methodName="google.iam.admin.v1.CreateRole" OR protoPayload.methodName="google.iam.admin.v1.DeleteRole" OR protoPayload.methodName="google.iam.admin.v1.UpdateRole"' 46 | describe "[#{gcp_project_id}] Custom Role changes filter" do 47 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 48 | it { should exist } 49 | end 50 | 51 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 52 | describe.one do 53 | filter = "metric.type=\"#{metrictype}\" resource.type=\"audited_resource\"" 54 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 55 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 56 | describe "[#{gcp_project_id}] Custom Role changes alert policy" do 57 | subject { condition } 58 | it { should exist } 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /controls/2.07-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for VPC Network Firewall rule changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.7' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for VPC Network Firewall rule changes" 27 | 28 | desc 'It is recommended that a metric filter and alarm be established for VPC Network Firewall rule changes.' 29 | desc 'rationale', 'Monitoring for Create or Update firewall rule events gives insight network access changes and may reduce the time it takes to detect suspicious activity.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[AU-3 AU-12] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/vpc/docs/firewalls' 44 | 45 | log_filter = 'resource.type=global AND jsonPayload.event_subtype="compute.firewalls.patch" OR jsonPayload.event_subtype="compute.firewalls.insert"' 46 | describe "[#{gcp_project_id}] VPC FW Rule changes filter" do 47 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 48 | it { should exist } 49 | end 50 | 51 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 52 | describe.one do 53 | filter = "metric.type=\"#{metrictype}\" resource.type=\"global\"" 54 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 55 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 56 | describe "[#{gcp_project_id}] VPC FW Rule changes alert policy" do 57 | subject { condition } 58 | it { should exist } 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /controls/2.08-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for VPC network route changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.8' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for VPC network route changes " 27 | 28 | desc 'It is recommended that a metric filter and alarm be established for VPC network route changes.' 29 | desc 'rationale', "Google Cloud Platform (GCP) routes define the paths network traffic takes from a VM instance to another destinations. The other destination can be inside your VPC network (such as another VM) or outside of it. Every route consists of a destination and a next hop. Traffic whose destination IP is within the destination range is sent to the next hop for delivery. 30 | 31 | Monitoring changes to route tables will help ensure that all VPC traffic flows through an expected path." 32 | 33 | tag cis_scored: true 34 | tag cis_level: 1 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: %w[AU-3 AU-12] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 44 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 45 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/access-control/iam' 46 | 47 | log_filter = 'resource.type=global AND jsonPayload.event_subtype="compute.routes.delete" OR jsonPayload.event_subtype="compute.routes.insert"' 48 | describe "[#{gcp_project_id}] VPC Route changes filter" do 49 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 50 | it { should exist } 51 | end 52 | 53 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 54 | describe.one do 55 | filter = "metric.type=\"#{metrictype}\" resource.type=\"global\"" 56 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 57 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 58 | describe "[#{gcp_project_id}] VPC Route changes alert policy" do 59 | subject { condition } 60 | it { should exist } 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /controls/2.09-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for VPC network changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.9' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for VPC network changes " 27 | 28 | desc 'It is recommended that a metric filter and alarm be established for VPC network changes.' 29 | desc 'rationale', "It is possible to have more than 1 VPC within an project, in addition it is also possible to create a peer connection between 2 VPCs enabling network traffic to route between VPCs. 30 | 31 | Monitoring changes to VPC will help ensure VPC traffic flow is not getting impacted." 32 | 33 | tag cis_scored: true 34 | tag cis_level: 1 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: %w[AU-3 AU-12] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 44 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 45 | ref 'GCP Docs', url: 'https://cloud.google.com/vpc/docs/overview' 46 | 47 | log_filter = 'resource.type=audited_resource AND jsonPayload.event_subtype="compute.networks.insert" OR jsonPayload.event_subtype="compute.networks.patch" OR jsonPayload.event_subtype="compute.networks.delete" OR jsonPayload.event_subtype="compute.networks.removePeering" OR jsonPayload.event_subtype="compute.networks.addPeering"' 48 | describe "[#{gcp_project_id}] VPC Network changes filter" do 49 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 50 | it { should exist } 51 | end 52 | 53 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 54 | describe.one do 55 | filter = "metric.type=\"#{metrictype}\" resource.type=\"audited_resource\"" 56 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 57 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 58 | describe "[#{gcp_project_id}] VPC Network changes alert policy" do 59 | subject { condition } 60 | it { should exist } 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /controls/2.10-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure log metric filter and alerts exists for Cloud Storage IAM permission changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.10' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for Cloud Storage IAM permission changes" 27 | 28 | desc 'It is recommended that a metric filter and alarm be established for Cloud Storage Bucket IAM changes.' 29 | desc 'rationale', 'Monitoring changes to Cloud Storage bucket permissions may reduce time to detect and correct permissions on sensitive Cloud Storage bucket and objects inside the bucket.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[AU-3 AU-12] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 42 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/overview' 44 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/access-control/iam-roles' 45 | 46 | log_filter = 'resource.type=gcs_bucket AND protoPayload.methodName="storage.setIamPermissions"' 47 | describe "[#{gcp_project_id}] Cloud Storage changes filter" do 48 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 49 | it { should exist } 50 | end 51 | 52 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 53 | describe.one do 54 | filter = "metric.type=\"#{metrictype}\" resource.type=\"gcs_bucket\"" 55 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 56 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 57 | describe "[#{gcp_project_id}] Cloud Storage changes alert policy" do 58 | subject { condition } 59 | it { should exist } 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /controls/2.11-logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title ' Ensure log metric filter and alerts exists for SQL instance configuration changes' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '2.11' 21 | control_abbrev = 'logging' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure log metric filter and alerts exists for SQL instance configuration changes" 27 | 28 | desc 'It is recommended that a metric filter and alarm be established for SQL Instance configuration changes.' 29 | desc 'rationale', "Monitoring changes to Sql Instance configuration changes may reduce time to detect and correct misconfigurations done on sql server. 30 | 31 | Below are the few of configurable Options which may impact security posture of a SQL Instance: 32 | 33 | - Enable auto backups and high availability: Misconfiguration may adversely impact Business continuity, Disaster Recovery and High Availability 34 | - Authorize networks : Misconfiguration may increase exposure to the untrusted networks" 35 | 36 | tag cis_scored: true 37 | tag cis_level: 1 38 | tag cis_gcp: control_id.to_s 39 | tag cis_version: cis_version.to_s 40 | tag project: gcp_project_id.to_s 41 | tag nist: %w[AU-3 AU-12] 42 | 43 | ref 'CIS Benchmark', url: cis_url.to_s 44 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/logs-based-metrics/' 45 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/custom-metrics/' 46 | ref 'GCP Docs', url: 'https://cloud.google.com/monitoring/alerts/' 47 | ref 'GCP Docs', url: 'https://cloud.google.com/logging/docs/reference/tools/gcloud-logging' 48 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/overview' 49 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/' 50 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/' 51 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/postgres/' 52 | 53 | log_filter = 'resource.type=audited_resource AND protoPayload.methodName="cloudsql.instances.update"' 54 | describe "[#{gcp_project_id}] Cloud SQL changes filter" do 55 | subject { google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter) } 56 | it { should exist } 57 | end 58 | 59 | google_project_metrics(project: gcp_project_id).where(metric_filter: log_filter).metric_types.each do |metrictype| 60 | describe.one do 61 | filter = "metric.type=\"#{metrictype}\" resource.type=\"audited_resource\"" 62 | google_project_alert_policies(project: gcp_project_id).where(policy_enabled_state: true).policy_names.each do |policy| 63 | condition = google_project_alert_policy_condition(policy: policy, filter: filter) 64 | describe "[#{gcp_project_id}] Cloud SQL changes alert policy" do 65 | subject { condition } 66 | it { should exist } 67 | end 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /controls/3.01-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure the default network does not exist in a project' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.1' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure the default network does not exist in a project" 27 | 28 | desc 'To prevent use of default network, a project should not have a default network.' 29 | desc 'rationale', 'The default network has automatically created firewall rules and has pre-fabricated network configuration. Based on your security and networking requirements, you should create your network and delete the default network.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 2 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['CM-6'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/networking#firewall_rules' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/reference/latest/networks/insert' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/reference/latest/networks/delete' 42 | 43 | describe "[#{gcp_project_id}] Subnets" do 44 | subject { google_compute_networks(project: gcp_project_id) } 45 | its('network_names') { should_not include 'default' } 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /controls/3.02-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure legacy networks do not exists for a project' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.2' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure legacy networks does not exists for a project" 27 | 28 | desc 'In order to prevent use of legacy networks, a project should not have a legacy network configured.' 29 | desc 'rationale', 'Legacy networks have a single network IPv4 prefix range and a single gateway IP address for the whole network. The network is global in scope and spans all cloud regions. You cannot create subnetworks in a legacy network or switch from legacy to auto or custom subnet networks. Legacy networks can thus have an impact for high network traffic projects and subject to the single point of contention or failure.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['CM-6'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/networking#creating_a_legacy_network' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/networking#legacy_non-subnet_network' 41 | 42 | network_names = google_compute_networks(project: gcp_project_id).network_names 43 | 44 | if network_names.empty? 45 | describe "[#{gcp_project_id}] does not have any networks. This test is Not Applicable." do 46 | skip "[#{gcp_project_id}] does not have any networks." 47 | end 48 | else 49 | google_compute_networks(project: gcp_project_id).network_names.each do |network| 50 | describe "[#{gcp_project_id}] Network [#{network}] " do 51 | subject { google_compute_network(project: gcp_project_id, name: network) } 52 | it { should_not be_legacy } 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /controls/3.03-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that DNSSEC is enabled for Cloud DNS' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.3' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that DNSSEC is enabled for Cloud DNS" 27 | 28 | desc 'Cloud DNS is a fast, reliable and cost-effective Domain Name System that powers millions of domains on the internet. DNSSEC in Cloud DNS enables domain owners to take easy steps to protect their domains against DNS hijacking and man-in-the-middle and other attacks.' 29 | desc 'rationale', 'Domain Name System Security Extensions (DNSSEC) adds security to the Domain Name System (DNS) protocol by enabling DNS responses to be validated. Having a trustworthy Domain Name System (DNS) that translates a domain name like www.example.com into its associated IP address is an increasingly important building block of today’s web-based applications. Attackers can hijack this process of domain/IP lookup and redirect users to a malicious site through DNS hijacking and man-in-the-middle attacks. DNSSEC helps mitigate the risk of such attacks by cryptographically signing DNS records. As a result, it prevents attackers from issuing fake DNS responses that may misdirect browsers to nefarious websites.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['CM-6'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloudplatform.googleblog.com/2017/11/DNSSEC-now-available-in-Cloud-DNS.html' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/dns/dnssec-config#enabling' 41 | ref 'GCP Docs', url: 'https://cloud.google.com/dns/dnssec' 42 | 43 | managed_zone_names = google_dns_managed_zones(project: gcp_project_id).where(visibility: 'public').zone_names 44 | if managed_zone_names.empty? 45 | impact 'none' 46 | describe "[#{gcp_project_id}] does not have DNS Zones with Public visibility. This test is Not Applicable." do 47 | skip "[#{gcp_project_id}] does not have DNS Zones with Public visibility." 48 | end 49 | else 50 | managed_zone_names.each do |dnszone| 51 | describe "[#{gcp_project_id}] DNS Zone [#{dnszone}] with DNSSEC" do 52 | subject { google_dns_managed_zone(project: gcp_project_id, zone: dnszone) } 53 | its('dnssec_config.state') { should cmp 'on' } 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /controls/3.04-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that RSASHA1 is not used for key-signing key in Cloud DNS DNSSEC' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.4' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'none' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that RSASHA1 is not used for key-signing key in Cloud DNS DNSSEC" 27 | 28 | desc 'DNSSEC algorithm numbers in this registry may be used in CERT RRs. Zone signing (DNSSEC) and transaction security mechanisms (SIG(0) and TSIG) make use of particular subsets of these algorithms. The algorithm used for key signing should be recommended one and it should not be weak.' 29 | desc 'rationale', 'DNSSEC algorithm numbers in this registry may be used in CERT RRs. Zonesigning (DNSSEC) and transaction security mechanisms (SIG(0) and TSIG) make use of particular subsets of these algorithms. The algorithm used for key signing should be recommended one and it should not be weak. 30 | 31 | When enabling DNSSEC for a managed zone, or creating a managed zone with DNSSEC, you can select the DNSSEC signing algorithms and the denial-of-existence type. Changing the DNSSEC settings is only effective for a managed zone if DNSSEC is not already enabled. If you need to change the settings for a managed zone where it has been enabled, you can turn DNSSEC off and then re-enable it with different settings.' 32 | 33 | tag cis_scored: false 34 | tag cis_level: 1 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: ['CM-6'] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/dns/dnssec-advanced#advanced_signing_options' 42 | 43 | managed_zone_names = google_dns_managed_zones(project: gcp_project_id).zone_names 44 | 45 | if managed_zone_names.empty? 46 | describe "[#{gcp_project_id}] does not have DNS Zones. This test is Not Applicable." do 47 | skip "[#{gcp_project_id}] does not have DNS Zones." 48 | end 49 | else 50 | managed_zone_names.each do |dnszone| 51 | zone = google_dns_managed_zone(project: gcp_project_id, zone: dnszone) 52 | if zone.visibility == 'private' 53 | describe "[#{gcp_project_id}] DNS zone #{dnszone} has private visibility. This test is not applicable for private zones." do 54 | skip "[#{gcp_project_id}] DNS zone #{dnszone} has private visibility." 55 | end 56 | elsif zone.dnssec_config.state == 'on' 57 | zone.dnssec_config.default_key_specs.select { |spec| spec.key_type == 'keySigning' }.each do |spec| 58 | impact 'medium' 59 | describe "[#{gcp_project_id}] DNS Zone [#{dnszone}] with DNSSEC key-signing" do 60 | subject { spec } 61 | its('algorithm') { should_not cmp 'RSASHA1' } 62 | its('algorithm') { should_not cmp nil } 63 | end 64 | end 65 | else 66 | impact 'medium' 67 | describe "[#{gcp_project_id}] DNS Zone [#{dnszone}] DNSSEC" do 68 | subject { 'off' } 69 | it { should cmp 'on' } 70 | end 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /controls/3.05-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that RSASHA1 is not used for zone-signing key in Cloud DNS DNSSEC' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.5' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'none' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that RSASHA1 is not used for zone-signing key in Cloud DNS DNSSEC" 27 | 28 | desc 'DNSSEC algorithm numbers in this registry may be used in CERT RRs. Zone signing (DNSSEC) and transaction security mechanisms (SIG(0) and TSIG) make use of particular subsets of these algorithms. The algorithm used for key signing should be recommended one and it should not be weak.' 29 | desc 'rationale', "DNSSEC algorithm numbers in this registry may be used in CERT RRs. Zonesigning (DNSSEC) and transaction security mechanisms (SIG(0) and TSIG) make use of particular subsets of these algorithms. 30 | 31 | The algorithm used for key signing should be recommended one and it should not be weak. When enabling DNSSEC for a managed zone, or creating a managed zone with DNSSEC, you can select the DNSSEC signing algorithms and the denial-of-existence type. Changing the DNSSEC settings is only effective for a managed zone if DNSSEC is not already enabled. If you need to change the settings for a managed zone where it has been enabled, you can turn DNSSEC off and then re-enable it with different settings." 32 | 33 | tag cis_scored: false 34 | tag cis_level: 1 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: ['CM-6'] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/dns/dnssec-advanced#advanced_signing_options' 42 | 43 | managed_zone_names = google_dns_managed_zones(project: gcp_project_id).zone_names 44 | 45 | if managed_zone_names.empty? 46 | describe "[#{gcp_project_id}] does not have DNS Zones. This test is Not Applicable." do 47 | skip "[#{gcp_project_id}] does not have DNS Zones." 48 | end 49 | else 50 | managed_zone_names.each do |dnszone| 51 | zone = google_dns_managed_zone(project: gcp_project_id, zone: dnszone) 52 | if zone.visibility == 'private' 53 | describe "[#{gcp_project_id}] DNS zone #{dnszone} has private visibility. This test is not applicable for private zones." do 54 | skip "[#{gcp_project_id}] DNS zone #{dnszone} has private visibility." 55 | end 56 | elsif zone.dnssec_config.state == 'on' 57 | zone.dnssec_config.default_key_specs.select { |spec| spec.key_type == 'zoneSigning' }.each do |spec| 58 | impact 'medium' 59 | describe "[#{gcp_project_id}] DNS Zone [#{dnszone}] with DNSSEC zone-signing" do 60 | subject { spec } 61 | its('algorithm') { should_not cmp 'RSASHA1' } 62 | its('algorithm') { should_not cmp nil } 63 | end 64 | end 65 | else 66 | impact 'medium' 67 | describe "[#{gcp_project_id}] DNS Zone [#{dnszone}] DNSSEC" do 68 | subject { 'off' } 69 | it { should cmp 'on' } 70 | end 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /controls/3.06-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that SSH access is restricted from the internet' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.6' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'high' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that SSH access is restricted from the internet" 27 | 28 | desc 'GCP Firewall Rules are specific to a VPC Network. Each rule either allows or denies traffic when its conditions are met. Its conditions allow you to specify the type of traffic, such as ports and protocols, and the source or destination of the traffic, including IP addresses, subnets, and instances. Firewall rules are defined at the VPC network level, and are specific to the network in which they are defined. The rules themselves cannot be shared among networks. Firewall rules only support IPv4 traffic. When specifying a source for an ingress rule or a destination for an egress rule by address, you can only use an IPv4 address or IPv4 block in CIDR notation. Generic (0.0.0.0/0) incoming traffic from internet to VPC or VM instance using SSH on Port 22 can be avoided.' 29 | desc 'rationale', 'GCP Firewall Rules within a VPC Network. These rules apply to outgoing (egress) traffic from instances and incoming (ingress) traffic to instances in the network. Egress and ingress traffic are controlled even if the traffic stays within the network (for example, instance-to-instance communication). For an instance to have outgoing Internet access, the network must have a valid Internet gateway route or custom route whose destination IP is specified. This route simply defines the path to the Internet, to avoid the most general (0.0.0.0/0) destination IP Range specified from Internet through SSH with default Port 22. We need to restrict generic access from Internet to specific IP Range.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 2 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[CM-7 CA-3 SC-7] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/vpc/docs/firewalls#blockedtraffic' 40 | 41 | google_compute_firewalls(project: gcp_project_id).where(firewall_direction: 'INGRESS').firewall_names.each do |firewall_name| 42 | describe "[#{gcp_project_id}] #{firewall_name}" do 43 | subject { google_compute_firewall(project: gcp_project_id, name: firewall_name) } 44 | it 'should not allow SSH from 0.0.0.0/0' do 45 | expect(subject.allowed_ssh? && (subject.allow_ip_ranges? ['0.0.0.0/0'])).to be false 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /controls/3.07-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that RDP access is restricted from the internet' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.7' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'high' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that RDP access is restricted from the internet" 27 | 28 | desc 'GCP Firewall Rules are specific to a VPC Network. Each rule either allows or denies traffic when its conditions are met. Its conditions allow you to specify the type of traffic, such as ports and protocols, and the source or destination of the traffic, including IP addresses, subnets, and instances. Firewall rules are defined at the VPC network level, and are specific to the network in which they are defined. The rules themselves cannot be shared among networks. Firewall rules only support IPv4 traffic. When specifying a source for an ingress rule or a destination for an egress rule by address, you can only use an IPv4 address or IPv4 block in CIDR notation. Generic (0.0.0.0/0) incoming traffic from internet to VPC or VM instance using RDP on Port 3389 can be avoided.' 29 | desc 'rationale', 'GCP Firewall Rules within a VPC Network. These rules apply to outgoing (egress) traffic from instances and incoming (ingress) traffic to instances in the network. Egress and ingress traffic are controlled even if the traffic stays within the network (for example, instance-to-instance communication). For an instance to have outgoing Internet access, the network must have a valid Internet gateway route or custom route whose destination IP is specified. This route simply defines the path to the Internet, to avoid the most general (0.0.0.0/0) destination IP Range specified from Internet through RDP with default Port 3389. We need to restrict generic access from Internet to specific IP Range.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 2 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[CM-7 CA-3 SC-7] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/vpc/docs/firewalls#blockedtraffic' 40 | 41 | google_compute_firewalls(project: gcp_project_id).where(firewall_direction: 'INGRESS').firewall_names.each do |firewall_name| 42 | describe "[#{gcp_project_id}] #{firewall_name}" do 43 | subject { google_compute_firewall(project: gcp_project_id, name: firewall_name) } 44 | it 'Should not allow RDP from 0.0.0.0/0' do 45 | expect(subject.allowed_rdp? && (subject.allow_ip_ranges? ['0.0.0.0/0'])).to be false 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /controls/3.08-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure VPC Flow logs is enabled for every subnet in VPC Network' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '3.8' 21 | control_abbrev = 'networking' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'low' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure VPC Flow logs is enabled for every subnet in VPC Network" 27 | 28 | desc "Flow Logs is a feature that enables you to capture information about the IP traffic going to and from network interfaces in your VPC Subnets. After you've created a flow log, you can view and retrieve its data in Stackdriver Logging. It is recommended that Flow Logs be enabled for every business critical VPC subnet." 29 | desc 'rationale', "VPC networks and subnetworks provide logically isolated and secure network partitions where you can launch GCP resources. When Flow Logs is enabled for a subnet, VMs within subnet starts reporting on all TCP and UDP flows. Each VM samples the TCP and UDP flows it sees, inbound and outbound, whether the flow is to or from another VM, a host in your on-premises datacenter, a Google service, or a host on the Internet. If two GCP VMs are communicating, and both are in subnets that have VPC Flow Logs enabled, both VMs report the flows. 30 | 31 | Flow Logs supports following use cases: 32 | 33 | - Network monitoring 34 | - Understanding network usage and optimizing network traffic expenses 35 | - Network forensics 36 | - Real-time security analysis 37 | 38 | Flow Logs provide visibility into network traffic for each VM inside the subnet and can be used to detect anomalous traffic or insight during security workflows." 39 | 40 | tag cis_scored: true 41 | tag cis_level: 1 42 | tag cis_gcp: control_id.to_s 43 | tag cis_version: cis_version.to_s 44 | tag project: gcp_project_id.to_s 45 | tag nist: %w[AU-12 SI-4] 46 | 47 | ref 'CIS Benchmark', url: cis_url.to_s 48 | ref 'GCP Docs', url: 'https://cloud.google.com/vpc/docs/using-flow-logs#enabling_vpc_flow_logging' 49 | ref 'GCP Docs', url: 'https://cloud.google.com/vpc/' 50 | 51 | google_compute_regions(project: gcp_project_id).region_names.each do |region| 52 | google_compute_subnetworks(project: gcp_project_id, region: region).subnetwork_names.each do |subnet| 53 | subnet_obj = google_compute_subnetwork(project: gcp_project_id, region: region, name: subnet) 54 | if subnet_obj.purpose == 'INTERNAL_HTTPS_LOAD_BALANCER' # filter subnets for internal HTTPs Load Balancing 55 | describe "[#{gcp_project_id} #{region}/#{subnet}] does not support VPC Flow Logs. This test is Not Applicable." do 56 | skip "[#{gcp_project_id} #{region}/#{subnet}] does not support VPC Flow Logs." 57 | end 58 | else 59 | describe "[#{gcp_project_id}] #{region}/#{subnet}" do 60 | subject { subnet_obj } 61 | if subnet_obj.methods.include?(:log_config) == true 62 | it 'should have logging enabled' do 63 | expect(subnet_obj.log_config.enable).to be true 64 | end 65 | end 66 | end 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /controls/3.09-networking.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure no HTTPS or SSL proxy load balancers permit SSL policies with 16 | weak cipher suites' 17 | 18 | gcp_project_id = input('gcp_project_id') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '3.9' 22 | control_abbrev = 'networking' 23 | 24 | gcp_https_proxies = google_compute_target_https_proxies(project: gcp_project_id).names 25 | gcp_ssl_policies = google_compute_ssl_policies(project: gcp_project_id).names 26 | 27 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 28 | impact 'low' 29 | 30 | title "[#{control_abbrev.upcase}] Ensure no HTTPS or SSL proxy load balancers permit SSL policies with weak cipher suites" 31 | 32 | desc 'Secure Sockets Layer (SSL) policies determine what port Transport Layer Security (TLS) features clients are permitted to use when connecting to load balancers. To prevent usage of insecure features, SSL policies should use (a) at least TLS 1.2 with the MODERN profile; or (b) the RESTRICTED profile, because it effectively requires clients to use TLS 1.2 regardless of the chosen minimum TLS version; or (3) a CUSTOM profile that does not support any of the following features: TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA' 33 | desc 'rationale', 'Load balancers are used to efficiently distribute traffic across multiple servers. Both SSL proxy and HTTPS load balancers are external load balancers, meaning they distribute traffic from the Internet to a GCP network. GCP customers can configure load balancer SSL policies with a minimum TLS version (1.0, 1.1, or 1.2) that clients can use to establish a connection, along with a profile (Compatible, Modern, Restricted, or Custom) that specifies permissible cipher suites. To comply with users using outdated protocols, GCP load balancers can be configured to permit insecure cipher suites. In fact, the GCP default SSL policy uses a minimum TLS versionls of 1.0 and a Compatible profile, which allows the widest range of insecure cipher suites. As a result, it is easy for customers to configure a load balancer without even knowing that they are permitting outdated cipher suites.' 34 | 35 | tag cis_scored: false 36 | tag cis_level: 1 37 | tag cis_gcp: control_id.to_s 38 | tag cis_version: cis_version.to_s 39 | tag project: gcp_project_id.to_s 40 | tag nist: ['SC-1'] 41 | 42 | ref 'CIS Benchmark', url: cis_url.to_s 43 | ref 'GCP Docs', url: 'https://cloud.google.com/load-balancing/docs/use-ssl-policies' 44 | ref 'GCP Docs', url: 'https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r.pdf' 45 | 46 | # All load balancers have custom/strong TLS profiles set 47 | 48 | gcp_https_proxies.each do |proxy| 49 | describe "[#{gcp_project_id}] HTTPS Proxy: #{proxy}" do 50 | subject { google_compute_target_https_proxy(project: gcp_project_id, name: proxy) } 51 | it 'should have a custom SSL policy configured' do 52 | expect(subject.ssl_policy).to_not cmp(nil) 53 | end 54 | end 55 | end 56 | # Ensure SSL Policies use strong TLS 57 | gcp_ssl_policies.each do |policy| 58 | case google_compute_ssl_policy(project: gcp_project_id, name: policy).profile 59 | when 'MODERN' 60 | describe "[#{gcp_project_id}] SSL Policy: #{policy}" do 61 | subject { google_compute_ssl_policy(project: gcp_project_id, name: policy) } 62 | it 'should minimally require TLS 1.2' do 63 | expect(subject.min_tls_version).to cmp('TLS_1_2') 64 | end 65 | end 66 | 67 | when 'RESTRICTED' 68 | describe "[#{gcp_project_id}] SSL Policy: #{policy} profile should be RESTRICTED" do 69 | subject { google_compute_ssl_policy(project: gcp_project_id, name: policy).profile } 70 | it { should cmp 'RESTRICTED' } 71 | end 72 | 73 | when 'CUSTOM' 74 | describe "[#{gcp_project_id}] SSL Policy: #{policy} profile CUSTOM should not contain these cipher suites [TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA] " do 75 | subject { google_compute_ssl_policy(project: gcp_project_id, name: policy) } 76 | its('custom_features') { should_not be_in %w[TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_256_GCM_SHA384 TLS_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_256_CBC_SHA TLS_RSA_WITH_3DES_EDE_CBC_SHA] } 77 | end 78 | end 79 | end 80 | 81 | if gcp_https_proxies.empty? && gcp_ssl_policies.empty? 82 | impact 'none' 83 | describe "[#{gcp_project_id}] No HTTPS or SSL proxy load balancers found. This test is Not Applicable." do 84 | skip "[#{gcp_project_id}] No HTTPS or SSL proxy load balancers found" 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /controls/4.01-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title ' Ensure that instances are not configured to use the default service account ' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.1' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure that instances are not configured to use the default compute engine service account with full access to all Cloud APIs" 30 | 31 | desc 'To support principle of least privileges and prevent potential privilege escalation it is recommended that instances are not assigned to default service account Compute Engine default service account' 32 | desc 'rationale', 'The default Compute Engine service account has the Editor role on the project, which allows read and write access to most Google Cloud Services. To defend against privilege escalations if your VM is compromised and prevent an attacker from gaining access to all of your project, it is recommended to not use the default Compute Engine service account. Instead, you should create a new service account and assigning only the permissions needed by your instance.' 33 | 34 | tag cis_scored: true 35 | tag cis_level: 1 36 | tag cis_gcp: control_id.to_s 37 | tag cis_version: cis_version.to_s 38 | tag project: gcp_project_id.to_s 39 | tag nist: %w[AC-2 AC-6] 40 | 41 | ref 'CIS Benchmark', url: cis_url.to_s 42 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances' 43 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/access/service-accounts' 44 | 45 | project_number = google_project(project: gcp_project_id).project_number 46 | gce_instances.each do |instance| 47 | next if instance[:name] =~ /^gke-/ 48 | google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]).service_accounts.each do |serviceaccount| 49 | describe "VM #{instance[:name]} should not include the default compute engine service account" do 50 | subject { serviceaccount.email } 51 | it { should_not include("#{project_number}-compute@developer.gserviceaccount.com") } 52 | end 53 | end 54 | end 55 | 56 | if gce_instances.empty? 57 | impact 'none' 58 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 59 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /controls/4.02-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that instances are not configured to use the default service account with full access to all Cloud APIs' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.2' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure that instances are not configured to use the default service account with full access to all Cloud APIs" 30 | 31 | desc 'To support principle of least privileges and prevent potential privilege escalation it is recommended that instances are not assigned to default service account Compute Engine default service account with Scope Allow full access to all Cloud APIs.' 32 | desc 'rationale', "Along with ability to optionally create, manage and use user managed custom service accounts, Google Compute Engine provides default service account Compute Engine default service account for an instances to access necessary cloud services. Project Editor role is assigned to Compute Engine default service account hence, This service account has almost all capabilities over all cloud services except billing. However, when Compute Engine default service account assigned to an instance it can operate in 3 scopes. 33 | 34 | 1. Allow default access: Allows only minimum access required to run an Instance (Least Privileges) 35 | 2. Allow full access to all Cloud APIs: Allow full access to all the cloud APIs/Services (Too much access) 36 | 3. Set access for each API: Allows Instance administrator to choose only those APIs that are needed to perform specific business functionality expected by instance 37 | 38 | When an instance is configured with Compute Engine default service account with Scope Allow full access to all Cloud APIs, based on IAM roles assigned to the user(s) accessing Instance, it may allow user to perform cloud operations/API calls that user is not supposed to perform leading to successful privilege escalation." 39 | 40 | tag cis_scored: true 41 | tag cis_level: 1 42 | tag cis_gcp: control_id.to_s 43 | tag cis_version: cis_version.to_s 44 | tag project: gcp_project_id.to_s 45 | tag nist: %w[AC-2 AC-6] 46 | 47 | ref 'CIS Benchmark', url: cis_url.to_s 48 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances' 49 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/access/service-accounts' 50 | 51 | project_number = google_project(project: gcp_project_id).project_number 52 | gce_instances.each do |instance| 53 | next if instance[:name] =~ /^gke-/ 54 | google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]).service_accounts.each do |serviceaccount| 55 | next if serviceaccount.email != "#{project_number}-compute@developer.gserviceaccount.com" 56 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]}" do 57 | subject { serviceaccount.scopes } 58 | it { should_not include 'https://www.googleapis.com/auth/cloud-platform' } 59 | end 60 | end 61 | end 62 | 63 | if gce_instances.empty? 64 | impact 'none' 65 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 66 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /controls/4.03-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title "Ensure 'Block Project-wide SSH keys' is enabled for VM instances" 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.3' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure 'Block Project-wide SSH keys' enabled for VM instances" 30 | 31 | desc 'It is recommended to user Instance specific SSH key(s) instead of using common/shared project-wide SSH key(s) to access Instances.' 32 | desc 'rationale', 'Project-wide SSH keys are stored in Compute/Project-meta-data. Project wide SSH keys can be used to login into all the instances within project. Using project-wide SSH keys eases the SSH key management but if compromised, poses the security risk which can impact all the instances within project. It is recommended to use Instance specific SSH keys which can limit the attack surface if the SSH keys are compromised.' 33 | 34 | tag cis_scored: true 35 | tag cis_level: 1 36 | tag cis_gcp: control_id.to_s 37 | tag cis_version: cis_version.to_s 38 | tag project: gcp_project_id.to_s 39 | tag nist: ['AC-2'] 40 | 41 | ref 'CIS Benchmark', url: cis_url.to_s 42 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys' 43 | 44 | gce_instances.each do |instance| 45 | next if instance[:name] =~ /^gke-/ 46 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]}" do 47 | subject { google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]) } 48 | its('block_project_ssh_keys') { should be true } 49 | end 50 | end 51 | 52 | if gce_instances.empty? 53 | impact 'none' 54 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 55 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /controls/4.04-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure oslogin is enabled for a Project' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '4.4' 21 | control_abbrev = 'vms' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure oslogin is enabled for a Project" 27 | 28 | desc 'Enabling OS login binds SSH certificates to IAM users and facilitates effective SSH certificate management.' 29 | desc 'rationale', 'Enabling osLogin ensures that SSH keys used to connect to instances are mapped with IAM users. Revoking access to IAM user will revoke all the SSH keys associated with that particular user. It facilitates centralized and automated SSH key pair management which is useful in handling cases like response to compromised SSH key pairs and/or revocation of external/third-party/Vendor users.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['AC-2'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/instances/managing-instance-access' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/instances/managing-instance-access#enable_oslogin' 41 | 42 | describe "[#{gcp_project_id}]" do 43 | subject { google_compute_project_info(project: gcp_project_id) } 44 | it { should have_enabled_oslogin } 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /controls/4.05-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title "Ensure 'Enable connecting to serial ports' is not enabled for VM Instance" 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.5' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure 'Enable connecting to serial ports' is not enabled for VM Instance" 30 | 31 | desc "Interacting with a serial port is often referred to as the serial console, which is similar to using a terminal window, in that input and output is entirely in text mode and there is no graphical interface or mouse support. 32 | 33 | If you enable the interactive serial console on an instance, clients can attempt to connect to that instance from any IP address. Therefore interactive serial console support should be disabled." 34 | desc 'rationale', "A virtual machine instance has four virtual serial ports. Interacting with a serial port is similar to using a terminal window, in that input and output is entirely in text mode and there is no graphical interface or mouse support. The instance's operating system, BIOS, and other system-level entities often write output to the serial ports, and can accept input such as commands or answers to prompts. Typically, these system-level entities use the first serial port (port 1) and serial port 1 is often referred to as the serial console. 35 | 36 | The interactive serial console does not support IP-based access restrictions such as IP whitelists. If you enable the interactive serial console on an instance, clients can attempt to connect to that instance from any IP address. This allows anybody to connect to that instance if they know the correct SSH key, username, project ID, zone, and instance name. 37 | 38 | Therefore interactive serial console support should be disabled." 39 | 40 | tag cis_scored: true 41 | tag cis_level: 1 42 | tag cis_gcp: control_id.to_s 43 | tag cis_version: cis_version.to_s 44 | tag project: gcp_project_id.to_s 45 | tag nist: ['CM-7'] 46 | 47 | ref 'CIS Benchmark', url: cis_url.to_s 48 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/instances/interacting-with-serial-console' 49 | 50 | gce_instances.each do |instance| 51 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]}" do 52 | subject { google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]) } 53 | it { should have_serial_port_disabled } 54 | end 55 | end 56 | 57 | if gce_instances.empty? 58 | impact 'none' 59 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 60 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /controls/4.06-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that IP forwarding is not enabled on Instances' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.6' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure that IP forwarding is not enabled on Instances" 30 | 31 | desc "Compute Engine instance cannot forward a packet unless the source IP address of the packet matches the IP address of the instance. Similarly, GCP won't deliver a packet whose destination IP address is different than the IP address of the instance receiving the packet. However, both capabilities are required if you want to use instances to help route packets. Forwarding of data packets should be disabled to prevent data loss or information disclosure." 32 | desc 'rationale', "Compute Engine instance cannot forward a packet unless the source IP address of the packet matches the IP address of the instance. Similarly, GCP won't deliver a packet whose destination IP address is different than the IP address of the instance receiving the packet. However, both capabilities are required if you want to use instances to help route packets. To enable this source and destination IP check, disable the canIpForward field, which allows an instance to send and receive packets with non-matching destination or source IPs." 33 | 34 | tag cis_scored: true 35 | tag cis_level: 1 36 | tag cis_gcp: control_id.to_s 37 | tag cis_version: cis_version.to_s 38 | tag project: gcp_project_id.to_s 39 | tag nist: %w[CM-6 CM-8] 40 | 41 | ref 'CIS Benchmark', url: cis_url.to_s 42 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/networking#canipforward' 43 | 44 | gce_instances.each do |instance| 45 | next if instance[:name] =~ /^gke-/ 46 | gce = google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]) 47 | describe.one do 48 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]}" do 49 | subject { gce } 50 | its('can_ip_forward') { should be false } 51 | end 52 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]} ip-forwarding disabled" do 53 | subject { gce } 54 | it { should exist } 55 | end 56 | end 57 | end 58 | 59 | if gce_instances.empty? 60 | impact 'none' 61 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 62 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /controls/4.07-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure VM disks for critical VMs are encrypted with CustomerSupplied Encryption Keys (CSEK)' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.7' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure VM disks for critical VMs are encrypted with CustomerSupplied Encryption Keys (CSEK)" 30 | 31 | desc 'Customer-Supplied Encryption Keys (CSEK) are a feature in Google Cloud Storage and Google Compute Engine. If you supply your own encryption keys, Google uses your key to protect the Google-generated keys used to encrypt and decrypt your data. By default, Google Compute Engine encrypts all data at rest. Compute Engine handles and manages this encryption for you without any additional actions on your part. However, if you wanted to control and manage this encryption yourself, you can provide your own encryption keys.' 32 | desc 'rationale', "By default, Google Compute Engine encrypts all data at rest. Compute Engine handles and manages this encryption for you without any additional actions on your part. However, if you wanted to control and manage this encryption yourself, you can provide your own encryption keys. 33 | 34 | If you provide your own encryption keys, Compute Engine uses your key to protect the Google-generated keys used to encrypt and decrypt your data. Only users who can provide the correct key can use resources protected by a customer-supplied encryption key. 35 | 36 | Google does not store your keys on its servers and cannot access your protected data unless you provide the key. This also means that if you forget or lose your key, there is no way for Google to recover the key or to recover any data encrypted with the lost key. 37 | 38 | At least business critical VMs should have VM disks encrypted with CSEK." 39 | 40 | tag cis_scored: true 41 | tag cis_level: 2 42 | tag cis_gcp: control_id.to_s 43 | tag cis_version: cis_version.to_s 44 | tag project: gcp_project_id.to_s 45 | tag nist: ['SC-1'] 46 | 47 | ref 'CIS Benchmark', url: cis_url.to_s 48 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/disks/customer-supplied-encryption#encrypt_a_new_persistent_disk_with_your_own_keys' 49 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/reference/rest/v1/disks/get' 50 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/disks/customer-supplied-encryption#key_file' 51 | 52 | gce_instances.each do |instance| 53 | next if instance[:name] =~ /^gke-/ 54 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]}" do 55 | subject { google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]) } 56 | it { should have_disks_encrypted_with_csek } 57 | end 58 | end 59 | 60 | if gce_instances.empty? 61 | impact 'none' 62 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 63 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /controls/4.08-vms.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure Compute instances are launched with Shielded VM enabled' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | gce_zones = input('gce_zones') 19 | cis_version = input('cis_version') 20 | cis_url = input('cis_url') 21 | control_id = '4.8' 22 | control_abbrev = 'vms' 23 | 24 | gce_instances = GCECache(project: gcp_project_id, gce_zones: gce_zones).gce_instances_cache 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure Compute instances are launched with Shielded VM enabled" 30 | 31 | desc 'To defend against against advanced threats and ensure that the boot loader and firmware 32 | on your VMs are signed and untampered, it is recommended that Compute instances are 33 | launched with Shielded VM enabled.' 34 | desc 'rationale', "Shielded VMs are virtual machines (VMs) on Google Cloud Platform hardened by a set of 35 | security controls that help defend against rootkits and bootkits. 36 | Shielded VM offers verifiable integrity of your Compute Engine VM instances, so you can be 37 | confident your instances haven't been compromised by boot- or kernel-level malware or 38 | rootkits. Shielded VM's verifiable integrity is achieved through the use of Secure Boot, 39 | virtual trusted platform module (vTPM)-enabled Measured Boot, and integrity monitoring. 40 | Shielded VM instances run firmware which is signed and verified using Google's Certificate 41 | Authority, ensuring that the instance's firmware is unmodified and establishing the root of 42 | trust for Secure Boot. 43 | Integrity monitoring helps you understand and make decisions about the state of your VM 44 | instances and the Shielded VM vTPM enables Measured Boot by performing the 45 | measurements needed to create a known good boot baseline, called the integrity policy 46 | baseline. The integrity policy baseline is used for comparison with measurements from 47 | subsequent VM boots to determine if anything has changed. 48 | Secure Boot helps ensure that the system only runs authentic software by verifying the 49 | digital signature of all boot components, and halting the boot process if signature 50 | verification fails." 51 | 52 | tag cis_scored: true 53 | tag cis_level: 2 54 | tag cis_gcp: control_id.to_s 55 | tag cis_version: cis_version.to_s 56 | tag project: gcp_project_id.to_s 57 | tag nist: ['SC-1'] 58 | 59 | ref 'CIS Benchmark', url: cis_url.to_s 60 | ref 'GCP Docs', url: 'https://cloud.google.com/compute/docs/instances/modifying-shielded-vm' 61 | ref 'GCP Docs', url: 'https://cloud.google.com/shielded-vm' 62 | ref 'GCP Docs', url: 'https://cloud.google.com/security/shielded-cloud/shielded-vm#organization-policy-constraint' 63 | 64 | gce_instances.each do |instance| 65 | instance_object = google_compute_instance(project: gcp_project_id, zone: instance[:zone], name: instance[:name]) 66 | describe "[#{gcp_project_id}] Instance #{instance[:zone]}/#{instance[:name]}" do 67 | if instance_object.shielded_instance_config.nil? 68 | it 'should have a shielded instance config' do 69 | expect(false).to be true 70 | end 71 | else 72 | it 'should have secure boot enabled' do 73 | expect(instance_object.shielded_instance_config.enable_secure_boot).to be true 74 | end 75 | it 'should have integrity monitoring enabled' do 76 | expect(instance_object.shielded_instance_config.enable_integrity_monitoring).to be true 77 | end 78 | it 'should have virtual trusted platform module (vTPM) enabled' do 79 | expect(instance_object.shielded_instance_config.enable_vtpm).to be true 80 | end 81 | end 82 | end 83 | end 84 | 85 | if gce_instances.empty? 86 | impact 'none' 87 | describe "[#{gcp_project_id}] No Google Compute Engine instances were found. This test is Not Applicable." do 88 | skip "[#{gcp_project_id}] No Google Compute Engine instances were found" 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /controls/5.01-storage.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud Storage bucket is not anonymously or publicly accessible' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '5.1' 21 | control_abbrev = 'storage' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'high' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that Cloud Storage bucket is not anonymously or publicly accessible" 27 | 28 | desc 'It is recommended that IAM policy on Cloud Storage bucket does not allows anonymous and/or public access.' 29 | desc 'rationale', 'Allowing anonymous and/or public access grants permissions to anyone to access bucket content. Such access might not be desired if you are storing any sensitive data. Hence, ensure that anonymous and/or public access to a bucket is not allowed.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: %w[AC-2 CA-3] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/access-control/iam-reference' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/access-control/making-data-public' 41 | 42 | storage_buckets = google_storage_buckets(project: gcp_project_id).bucket_names 43 | 44 | storage_buckets.each do |bucket| 45 | google_storage_bucket_iam_bindings(bucket: bucket).iam_binding_roles.each do |role| 46 | describe "[#{gcp_project_id}] GCS Bucket #{bucket}, Role: #{role}" do 47 | subject { google_storage_bucket_iam_binding(bucket: bucket, role: role) } 48 | its('members') { should_not include 'allUsers' } 49 | its('members') { should_not include 'allAuthenticatedUsers' } 50 | end 51 | end 52 | end 53 | 54 | if storage_buckets.empty? 55 | impact 'none' 56 | describe "[#{gcp_project_id}] No Google Storage Buckets were found. This test is Not Applicable." do 57 | skip "[#{gcp_project_id}] No Google Storage Buckets were found" 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /controls/5.02-storage.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud Storage buckets have uniform bucket-level access enabled' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '5.2' 21 | control_abbrev = 'storage' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'medium' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that Cloud Storage buckets have uniform bucket-level access enabled" 27 | 28 | desc 'It is recommended that uniform bucket-level access is enabled on Cloud Storage buckets.' 29 | desc 'rationale', "It is recommended to use uniform bucket-level access to unify and simplify how you grant 30 | access to your Cloud Storage resources. 31 | Cloud Storage offers two systems for granting users permission to access your buckets and 32 | objects: Cloud Identity and Access Management (Cloud IAM) and Access Control Lists 33 | (ACLs). These systems act in parallel - in order for a user to access a Cloud Storage 34 | resource, only one of the systems needs to grant the user permission. Cloud IAM is used 35 | throughout Google Cloud and allows you to grant a variety of permissions at the bucket and 36 | project levels. ACLs are used only by Cloud Storage and have limited permission options, 37 | but they allow you to grant permissions on a per-object basis. 38 | 39 | In order to support a uniform permissioning system, Cloud Storage has uniform bucket- 40 | level access. Using this feature disables ACLs for all Cloud Storage resources: access to 41 | 42 | Cloud Storage resources then is granted exclusively through Cloud IAM. Enabling uniform 43 | bucket-level access guarantees that if a Storage bucket is not publicly accessible, no object 44 | in the bucket is publicly accessible either." 45 | 46 | tag cis_scored: true 47 | tag cis_level: 2 48 | tag cis_gcp: control_id.to_s 49 | tag cis_version: cis_version.to_s 50 | tag project: gcp_project_id.to_s 51 | tag nist: ['AC-3'] 52 | 53 | ref 'CIS Benchmark', url: cis_url.to_s 54 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/uniform-bucket-level-access' 55 | 56 | storage_buckets = google_storage_buckets(project: gcp_project_id).bucket_names 57 | 58 | storage_buckets.each do |bucket| 59 | uniform_bucket_level_access = google_storage_bucket(name: bucket).acl.nil? 60 | describe "[#{gcp_project_id}] GCS Bucket #{bucket}" do 61 | it 'should have uniform bucket-level access enabled' do 62 | expect(uniform_bucket_level_access).to be true 63 | end 64 | end 65 | end 66 | 67 | if storage_buckets.empty? 68 | impact 'none' 69 | describe "[#{gcp_project_id}] No Google Storage Buckets were found. This test is Not Applicable." do 70 | skip "[#{gcp_project_id}] No Google Storage Buckets were found" 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /controls/6.01-db.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that MySql database instances are secure.' 16 | 17 | # title 'Ensure that MySql database instance does not allow anyone to connect with administrative privileges.' 18 | 19 | gcp_project_id = input('gcp_project_id') 20 | cis_version = input('cis_version') 21 | cis_url = input('cis_url') 22 | control_id = '6.1' 23 | control_abbrev = 'db' 24 | 25 | sql_cache = CloudSQLCache(project: gcp_project_id) 26 | sql_instance_names = sql_cache.instance_names 27 | 28 | # 6.1.1 29 | sub_control_id = "#{control_id}.1" 30 | control "cis-gcp-#{sub_control_id}-#{control_abbrev}" do 31 | impact 'high' 32 | 33 | title "[#{control_abbrev.upcase}] Ensure that MySql database instance does not allow anyone to connect with administrative privileges." 34 | 35 | desc "It is recommended to set a password for the administrative user (root by default) to prevent unauthorized access to the SQL database Instances. 36 | This recommendation is applicable only for MySql Instances. PostgreSQL does not offer any setting for No Password from cloud console." 37 | desc 'rationale', 'At the time of MySql Instance creation, not providing a administrative password allows anyone to connect to the SQL database instance with administrative privileges. Root password should be set to ensure only authorized users have these privileges.' 38 | 39 | tag cis_scored: true 40 | tag cis_level: 1 41 | tag cis_gcp: sub_control_id.to_s 42 | tag cis_version: cis_version.to_s 43 | tag project: gcp_project_id.to_s 44 | tag nist: ['IA-5'] 45 | 46 | ref 'CIS Benchmark', url: cis_url.to_s 47 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/create-manage-users' 48 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/create-instance' 49 | 50 | describe 'This control is not scored' do 51 | skip 'This control is not scored' 52 | end 53 | end 54 | 55 | # 6.1.2 56 | sub_control_id = "#{control_id}.2" 57 | control "cis-gcp-#{sub_control_id}-#{control_abbrev}" do 58 | impact 'none' 59 | 60 | title "[#{control_abbrev.upcase}] Ensure 'skip_show_database' database flag for Cloud SQL Mysql 61 | instance is set to 'on'" 62 | 63 | desc 'It is recommended to set skip_show_database database flag for Cloud SQL Mysql instance to on' 64 | desc 'rationale', "'skip_show_database' database flag prevents people from using the SHOW DATABASES 65 | statement if they do not have the SHOW DATABASES privilege. This can improve security if 66 | you have concerns about users being able to see databases belonging to other users. Its 67 | effect depends on the SHOW DATABASES privilege: If the variable value is ON, the SHOW 68 | DATABASES statement is permitted only to users who have the SHOW DATABASES 69 | privilege, and the statement displays all database names. If the value is OFF, SHOW 70 | DATABASES is permitted to all users, but displays the names of only those databases for 71 | which the user has the SHOW DATABASES or other privilege. This recommendation is 72 | applicable to Mysql database instances." 73 | 74 | tag cis_scored: true 75 | tag cis_level: 1 76 | tag cis_gcp: sub_control_id.to_s 77 | tag cis_version: cis_version.to_s 78 | tag project: gcp_project_id.to_s 79 | tag nist: ['AC-3'] 80 | 81 | ref 'CIS Benchmark', url: cis_url.to_s 82 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/flags' 83 | 84 | sql_instance_names.each do |db| 85 | if sql_cache.instance_objects[db].database_version.include? 'MYSQL' 86 | impact 'medium' 87 | if sql_cache.instance_objects[db].settings.database_flags.nil? 88 | describe "[#{gcp_project_id} , #{db} ] does not any have database flags." do 89 | subject { false } 90 | it { should be true } 91 | end 92 | else 93 | describe.one do 94 | sql_cache.instance_objects[db].settings.database_flags.each do |flag| 95 | next unless flag.name == 'skip_show_database' 96 | describe flag do 97 | its('name') { should cmp 'skip_show_database' } 98 | its('value') { should cmp 'on' } 99 | end 100 | end 101 | end 102 | end 103 | else 104 | describe "[#{gcp_project_id}] [#{db}] is not a MySQL database. This test is Not Applicable." do 105 | skip "[#{gcp_project_id}] [#{db}] is not a MySQL database" 106 | end 107 | end 108 | end 109 | 110 | if sql_instance_names.empty? 111 | describe 'There are no Cloud SQL Instances in this project. This test is Not Applicable.' do 112 | skip 'There are no Cloud SQL Instances in this project' 113 | end 114 | end 115 | end 116 | 117 | # 6.1.3 118 | sub_control_id = "#{control_id}.3" 119 | control "cis-gcp-#{sub_control_id}-#{control_abbrev}" do 120 | impact 'none' 121 | 122 | title "[#{control_abbrev.upcase}] Ensure that the 'local_infile' database flag for a Cloud SQL Mysql instance is set to 'off'" 123 | 124 | desc 'It is recommended to set the local_infile database flag for a Cloud SQL MySQL instance to off.' 125 | desc 'rationale', "The local_infile flag controls the server-side LOCAL capability for LOAD DATA statements. Depending on the 126 | local_infile setting, the server refuses or permits local data loading by clients that have LOCAL enabled on 127 | the client side." 128 | 129 | tag cis_scored: true 130 | tag cis_level: 1 131 | tag cis_gcp: sub_control_id.to_s 132 | tag cis_version: cis_version.to_s 133 | tag project: gcp_project_id.to_s 134 | tag nist: ['SC-1'] 135 | 136 | ref 'CIS Benchmark', url: cis_url.to_s 137 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/flags' 138 | 139 | sql_instance_names.each do |db| 140 | if sql_cache.instance_objects[db].database_version.include? 'MYSQL' 141 | impact 'medium' 142 | if sql_cache.instance_objects[db].settings.database_flags.nil? 143 | describe "[#{gcp_project_id} , #{db} ] does not any have database flags." do 144 | subject { false } 145 | it { should be true } 146 | end 147 | else 148 | describe.one do 149 | sql_cache.instance_objects[db].settings.database_flags.each do |flag| 150 | next unless flag.name == 'local_infile' 151 | describe flag do 152 | its('name') { should cmp 'local_infile' } 153 | its('value') { should cmp 'off' } 154 | end 155 | end 156 | end 157 | end 158 | else 159 | describe "[#{gcp_project_id}] [#{db}] is not a MySQL database. This test is Not Applicable." do 160 | skip "[#{gcp_project_id}] [#{db}] is not a MySQL database" 161 | end 162 | end 163 | end 164 | 165 | if sql_instance_names.empty? 166 | describe "[#{gcp_project_id}] does not have CloudSQL instances. This test is Not Applicable." do 167 | skip "[#{gcp_project_id}] does not have CloudSQL instances." 168 | end 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /controls/6.04-db.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud SQL database instance requires all incoming connections to use SSL' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '6.4' 21 | control_abbrev = 'db' 22 | 23 | sql_cache = CloudSQLCache(project: gcp_project_id) 24 | sql_instance_names = sql_cache.instance_names 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'high' 28 | 29 | title "[#{control_abbrev.upcase}] Ensure that Cloud SQL database instance requires all incoming connections to use SSL" 30 | 31 | desc 'It is recommended to enforce all incoming connections to SQL database instance to use SSL.' 32 | desc 'rationale', 'SQL database connections if successfully trapped (MITM); can reveal sensitive data like credentials, database queries, query outputs etc. For security, it is recommended to always use SSL encryption when connecting to your instance. This recommendation is applicable for Postgresql, MySql generation 1 and MySql generation 2 Instances.' 33 | 34 | tag cis_scored: true 35 | tag cis_level: 1 36 | tag cis_gcp: control_id.to_s 37 | tag cis_version: cis_version.to_s 38 | tag project: gcp_project_id.to_s 39 | tag nist: %w[SC-1 SC-8] 40 | 41 | ref 'CIS Benchmark', url: cis_url.to_s 42 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/postgres/configure-ssl-instance' 43 | 44 | if sql_instance_names.empty? 45 | impact 'none' 46 | describe "[#{gcp_project_id}] does not have CloudSQL instances. This test is Not Applicable." do 47 | skip "[#{gcp_project_id}] does not have CloudSQL instances." 48 | end 49 | else 50 | sql_instance_names.each do |db| 51 | describe "[#{gcp_project_id}] CloudSQL #{db}" do 52 | subject { sql_cache.instance_objects[db] } 53 | it { should have_ip_configuration_require_ssl } 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /controls/6.05-db.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Database Server should accept connections only from trusted Network(s)/IP(s) and restrict access from the world' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '6.5' 21 | control_abbrev = 'db' 22 | 23 | sql_cache = CloudSQLCache(project: gcp_project_id) 24 | sql_instance_names = sql_cache.instance_names 25 | 26 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 27 | impact 'medium' 28 | 29 | title "[#{control_abbrev.upcase}] Database Server should accept connections only from trusted Network(s)/IP(s) and restrict access from the world" 30 | 31 | desc 'Database Server should accept connections only from trusted Network(s)/IP(s) and restrict access from the world.' 32 | desc 'rationale', 'To minimize attack surface on a Database server instance, only trusted/known and required IP(s) should be white-listed to connect to it.' 33 | 34 | tag cis_scored: true 35 | tag cis_level: 1 36 | tag cis_gcp: control_id.to_s 37 | tag cis_version: cis_version.to_s 38 | tag project: gcp_project_id.to_s 39 | tag nist: %w[SC-1 AC-3] 40 | 41 | ref 'CIS Benchmark', url: cis_url.to_s 42 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/configure-ip' 43 | 44 | if sql_instance_names.empty? 45 | impact 'none' 46 | describe "[#{gcp_project_id}] does not have CloudSQL instances. This test is Not Applicable." do 47 | skip "[#{gcp_project_id}] does not have CloudSQL instances." 48 | end 49 | else 50 | sql_instance_names.each do |db| 51 | describe "[#{gcp_project_id}] CloudSQL #{db}" do 52 | subject { sql_cache.instance_objects[db].settings.ip_configuration.authorized_networks } 53 | it { should_not include('0.0.0.0/0') } 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /controls/6.06-db.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud SQL database instances do not have public IPs' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '6.6' 21 | control_abbrev = 'db' 22 | 23 | sql_cache = CloudSQLCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'high' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that Cloud SQL database instances do not have public IPs" 29 | 30 | desc 'It is recommended to configure Second Generation Sql instance to use private IPs instead of public IPs.' 31 | desc 'rationale', "To lower the organization's attack surface, Cloud SQL databases should not have public IPs. Private IPs provide improved network security and lower latency for your application." 32 | 33 | tag cis_scored: true 34 | tag cis_level: 2 35 | tag cis_gcp: control_id.to_s 36 | tag cis_version: cis_version.to_s 37 | tag project: gcp_project_id.to_s 38 | tag nist: ['SC-1'] 39 | 40 | ref 'CIS Benchmark', url: cis_url.to_s 41 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/configure-private-ip' 42 | 43 | if sql_cache.instance_names.empty? 44 | impact 'none' 45 | describe "[#{gcp_project_id}] does not have CloudSQL instances. This test is Not Applicable." do 46 | skip "[#{gcp_project_id}] does not have CloudSQL instances." 47 | end 48 | else 49 | sql_cache.instance_names.each do |db| 50 | sql_cache.instance_objects[db].ip_addresses.each do |ip_address| 51 | describe "[#{gcp_project_id}] CloudSQL #{db}" do 52 | subject { ip_address } 53 | its('type') { should_not include('PRIMARY') } 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /controls/6.07-db.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that Cloud SQL database instances are configured with automated backups' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '6.7' 21 | control_abbrev = 'db' 22 | 23 | sql_cache = CloudSQLCache(project: gcp_project_id) 24 | 25 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 26 | impact 'medium' 27 | 28 | title "[#{control_abbrev.upcase}] Ensure that Cloud SQL database instances are configured with automated backups" 29 | 30 | desc 'It is recommended to have all SQL database instances set to enable automated backups.' 31 | desc 'rationale', 'Backups provide a way to restore a Cloud SQL instance to recover lost data or recover from a problem 32 | with that instance. Automated backups need to be set for any instance that contains data that should 33 | be protected from loss or damage' 34 | 35 | tag cis_scored: true 36 | tag cis_level: 1 37 | tag cis_gcp: control_id.to_s 38 | tag cis_version: cis_version.to_s 39 | tag project: gcp_project_id.to_s 40 | tag nist: ['CP-9'] 41 | 42 | ref 'CIS Benchmark', url: cis_url.to_s 43 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/mysql/backup-recovery/backups' 44 | ref 'GCP Docs', url: 'https://cloud.google.com/sql/docs/postgres/backup-recovery/backing-up' 45 | 46 | if sql_cache.instance_names.empty? 47 | impact 'none' 48 | describe "[#{gcp_project_id}] does not have any CloudSQL instances, this test is Not Applicable" do 49 | skip "[#{gcp_project_id}] does not have any CloudSQL instances" 50 | end 51 | else 52 | sql_cache.instance_names.each do |db| 53 | describe "[#{gcp_project_id}] CloudSQL #{db} should have automated backups enabled and have a start time" do 54 | subject { sql_cache.instance_objects[db].settings.backup_configuration } 55 | its('enabled') { should cmp true } 56 | its('start_time') { should_not eq '' } 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /controls/7.01-bq.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that BigQuery datasets are not anonymously or publicly accessible' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '7.1' 21 | control_abbrev = 'storage' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'high' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that BigQuery datasets are not anonymously or publicly accessible" 27 | 28 | desc 'It is recommended that the IAM policy on BigQuery datasets does not allow anonymous and/or public access.' 29 | desc 'rationale', 'Granting permissions to allUsers or allAuthenticatedUsers allows anyone to access the dataset. Such access might not be desirable if sensitive data is being stored in the dataset. Therefore, ensure that anonymous and/or public access to a dataset is not allowed.' 30 | 31 | tag cis_scored: true 32 | tag cis_level: 1 33 | tag cis_gcp: control_id.to_s 34 | tag cis_version: cis_version.to_s 35 | tag project: gcp_project_id.to_s 36 | tag nist: ['AC-3'] 37 | 38 | ref 'CIS Benchmark', url: cis_url.to_s 39 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/access-control/iam-reference' 40 | ref 'GCP Docs', url: 'https://cloud.google.com/storage/docs/access-control/making-data-public' 41 | 42 | if google_bigquery_datasets(project: gcp_project_id).ids.empty? 43 | impact 'none' 44 | describe "[#{gcp_project_id}] does not have BigQuery Datasets, this test is Not Applicable." do 45 | skip "[#{gcp_project_id}] does not have BigQuery Datasets" 46 | end 47 | else 48 | google_bigquery_datasets(project: gcp_project_id).ids.each do |name| 49 | google_bigquery_dataset(project: gcp_project_id, name: name.split(':').last).access.each do |access| 50 | describe "[#{gcp_project_id}] BigQuery Dataset #{name} should not be anonymously or publicly accessible," do 51 | subject { access } 52 | its('iam_member') { should_not cmp 'allUsers' } 53 | its('special_group') { should_not cmp 'allAuthenticatedUsers' } 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /controls/7.02-bq.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that all BigQuery Tables are encrypted with Customermanaged encryption key (CMEK)' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '7.2' 21 | control_abbrev = 'storage' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'high' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that all BigQuery Tables are encrypted with Customermanaged encryption key (CMEK)" 27 | 28 | desc 'BigQuery by default encrypts the data as rest by employing Envelope Encryption using 29 | Google managed cryptographic keys. The data is encrypted using the data encryption 30 | keys and data encryption keys themselves are further encrypted using key encryption 31 | keys. This is seamless and do not require any additional input from the user. However, if 32 | you want to have greater control, Customer-managed encryption keys (CMEK) can be used 33 | as encryption key management solution for BigQuery Data Sets. If CMEK is used, the CMEK 34 | is used to encrypt the data encryption keys instead of using google-managed encryption 35 | keys.' 36 | desc 'rationale', 'BigQuery by default encrypts the data as rest by employing Envelope Encryption using 37 | Google managed cryptographic keys. This is seamless and does not require any additional 38 | input from the user. 39 | For greater control over the encryption, customer-managed encryption keys (CMEK) can 40 | be used as encryption key management solution for BigQuery tables. The CMEK is used to 41 | encrypt the data encryption keys instead of using google-managed encryption keys. 42 | BigQuery stores the table and CMEK association and the encryption/decryption is done 43 | automatically. 44 | Applying the Default Customer-managed keys on BigQuery data sets ensures that all the 45 | new tables created in the future will be encrypted using CMEK but existing tables need to 46 | be updated to use CMEK individually.' 47 | 48 | tag cis_scored: true 49 | tag cis_level: 2 50 | tag cis_gcp: control_id.to_s 51 | tag cis_version: cis_version.to_s 52 | tag project: gcp_project_id.to_s 53 | tag nist: ['SC-1'] 54 | 55 | ref 'CIS Benchmark', url: cis_url.to_s 56 | ref 'GCP Docs', url: 'https://cloud.google.com/bigquery/docs/customer-managed-encryption' 57 | 58 | if google_bigquery_datasets(project: gcp_project_id).ids.empty? 59 | impact 'none' 60 | describe "[#{gcp_project_id}] does not have BigQuery Datasets, this test is Not Applicable." do 61 | skip "[#{gcp_project_id}] does not have BigQuery Datasets" 62 | end 63 | else 64 | google_bigquery_datasets(project: gcp_project_id).ids.each do |dataset_name| 65 | google_bigquery_tables(project: gcp_project_id, dataset: dataset_name.split(':').last).table_references.each do |table_reference| 66 | describe "[#{gcp_project_id}] BigQuery Table #{table_reference.table_id} should use customer-managed encryption keys (CMEK)" do 67 | subject { google_bigquery_table(project: gcp_project_id, dataset: dataset_name.split(':').last, name: table_reference.table_id).encryption_configuration } 68 | its('kms_key_name') { should_not eq nil } 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /controls/7.03-bq.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | title 'Ensure that a Default Customer-managed encryption key (CMEK) is specified for all BigQuery Data Sets' 16 | 17 | gcp_project_id = input('gcp_project_id') 18 | cis_version = input('cis_version') 19 | cis_url = input('cis_url') 20 | control_id = '7.3' 21 | control_abbrev = 'storage' 22 | 23 | control "cis-gcp-#{control_id}-#{control_abbrev}" do 24 | impact 'high' 25 | 26 | title "[#{control_abbrev.upcase}] Ensure that a Default Customer-managed encryption key (CMEK) is 27 | specified for all BigQuery Data Sets" 28 | 29 | desc 'BigQuery by default encrypts the data as rest by employing Envelope Encryption using 30 | Google managed cryptographic keys. The data is encrypted using the data encryption 31 | keys and data encryption keys themselves are further encrypted using key encryption 32 | keys. This is seamless and do not require any additional input from the user. However, if 33 | you want to have greater control, Customer-managed encryption keys (CMEK) can be used 34 | as encryption key management solution for BigQuery Data Sets.' 35 | desc 'rationale', 'BigQuery by default encrypts the data as rest by employing Envelope Encryption using 36 | Google managed cryptographic keys. This is seamless and does not require any additional 37 | input from the user. 38 | For greater control over the encryption, customer-managed encryption keys (CMEK) can 39 | be used as encryption key management solution for BigQuery Data Sets. Setting a Default 40 | Customer-managed encryption key (CMEK) for a data set ensure any tables created in 41 | future will use the specified CMEK if none other is provided.' 42 | 43 | tag cis_scored: true 44 | tag cis_level: 2 45 | tag cis_gcp: control_id.to_s 46 | tag cis_version: cis_version.to_s 47 | tag project: gcp_project_id.to_s 48 | tag nist: ['SC-1'] 49 | 50 | ref 'CIS Benchmark', url: cis_url.to_s 51 | ref 'GCP Docs', url: 'https://cloud.google.com/bigquery/docs/customer-managed-encryption' 52 | 53 | if google_bigquery_datasets(project: gcp_project_id).ids.empty? 54 | impact 'none' 55 | describe "[#{gcp_project_id}] does not have BigQuery Datasets, this test is Not Applicable." do 56 | skip "[#{gcp_project_id}] does not have BigQuery Datasets" 57 | end 58 | else 59 | google_bigquery_datasets(project: gcp_project_id).ids.each do |name| 60 | describe "[#{gcp_project_id}] BigQuery Dataset #{name} should use customer-managed encryption keys (CMEK)" do 61 | subject { google_bigquery_dataset(project: gcp_project_id, name: name.split(':').last).default_encryption_configuration } 62 | its('kms_key_name') { should_not eq nil } 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /inspec.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The inspec-gcp-cis-benchmark Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: inspec-gcp-cis-benchmark 16 | title: "InSpec GCP CIS 1.2 Benchmark" 17 | maintainer: "Google Cloud Platform" 18 | copyright: "(c) 2022, Google, Inc." 19 | copyright_email: "copyright@google.com" 20 | license: "Apache-2.0" 21 | summary: "Inspec Google Cloud Platform Center for Internet Security Benchmark v1.2 Profile" 22 | version: 1.2.0-0 23 | 24 | supports: 25 | - platform: gcp 26 | 27 | depends: 28 | - name: inspec-gcp-helpers 29 | url: https://github.com/GoogleCloudPlatform/inspec-gcp-helpers/archive/1.0.9.tar.gz 30 | 31 | inputs: 32 | # {{gcp_project_id}} 33 | # must be defined at runtime by the user 34 | - name: gcp_project_id 35 | description: "The GCP project identifier." 36 | type: String 37 | required: true 38 | value: 'aaa-bbb-ccc-ddd' 39 | 40 | - name: cis_version 41 | description: "The short version of the GCP CIS Benchmark" 42 | value: "1.2" 43 | type: String 44 | 45 | - name: cis_url 46 | description: "The URL to the GCP CIS Benchmark" 47 | value: "https://www.cisecurity.org/benchmark/google_cloud_computing_platform/" 48 | type: String 49 | 50 | - name: sa_key_older_than_seconds 51 | description: "How many seconds SA keys should not be older than" 52 | value: 7776000 53 | type: Numeric 54 | 55 | - name: kms_rotation_period_seconds 56 | description: "How many seconds KMS Keys should be last rotated (90 days)" 57 | value: 7776000 58 | type: Numeric 59 | 60 | - name: gcp_gke_locations 61 | description: "The list of regions and/or zone names where GKE clusters are running. An empty array searches all locations" 62 | type: Array 63 | value: 64 | - "" 65 | 66 | - name: gce_zones 67 | description: "The list of zone names where GCE instances are running. An empty array searches all locations" 68 | type: Array 69 | value: 70 | - "" 71 | 72 | - name: log_min_messages 73 | description: "(Cloud SQL PostgreSQL) The log_min_messages flag controls which message levels are written to the server log (see control 6.2.13). ERROR is cosidered best practice." 74 | value: "ERROR" 75 | type: String 76 | 77 | - name: log_min_error_statement 78 | description: "(Cloud SQL PostgreSQL) The log_min_error_statement flag controls which SQL statements that cause an error condition are recorded in the server log (see control 6.2.14). ERROR is cosidered best practice." 79 | value: "ERROR" 80 | type: String 81 | 82 | - name: log_error_verbosity 83 | description: "(Cloud SQL PostgreSQL) The log_min_error_statement flag controls the amount of detail written in the server log for each message that is logged (see control 6.2.2). DEFAULT is cosidered best practice." 84 | value: "DEFAULT" 85 | type: String 86 | 87 | - name: log_statement 88 | description: "(Cloud SQL PostgreSQL) The log_statement flag controls which SQL statements are logged. Valid values are none (off), ddl, mod, and all (all statements) (see control 6.2.7). ddl is cosidered best practice." 89 | value: "ddl" 90 | type: String 91 | 92 | - name: log_hostname 93 | description: "(Cloud SQL PostgreSQL) By default, connection log messages only show the IP address of the connecting host. Turning this parameter on causes logging of the host name as well (see control 6.2.8)." 94 | value: "off" 95 | type: String 96 | 97 | - name: user_connections 98 | description: "(Cloud SQL SQL Server) The user connections option specifies the maximum number of simultaneous user connections that are allowed on an instance of SQL Server. (see control 6.3.3)." 99 | value: "0" 100 | type: String 101 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /walkthrough.md: -------------------------------------------------------------------------------- 1 | # Introduction to Running InSpec in Cloud Shell 2 | 3 | ## Getting Started 4 | 5 | This guide will show you how to install InSpec on your Cloud Shell instance and how to use InSpec to check the infrastructure in your Google Cloud Project against the CIS GCP Benchmark. 6 | 7 | **Time to complete**: About 5 minutes 8 | 9 | Click the **Start** button to move to the next step. 10 | 11 | ## What is InSpec? 12 | 13 | Before we jump in, let's briefly go over what InSpec can do. 14 | 15 | [InSpec](https://github.com/inspec/inspec), a popular framework in the DevSecOps community, checks the configuration state of resources within virtual machines, containers, and cloud providers such as GCP, AWS, and Azure. InSpec's lightweight nature, approachable domain specific Language (DSL) and extensibility, make it a valuable tool for: 16 | 17 | - Expressing compliance policies as code 18 | - Enabling development teams to add application-specific tests and assess the compliance of their applications to security policies before pushing changes to the production environment. 19 | - Automating compliance verification in CI/CD pipelines and as part of the release process 20 | - Unifying compliance assessments across multiple cloud providers and on premises environments 21 | 22 | Continue on to the next step to start setting up your tutorial. 23 | 24 | ## Installing InSpec 25 | 26 | InSpec is distributed as a Docker image. All you need to do is pull the image from the repository and create a function to run Inspec: 27 | 28 | ```bash 29 | docker pull chef/inspec:4.26.15 30 | 31 | function inspec-docker { docker run -it -e GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS=true -e CHEF_LICENSE=accept-no-persist --rm -v ~/.config:/root/.config -v $(pwd):/share chef/inspec:4.26.15 "$@"; } 32 | ``` 33 | 34 | **Tip**: Click the Copy to Cloud Shell button on the side of the code box and then hit Enter in your terminal. You can also click the copy button on the side of the code box and paste the command in the Cloud Shell terminal to run it. 35 | 36 | Next, you’ll select a Google Cloud Project to scan with InSpec. 37 | 38 | ## Select the Google Cloud Project to scan 39 | 40 | Pick a project where you have sufficient permissions. We'll use your user credentials in Cloud Shell to scan the project. 41 | 42 | 43 | 44 | The project you selected is **{{project-id}}**. If this is blank, make sure you selected a project using the dropdown box above. 45 | 46 | Hit Next after you successfully selected your project. 47 | 48 | ## Scan Your Project 49 | 50 | To scan your project against the CIS GCP Benchmark with InSpec, run: 51 | 52 | ```bash 53 | inspec-docker exec https://github.com/GoogleCloudPlatform/inspec-gcp-cis-benchmark.git -t gcp:// --input gcp_project_id={{project-id}} --reporter cli json:{{project-id}}_scan.json 54 | ``` 55 | 56 | This should take about two minutes to complete. 57 | 58 | Once complete, your terminal output should look something like this: 59 | 60 | ```bash 61 | Profile Summary: 14 successful controls, 34 control failures, 7 controls skipped 62 | Test Summary: 107 successful, 88 failures, 7 skipped 63 | ``` 64 | 65 | You can scroll up to see the details of passing and failing controls. 66 | 67 | To scan another project, press the Previous button and select a different project. 68 | 69 | Press Next if you're done scanning projects. 70 | 71 | ## Review your scan results with [Heimdall-Lite](https://heimdall-lite.mitre.org) 72 | 73 | ### What is Heimdall-Lite? 74 | 75 | Heimdall-Lite is a great open-source Security Results Viewer by the [MITRE Corporation](https://www.mitre.org) for reviewing your GCP CIS Benchmark scan results. 76 | 77 | Heimdall-Lite is one of many MITRE [Security Automation Framework](https://saf.mitre.org) (SAF) Supporting Tools working to enhance the Security Automation and DevSecOps communities. 78 | 79 | The [MITRE SAF](https://saf.mitre.org) is an open-source community partnership including Government, Industry and the Open Community working together to make truly automated security a reality. It also hosts many InSpec profiles created by the SAF and references to many partner developed profiles - **_including this one_**. 80 | 81 | **Tip**: MITRE hosts Heimdall-Lite on GitHub pages, but you can easily run it in your environment via Docker or NPM or whatever suites your need. See the projects GitHub more information. 82 | 83 | ### Download your JSON formatted results 84 | 85 | 1. Right click on your `{{project-id}}_scan.json` file 86 | 2. Then select `Download` to save the `{{project-id}}_scan.json` file locally 87 | 88 | ### Go to Heimdall Lite and Load your JSON formatted Results 89 | 90 | 1. Navigate to [Heimdall Lite](https://heimdall-lite.mitre.org) 91 | 2. Click `Local Files` on the left side of the loader 92 | 3. Drag and Drop or select and load your `{{project-id}}_scan.json` file to review your results. 93 | 94 | ## Congratulations 95 | 96 | 97 | 98 | You’re all set! 99 | 100 | You can now scan your Google Cloud Projects with InSpec directly from Cloud Shell. 101 | --------------------------------------------------------------------------------