diff --git a/docs/resources/google_project_alert_policies.md b/docs/resources/google_project_alert_policies.md new file mode 100644 index 000000000..b8e67f685 --- /dev/null +++ b/docs/resources/google_project_alert_policies.md @@ -0,0 +1,78 @@ +--- +title: About the google_project_alert_policies Resource +platform: gcp +--- + +# google\_compute\_alert\_policies + +Use the `google_project_alert_policies` InSpec audit resource to test properties of all, or a filtered group of, GCP project alert policies. + +
+ +## Syntax + +A `google_project_alert_policies` resource block collects GCP project alert policies by project then tests that group. + + describe google_project_alert_policies(project: 'chef-inspec-gcp') do + it { should exist } + end + +Use this InSpec resource to enumerate IDs then test in-depth using `google_project_alert_policy`. + + google_project_alert_policies(project: 'chef-inspec-gcp').policy_names.each do |policy_name| + describe google_project_alert_policy(name: policy_name) do + it { should exist } + it { should be_enabled } + end + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that there are no more than a specified number of project alert policies available for the project + + describe google_project_alert_policies(project: 'chef-inspec-gcp') do + its('count') { should be <= 100} + end + +### Test that an expected policy name is available for the project + + describe google_project_alert_policies(project: 'chef-inspec-gcp') do + its('policy_names') { should include 'projects/spaterson-project/alertPolicies/9271751234503117449' } + end + +### Test whether any expected policy display name is available for the project + + describe google_project_alert_policies(project: 'chef-inspec-gcp') do + its('policy_display_names') { should_not include 'banned policy' } + end + +### Ensure no existing policies are inactive + + describe google_project_alert_policies(project: 'chef-inspec-gcp') do + its('policy_enabled_states') { should_not include false } + end + + +
+ +## Filter Criteria + +This resource supports the following filter criteria: `policy_name`; `policy_display_name`; `policy_filter_list` and `policy_enabled_state`. Any of these may be used with `where`, as a block or as a method. + +## Properties + +* `policy_names` - an array of google_project_alert_policy name strings +* `policy_display_names` - an array of google_project_alert_policy display name strings +* `policy_enabled_states`- an array of google_project_alert_policy enabled status booleans +* `policy_filter_lists`- an array of google_project_alert_policy_condition filter string arrays + +
+ + +## GCP Permissions + +Ensure the [Stackdriver Logging API](https://console.cloud.google.com/apis/api/logging.googleapis.com/) is enabled for the project. \ No newline at end of file diff --git a/docs/resources/google_project_alert_policy.md b/docs/resources/google_project_alert_policy.md new file mode 100644 index 000000000..80f9a5f92 --- /dev/null +++ b/docs/resources/google_project_alert_policy.md @@ -0,0 +1,49 @@ +--- +title: About the google_project_alert_policy Resource +platform: gcp +--- + +# google\_project\_alert\_policy + +Use the `google_project_alert_policy` InSpec audit resource to test properties of a single GCP project alert policy. + +
+ +## Syntax + +A `google_project_alert_policy` resource block declares the tests for a single GCP project alert policy by name. + + describe google_project_alert_policy(name: 'projects/spaterson-project/alertPolicies/9271751234503117449') do + it { should exist } + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that a GCP alert policy is enabled + + describe google_project_alert_policy(name: 'projects/spaterson-project/alertPolicies/9271751234503117449') do + it { should be_enabled } + end + +### Test that a GCP compute alert policy display name is correct + + describe google_project_alert_policy(name: 'projects/spaterson-project/alertPolicies/9271751234503117449') do + its('display_name') { should eq 'policy name' } + end + +
+ +## Properties + +* `combiner`, `conditions`, `creation_record`, `display_name`, `enabled`, `mutation_record`, `name` + +
+ + +## GCP Permissions + +Ensure the [Stackdriver Logging API](https://console.cloud.google.com/apis/api/logging.googleapis.com/) is enabled for the project. diff --git a/docs/resources/google_project_alert_policy_condition.md b/docs/resources/google_project_alert_policy_condition.md new file mode 100644 index 000000000..afc6905b7 --- /dev/null +++ b/docs/resources/google_project_alert_policy_condition.md @@ -0,0 +1,50 @@ +--- +title: About the google_project_alert_policy_condition Resource +platform: gcp +--- + +# google\_project\_alert\_policy\_condition + +Use the `google_project_alert_policy_condition` InSpec audit resource to test properties of a single GCP project alert policy condition. + +
+ +## Syntax + +A `google_project_alert_policy_condition` resource block declares the tests for a single GCP project alert policy condition by name and filter. + + describe google_project_alert_policy_condition(name: 'projects/spaterson-project/alertPolicies/9271751234503117449', filter 'project=\"spaterson-project\"') do + it { should exist } + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + + +### Test that a GCP project alert policy condition has a particular threshold value + + describe google_project_alert_policy_condition(name: 'projects/spaterson-project/alertPolicies/9271751234503117449', filter 'project=\"spaterson-project\"') do + its('condition_threshold_value'){ should eq 0.001 } + end + +### Test that a GCP project alert policy condition has a particular aggregation alignment period + + describe google_project_alert_policy_condition(name: 'projects/spaterson-project/alertPolicies/9271751234503117449', filter 'project=\"spaterson-project\"') do + its('aggregation_alignment_period'){ should eq '60s' } + end + +
+ +## Properties + +* `condition_threshold_value`, `aggregation_alignment_period`, `aggregation_per_series_aligner`, `aggregation_cross_series_reducer` + +
+ + +## GCP Permissions + +Ensure the [Stackdriver Logging API](https://console.cloud.google.com/apis/api/logging.googleapis.com/) is enabled for the project. diff --git a/docs/resources/google_project_metrics.md b/docs/resources/google_project_metrics.md index b948f8ea1..0cf9391a0 100644 --- a/docs/resources/google_project_metrics.md +++ b/docs/resources/google_project_metrics.md @@ -55,12 +55,13 @@ The following examples show how to use this InSpec audit resource. ## Filter Criteria -This resource supports the following filter criteria: `metric_name` and `metric_filter`. Either of these may be used with `where`, as a block or as a method. +This resource supports the following filter criteria: `metric_name`; `metric_type` and `metric_filter`. Either of these may be used with `where`, as a block or as a method. ## Properties * `metric_names` - an array of google_project_metric name strings -* `metric_filters`- an array of google_project_metric filters +* `metric_filters`- an array of google_project_metric filter strings +* `metric_types` - an array of google_project_metric type strings
diff --git a/docs/resources/google_user.md b/docs/resources/google_user.md new file mode 100644 index 000000000..a03c7f231 --- /dev/null +++ b/docs/resources/google_user.md @@ -0,0 +1,61 @@ +--- +title: About the google_user Resource +platform: gcp +--- + +# google\_user + +Use the `google_user` InSpec audit resource to test properties of a single GCP user. + +
+ +## Syntax + +A `google_user` resource block declares the tests for a single GCP user by principal email address or immutable ID. + + describe google_user(user_key: 'principal_email_address@domain.com') do + it { should exist } + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that a GCP user with specified ID exists + + describe google_user(user_key: '110491234567894702010') do + it { should exist } + end + +### Test that a GCP user has expected full name + + describe google_user(user_key: '110491234567894702010') do + its('name.full_name') { should eq "Bill S. Preston Esq." } + end + +### Test that a GCP user has MFA enabled + + describe google_user(user_key: 'theodore_ted_logan@excellentadventure.com') do + it { should have_mfa_enabled } + end + +### Test that a GCP user is suspended or not + + describe google_user(user_key: 'theodore_ted_logan@excellentadventure.com') do + it { should_not be_suspended } + end + +
+ +## Properties + +* `agreed_to_terms`, `archived`, `change_password_at_next_login`, `creation_time`, `customer_id`, `emails`, `etag`, `id`, `include_in_global_address_list`, `ip_whitelisted`, `is_admin`, `is_delegated_admin`, `is_enforced_in2_sv`, `is_enrolled_in2_sv`, `is_mailbox_setup`, `kind`, `last_login_time`, `name`, `non_editable_aliases`, `org_unit_path`, `primary_email`, `suspended` + +
+ + +## GCP Permissions + +Ensure the G Suite Admin SDK [Directory API](https://developers.google.com/admin-sdk/directory/) is enabled and you have sufficient privileges to list users. \ No newline at end of file diff --git a/docs/resources/google_users.md b/docs/resources/google_users.md new file mode 100644 index 000000000..35dd50392 --- /dev/null +++ b/docs/resources/google_users.md @@ -0,0 +1,78 @@ +--- +title: About the google_users Resource +platform: gcp +--- + +# google\_users + +Use the `google_users` InSpec audit resource to test properties of all, or a filtered group of, GCP users. + +
+ +## Syntax + +A `google_users` resource block collects GCP users for the specified customer. As documented [here](https://developers.google.com/admin-sdk/directory/v1/reference/users/list), this defaults to the `my_customer` alias to represent your account's `customerId`. + + describe google_users(customer: 'my_customer') do + it { should exist } + end + +The `domain` argument can optionally be provided to get fields from only one domain. Either the customer or the domain parameter must be provided. + + describe google_users(domain: 'my_domain.com') do + it { should exist } + end + +Use this InSpec resource to enumerate IDs then test in-depth using `google_user`. + + google_users(customer: 'my_customer').user_ids.each do |user_id| + describe google_user(user_key: user_id) do + it { should exist } + it { should_not be_suspended } + end + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that there are no more than a specified number of users available for the project + + describe google_users(customer: 'my_customer') do + its('count') { should be <= 100} + end + +### Test that an expected user is available for the project + + describe google_users(customer: 'my_customer') do + its('user_names') { should include "Monsieur Happy" } + end + +### Test that a subset of all users with name matching "Batman" exists + + google_users(customer: 'my_customer').where(user_full_name: /Batman/).user_ids.each do |user_id| + describe google_user(user_key: user_id) do + it { should exist } + end + end + +
+ +## Filter Criteria + +This resource supports the following filter criteria: `user_id`; `user_full_name` and `user_email`. Any of these may be used with `where`, as a block or as a method. + +## Properties + +* `user_ids` - an array of google_user identifier integers +* `user_full_names` - an array of google_user full name strings +* `user_emails`- an array of google_user primary email address strings + +
+ + +## GCP Permissions + +Ensure the G Suite Admin SDK [Directory API](https://developers.google.com/admin-sdk/directory/) is enabled and you have sufficient privileges to list users. \ No newline at end of file diff --git a/libraries/google_compute_instance.rb b/libraries/google_compute_instance.rb index 1e2d379bf..b2dd7f59d 100644 --- a/libraries/google_compute_instance.rb +++ b/libraries/google_compute_instance.rb @@ -145,6 +145,7 @@ def block_project_ssh_keys return false if !defined?(@instance.metadata.items) @instance.metadata.items.each do |element| return true if element.key=='block-project-ssh-keys' and element.value.casecmp('true').zero? + return true if element.key=='block-project-ssh-keys' and element.value=='1' end false end diff --git a/libraries/google_compute_network.rb b/libraries/google_compute_network.rb index c0d544ecc..0c5e3720d 100644 --- a/libraries/google_compute_network.rb +++ b/libraries/google_compute_network.rb @@ -31,6 +31,8 @@ def legacy? return false if @network.auto_create_subnetworks return false if !defined?(@network.gateway_i_pv4) return false if !defined?(@network.i_pv4_range) + return false if @network.i_pv4_range.nil? + return false if @network.gateway_i_pv4.nil? true end diff --git a/libraries/google_container_cluster.rb b/libraries/google_container_cluster.rb index 11a688535..c817f9924 100644 --- a/libraries/google_container_cluster.rb +++ b/libraries/google_container_cluster.rb @@ -49,7 +49,8 @@ def has_legacy_abac_disabled? def has_master_authorized_networks_enabled? return false if !defined?(@cluster.master_authorized_networks_config) return false if @cluster.master_authorized_networks_config.to_h.empty? - return true if @cluster.master_authorized_networks_config.to_h=={ 'enabled': true } + return false if !defined?(@cluster.master_authorized_networks_config.enabled) + return true if @cluster.master_authorized_networks_config.enabled == true false end @@ -96,6 +97,12 @@ def has_pod_security_policy_config? false end + def private_cluster? + return false if !defined?(@cluster.private_cluster) + return true if @cluster.private_cluster==true + false + end + def exists? !@cluster.nil? end diff --git a/libraries/google_project_alert_policies.rb b/libraries/google_project_alert_policies.rb new file mode 100644 index 000000000..21bd3ec3a --- /dev/null +++ b/libraries/google_project_alert_policies.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'gcp_backend' +require 'google/apis/monitoring_v3' + +module Inspec::Resources + class GoogleProjectAlertPolicies < GcpResourceBase + name 'google_project_alert_policies' + desc 'Verifies settings for GCP project alert policies in bulk' + + example " + describe google_project_alert_policies(project: 'chef-inspec-gcp') do + it { should exist } + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @project = opts[:project] + end + + # FilterTable setup + filter_table_config = FilterTable.create + filter_table_config.add(:policy_names, field: :policy_name) + filter_table_config.add(:policy_display_names, field: :policy_display_name) + filter_table_config.add(:policy_enabled_states, field: :policy_enabled_state) + filter_table_config.add(:policy_filter_lists, field: :policy_filter_list) + filter_table_config.connect(self, :fetch_data) + + def fetch_data + policy_rows = [] + catch_gcp_errors do + @policies = @gcp.gcp_client(Google::Apis::MonitoringV3::MonitoringService).list_project_alert_policies("projects/#{@project}") + end + return [] if !@policies || !@policies.alert_policies + @policies.alert_policies.map do |policy| + policy_filters = [] + policy.conditions.each do |condition| + next if !defined?(condition.condition_threshold.filter) + policy_filters+=[condition.condition_threshold.filter] + end + + policy_rows+=[{ policy_name: policy.name, + policy_display_name: policy.display_name, + policy_enabled_state: policy.enabled, + policy_filter_list: policy_filters }] + end + @table = policy_rows + end + end +end diff --git a/libraries/google_project_alert_policy.rb b/libraries/google_project_alert_policy.rb new file mode 100644 index 000000000..66493a0bf --- /dev/null +++ b/libraries/google_project_alert_policy.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'gcp_backend' +require 'google/apis/monitoring_v3' + +module Inspec::Resources + class GoogleProjectAlertPolicy < GcpResourceBase + name 'google_project_alert_policy' + desc 'Verifies settings for a single GCP project alert policy' + + example " + describe google_project_alert_policy(policy: 'projects/spaterson-project/alertPolicies/9271751234503117449') do + it { should exist } + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @display_name = opts[:policy] + catch_gcp_errors do + @policy = @gcp.gcp_client(Google::Apis::MonitoringV3::MonitoringService).get_project_alert_policy(opts[:policy]) + create_resource_methods(@policy) + end + end + + def enabled? + return false if !defined?(@policy.enabled) + @policy.enabled + end + + def exists? + !@policy.nil? + end + + def to_s + "Alert Policy #{@display_name}" + end + end +end diff --git a/libraries/google_project_alert_policy_condition.rb b/libraries/google_project_alert_policy_condition.rb new file mode 100644 index 000000000..94856e62b --- /dev/null +++ b/libraries/google_project_alert_policy_condition.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'gcp_backend' +require 'google/apis/monitoring_v3' + +module Inspec::Resources + class GoogleProjectAlertPolicyCondition < GcpResourceBase + name 'google_project_alert_policy_condition' + desc 'Verifies settings for a single GCP project alert policy condition by policy name and filter name' + + example " + describe google_project_alert_policy_condition(policy: 'projects/my-project/alertPolicies/9271751234503117449', filter 'project=\"my-project\"') do + it { should exist } + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @filter = opts[:filter] + @policy = opts[:policy] + catch_gcp_errors do + @policy_result = @gcp.gcp_client(Google::Apis::MonitoringV3::MonitoringService).get_project_alert_policy(@policy) + @condition = condition_for_filter(@filter) + end + end + + def exists? + !@condition.nil? + end + + def condition_for_filter(filter) + return nil if !defined?(@policy_result.conditions) + @policy_result.conditions.each do |condition| + next if !defined?(condition.condition_threshold.filter) + return condition if condition.condition_threshold.filter == filter + end + nil + end + + def condition_threshold_value + return false if !defined?(@condition.condition_threshold.threshold_value) + @condition.condition_threshold.threshold_value + end + + def aggregation_alignment_period + return false if !defined?(@condition.condition_threshold.aggregations[0].alignment_period) + @condition.condition_threshold.aggregations[0].alignment_period + end + + def aggregation_per_series_aligner + return false if !defined?(@condition.condition_threshold.aggregations[0].per_series_aligner) + @condition.condition_threshold.aggregations[0].per_series_aligner + end + + def aggregation_cross_series_reducer + return false if !defined?(@condition.condition_threshold.aggregations[0].cross_series_reducer) + @condition.condition_threshold.aggregations[0].cross_series_reducer + end + + def to_s + "Alert Policy Condition #{@policy} \"#{@filter}\"" + end + end +end diff --git a/libraries/google_project_metric.rb b/libraries/google_project_metric.rb index 22d70a197..af46038cd 100644 --- a/libraries/google_project_metric.rb +++ b/libraries/google_project_metric.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'gcp_backend' +require 'google/apis/logging_v2' module Inspec::Resources class GoogleProjectMetric < GcpResourceBase diff --git a/libraries/google_project_metrics.rb b/libraries/google_project_metrics.rb index 0ce2c5220..63690c445 100644 --- a/libraries/google_project_metrics.rb +++ b/libraries/google_project_metrics.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'gcp_backend' +require 'google/apis/logging_v2' module Inspec::Resources class GoogleProjectMetrics < GcpResourceBase @@ -22,6 +23,7 @@ def initialize(opts = {}) # FilterTable setup filter_table_config = FilterTable.create filter_table_config.add(:metric_names, field: :metric_name) + filter_table_config.add(:metric_types, field: :metric_type) filter_table_config.add(:metric_destinations, field: :metric_destination) filter_table_config.connect(self, :fetch_data) @@ -35,7 +37,8 @@ def fetch_data return [] if !@metrics || !@metrics.metrics @metrics.metrics.map do |metric| metric_rows+=[{ metric_name: metric.name, - metric_filter: metric.filter }] + metric_filter: metric.filter, + metric_type: metric.metric_descriptor.type }] end next_page = @metrics.next_page_token break unless next_page diff --git a/libraries/google_user.rb b/libraries/google_user.rb new file mode 100644 index 000000000..ad044fd24 --- /dev/null +++ b/libraries/google_user.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'gcp_backend' +require 'google/apis/admin_directory_v1' + +module Inspec::Resources + class GoogleUser < GcpResourceBase + name 'google_user' + desc 'Verifies settings for a GCP user' + + example " + describe google_user(user_key: '110491234567894702010') do + it { should exist } + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @display_name = opts[:user_key] + catch_gcp_errors do + @user = @gcp.gcp_admin_client.get_user(opts[:user_key]) + create_resource_methods(@user) + end + end + + def exists? + !@user.nil? + end + + def suspended? + return false if !defined?(@user.suspended) + @user.suspended + end + + def has_mfa_enabled? + return false if !defined?(@user.is_enrolled_in2_sv) + @user.is_enrolled_in2_sv + end + + def to_s + "User #{@display_name}" + end + end +end diff --git a/libraries/google_users.rb b/libraries/google_users.rb new file mode 100644 index 000000000..af3332101 --- /dev/null +++ b/libraries/google_users.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'gcp_backend' +require 'google/apis/admin_directory_v1' + +module Inspec::Resources + class GoogleUsers < GcpResourceBase + name 'google_users' + desc 'Verifies settings for GCP users in bulk' + + example " + describe google_users(customer: 'my_customer') do + it { should exist } + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @customer = opts[:customer] || 'my_customer' + @domain = opts[:domain] || '' + end + + # FilterTable setup + filter_table_config = FilterTable.create + filter_table_config.add(:user_ids, field: :user_id) + filter_table_config.add(:user_full_names, field: :user_full_name) + filter_table_config.add(:user_emails, field: :user_email) + filter_table_config.connect(self, :fetch_data) + + def fetch_data + user_rows = [] + next_page = nil + loop do + catch_gcp_errors do + @users = @gcp.gcp_admin_client.list_users(customer: @customer, domain: @domain, page_token: next_page) + end + return [] if !@users || !@users.users + @users.users.map do |user| + user_rows+=[{ user_id: user.id, + user_name: user.name.full_name, + user_email: user.primary_email }] + end + next_page = @users.next_page_token + break unless next_page + end + @table = user_rows + end + end +end