Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Custom organization policy library additional controls (GoogleCloudPl…
…atform#1598)

* feat: add monitoring alert labels (#63)

* feat: remove constraint and update fast factory constraint

* feat: update tf format to match fast module

* feat: update sha

* fix: format python files

* feat: add parameterization and update alerts to cloud foundation fabric factory format

* Update various custom organization policies and tests

* fix: cloudsql

* fix: update doc

* fix: add firewall rule policy

* feat: update various constraint policies

---------

Co-authored-by: Andrew Gold <41129777+agold-rh@users.noreply.github.com>
  • Loading branch information
vannicktrinquier and agold-rh authored Oct 20, 2025
commit 8152a26dc086cfbae0e6fbece95ca33526dd8612
1 change: 1 addition & 0 deletions tools/custom-organization-policy-library/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.terraform.tfstate*
*.out
*.bak
*.env
*/**/terraform.tfstate*
terraform/
tests/venv/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#! Copyright 2024 Google LLC
#!
#! Licensed under the Apache License, Version 2.0 (the "License");
#! you may not use this file except in compliance with the License.
#! You may obtain a copy of the License at
#!
#! http://www.apache.org/licenses/LICENSE-2.0
#!
#! Unless required by applicable law or agreed to in writing, software
#! distributed under the License is distributed on an "AS IS" BASIS,
#! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#! See the License for the specific language governing permissions and
#! limitations under the License.

#@ load("@ytt:overlay", "overlay")
#@data/values-schema
---
#@overlay/match missing_ok=True
accesscontextmanager:
accesscontextmanagerDisableBridgePerimeters:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: false
cis: false
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ cloudsql:
bundles:
pci-dss: false
cis: false
cloudsqlRequirePostgreSQLDatabaseAdditionalFlags:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: true
cis: true
cloudsqlRequirePostgreSQLDatabaseFlags:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ firewall:
params:
#@schema/validation min_len=1
name_regex: ""
firewallEnforcePolicyRuleLogging:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: false
cis: false
firewallEnforceRuleLogging:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: false
cis: false
firewallRequireDescription:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,6 @@
---
#@overlay/match missing_ok=True
gke:
gkeAllowedInitialClusterVersions:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: true
cis: false
params:
#@schema/validation min_len=1
initial_cluster_versions:
- ""
gkeAllowedNodePoolImages:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ iam:
bundles:
pci-dss: false
cis: true
params:
exceptions:
- ""
iamDisableBasicRoles:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
Expand All @@ -51,9 +54,21 @@ iam:
bundles:
pci-dss: true
cis: true
params:
exceptions:
- ""
iamDisablePublicBindings:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: true
cis: true
cis: true
iamDisableRedisAdminRoles:
#@schema/validation one_of=["default", "skip", "include", "skip-policy"]
generation: "default"
bundles:
pci-dss: true
cis: true
params:
exceptions:
- ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("accesscontextmanagerDisableBridgePerimeters")

#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resourceTypes:
- accesscontextmanager.googleapis.com/ServicePerimeter
methodTypes:
- CREATE
- UPDATE
condition: "resource.perimeterType == 'PERIMETER_TYPE_BRIDGE'"
actionType: DENY
displayName: Deny usage of perimeter bridges
description: Ensure no perimeter bridges are used. Instead, use ingress and egress rules.
#@ end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("cloudsqlRequirePostgreSQLDatabaseAdditionalFlags")

#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resourceTypes:
- sqladmin.googleapis.com/Instance
methodTypes:
- CREATE
- UPDATE
condition: >-
resource.databaseVersion.startsWith('POSTGRES') && (
!resource.settings.databaseFlags.exists(flag, flag.name == 'log_checkpoints' && flag.value == 'on') ||
!resource.settings.databaseFlags.exists(flag, flag.name == 'log_executor_stats' && flag.value == 'off') ||
!resource.settings.databaseFlags.exists(flag, flag.name == 'log_lock_waits' && flag.value == 'on')
)
actionType: DENY
display_name: Require Cloud SQL for PostgreSQL instance database flags to be configured correctly (e.g log_checkpoints, log_executor_stats, log_lock_waits)
description: Ensure Cloud SQL for PostgreSQL instance database flags are set correctly (e.g log_checkpoints, log_executor_stats, log_lock_waits)
#@ end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#@ constraint = build_constraint("computeAllowedInstanceLabels")

#@ def condition(labels):
#@ return "resource.labels.all(label, (label in " + str(labels) + ")) == false"
#@ return "resource.labels.all(label, (label in " + str(labels) + ") || label.startsWith('goog-')) == false"
#@ end

#@ if constraint.to_generate():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@
#@ constraint = build_constraint("firewallEnforceNamingConvention")

#@ def condition(name_regex):
#@ return 'resource.name.matches("' + str(name_regex) + '") == false'
#@ lines = [
#@ '(',
#@ ' resource.name.matches("' + str(name_regex) + '") == false &&',
#@ ' !resource.name.startsWith("gke-") &&',
#@ ' !resource.name.startsWith("k8s-") &&',
#@ ' !resource.name.endsWith("-hc") &&',
#@ ' !resource.name.startsWith("k8s2-") &&',
#@ ' !resource.name.startsWith("gkegw1-l7-") &&',
#@ ' !resource.name.startsWith("gkemcg1-l7-")',
#@ ')'
#@ ]
#@ return "\n".join(lines)
#@ end

#@ if constraint.to_generate():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("firewallEnforcePolicyRuleLogging")

#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resource_types:
- compute.googleapis.com/FirewallPolicy
condition: resource.rules.exists(rule, rule.action != 'goto_next' && rule.enableLogging == false)
action_type: DENY
method_types:
- CREATE
- UPDATE
display_name: Require Firewall Policy rules to have logging enabled
description: Ensure that Firewall Policy rules have logging enabled
#@ end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("firewallEnforceRuleLogging")

#@ def condition():
#@ lines = [
#@ '(',
#@ ' (has(resource.logConfig) == false || resource.logConfig.enable == false) &&',
#@ ' !resource.name.startsWith("gke-") &&',
#@ ' !resource.name.startsWith("k8s-") &&',
#@ ' !resource.name.endsWith("-hc") &&',
#@ ' !resource.name.startsWith("k8s2-") &&',
#@ ' !resource.name.startsWith("gkegw1-l7-") &&',
#@ ' !resource.name.startsWith("gkemcg1-l7-")',
#@ ')'
#@ ]
#@ return "\n".join(lines)
#@ end


#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resource_types:
- compute.googleapis.com/Firewall
condition: #@ condition()
action_type: DENY
method_types:
- CREATE
- UPDATE
display_name: Require VPC Firewall rules to have logging enabled
description: Ensure that VPC Firewall rules have logging enabled
#@ end
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("firewallRequireDescription")

#@ def condition():
#@ lines = [
#@ '(',
#@ ' resource.description == "" &&',
#@ ' !resource.name.startsWith("gke-") &&',
#@ ' !resource.name.startsWith("k8s-") &&',
#@ ' !resource.name.endsWith("-hc") &&',
#@ ' !resource.name.startsWith("k8s2-") &&',
#@ ' !resource.name.startsWith("gkegw1-l7-") &&',
#@ ' !resource.name.startsWith("gkemcg1-l7-")',
#@ ')'
#@ ]
#@ return "\n".join(lines)
#@ end

#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resource_types:
- compute.googleapis.com/Firewall
condition: resource.description == ""
condition: #@ condition()
action_type: DENY
method_types:
- CREATE
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("iamAllowedMembers")

#@ def allowed_domains(domains):
#@ quoted_domains = ["'%s'" % d for d in domains]
#@ joined_domains = ", ".join(quoted_domains)
#@
#@ return "!MemberSubjectEndsWith(member, [%s])" % joined_domains
#@ end

#@ def condition(domains):
#@ return "resource.bindings.exists(binding, binding.members.exists(member, !MemberSubjectEndsWith(member, " + str(domains) + ")))"
#@ lines = [
#@ "resource.bindings.exists(binding,",
#@ " binding.members.exists(member,",
#@ " {domains}",
#@ " )",
#@ ")"
#@ ]
#@
#@ full_string = "\n".join(lines)
#@ return full_string.format(domains=allowed_domains(domains))
#@ end


#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resourceTypes:
- iam.googleapis.com/AllowPolicy
methodTypes:
- CREATE
- UPDATE
condition: #@ condition(constraint.params().domains)
condition: #@ condition(constraint.params().domains)
actionType: DENY
displayName: Deny principals and members outside the organization domain
description: Ensure no binding are done with members outside the organization domain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
#@ load("/constraints.lib.star", "build_constraint")
#@ constraint = build_constraint("iamDisableAdminServiceAccount")

#@ def exceptions(domains):
#@ quoted_domains = ["'%s'" % d for d in domains]
#@ joined_domains = ", ".join(quoted_domains)
#@ return "!MemberSubjectMatches(member, [%s])" % joined_domains
#@ end

#@ def condition(principal_exceptions):
#@ lines = [
#@ "resource.bindings.exists(binding,",
#@ " binding.members.exists(member,",
#@ " {exceptions} &&",
#@ " !MemberSubjectEndsWith(member, ['@cloudservices.gserviceaccount.com']) &&",
#@ " MemberSubjectStartsWith(member, ['serviceAccount:'])",
#@ " ) &&",
#@ " (",
#@ " RoleNameMatches(binding.role, ['roles/editor']) ||",
#@ " RoleNameMatches(binding.role, ['roles/owner']) ||",
#@ " RoleNameContains(binding.role, ['admin', 'Admin'])",
#@ " )",
#@ ")"
#@ ]
#@
#@ full_string = "\n".join(lines)
#@ return full_string.format(exceptions=exceptions(principal_exceptions))
#@ end


#@ if constraint.to_generate():
name: #@ constraint.constraint_name()
resourceTypes:
- iam.googleapis.com/AllowPolicy
methodTypes:
- CREATE
- UPDATE
condition: >-
resource.bindings.exists(binding,
binding.members.exists(member, !MemberSubjectEndsWith(member, ['@cloudservices.gserviceaccount.com']) &&
MemberSubjectStartsWith(member, ['serviceAccount:'])) &&
(
RoleNameMatches(binding.role, ["roles/editor"]) ||
RoleNameMatches(binding.role, ["roles/owner"]) ||
RoleNameContains(binding.role, ['admin', "Admin"])
)
)
condition: #@ condition(constraint.params().exceptions)
actionType: DENY
displayName: Deny use of the basic roles and usage of admin role for sevice account
description: Ensure no use of the basic roles (viewer, editor and owner) and usage of admin role for sevice account
Expand Down
Loading