Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add dry-run VPC SC implementation #25

Merged
merged 6 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ekm-over-vpc-onboarding/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ This guide provides instructions of an automation for Cloud External Key Manager

**Note 2:** Your EKM provider should be placed/referenced in your VPC project. A Private IP address of the EKM or an IP address for the load balancer pointing to the EKM is required in your `terraform.tfvars` file. You will need to edit `modules/create_ekm_resources/network.tf` file for any forwarding-rule resources you would like to add

**Note 3:** When both `create_vpc_project` and `create_kms_project` flags are true, a [VPC Service Controls](https://cloud.google.com/security/vpc-service-controls?hl=en) structure will be created in a [dry-run mode](https://cloud.google.com/vpc-service-controls/docs/dry-run-mode) in order to add an extra layer of security. Note when using dry-run mode requests that violate the perimeter policy are not denied, only logged. You can manually change to [enforced mode](https://cloud.google.com/vpc-service-controls/docs/manage-dry-run-configurations#enforce-configuration) after you complete the setup. A VPC SC perimeter can take up to 30 minutes to propagate and take effect, so it is recommended to wait this time before changing to enforced.

## Deploy infrastructure

1. Rename `terraform.example.tfvars` to `terraform.tfvars`:
Expand All @@ -42,12 +44,16 @@ This guide provides instructions of an automation for Cloud External Key Manager

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| access\_context\_manager\_policy\_id | Access context manager access policy ID. Used only when enable\_vpc\_sc flag is true. If empty, a new access context manager access policy will be created. | `string` | `""` | no |
| access\_level\_members | Condition - An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}. | `list(string)` | `[]` | no |
| access\_level\_members\_name | Description of the AccessLevel and its use. Does not affect behavior. | `string` | `"ekm_vpc_sc_access_level_member"` | no |
| billing\_account | Billing Account for the customer | `string` | `""` | no |
| create\_kms\_project | If true, a project for KMS will be created automatically | `bool` | `true` | no |
| create\_vpc\_project | If true, a project for VPC will be created automatically | `bool` | `true` | no |
| crypto\_space\_path | External key provider crypto space path (ie. v0/longlived/a1-example) | `string` | `""` | no |
| ekm\_connection\_key\_path | Each Cloud EKM key version contains either a key URI or a key path. This is a unique identifier for the external key material that Cloud EKM uses when requesting cryptographic operations using the key. When key\_management\_mode is CLOUD\_KMS, this variable will be equals to crypto\_space\_path | `string` | n/a | yes |
| ekmconnection\_name | Name of the ekmconnection resource | `string` | `"ekmconnection"` | no |
| enable\_vpc\_sc | VPC Service Controls define a security perimeter around Google Cloud resources to constrain data within a VPC and mitigate data exfiltration risks. VPC SC structure will be created only when both create project flags (create\_kms\_project and create\_vpc\_project) are true. | `bool` | `true` | no |
| external\_key\_manager\_ip | Private IP address of the external key manager or ip address for the load balancer pointing to the external key manager | `string` | `"10.2.0.48"` | no |
| external\_key\_manager\_port | Port of the external key manager or port for the load balancer pointing to the external key manager | `string` | `"443"` | no |
| external\_provider\_hostname | Hostname for external key manager provider (ie. private-ekm.example.endpoints.cloud.goog) | `string` | n/a | yes |
Expand All @@ -60,6 +66,7 @@ This guide provides instructions of an automation for Cloud External Key Manager
| location | Location where resources will be created | `string` | `"us-central1"` | no |
| network\_name | Name of the Network resource | `string` | `"vpc-network-name"` | no |
| organization\_id | The ID of the existing GCP organization | `string` | n/a | yes |
| perimeter\_name | Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores. | `string` | `"ekm_perimeter"` | no |
| project\_creator\_member\_email | Email of the user that will be granted permissions to create resources under the projects | `string` | `""` | no |
| random\_project\_suffix | If true, a suffix of 4 random characters will be appended to project names. Only applies when create project flag is true. | `bool` | `false` | no |
| servicedirectory\_name | Service Directory resource name | `string` | `"ekm-service-directory"` | no |
Expand Down
25 changes: 15 additions & 10 deletions ekm-over-vpc-onboarding/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@
module "create_vpc_kms_project" {
source = "./modules/create_vpc_kms_project"

organization_id = var.organization_id
folder_id = var.folder_id
kms_project_name = var.kms_project_name
kms_project_id = var.kms_project_id
vpc_project_name = var.vpc_project_name
vpc_project_id = var.vpc_project_id
billing_account = var.billing_account
create_kms_project = var.create_kms_project
create_vpc_project = var.create_vpc_project
random_project_suffix = var.random_project_suffix
organization_id = var.organization_id
folder_id = var.folder_id
kms_project_name = var.kms_project_name
kms_project_id = var.kms_project_id
vpc_project_name = var.vpc_project_name
vpc_project_id = var.vpc_project_id
billing_account = var.billing_account
create_kms_project = var.create_kms_project
create_vpc_project = var.create_vpc_project
random_project_suffix = var.random_project_suffix
enable_vpc_sc = var.enable_vpc_sc
access_context_manager_policy_id = var.access_context_manager_policy_id
access_level_members_name = var.access_level_members_name
access_level_members = var.access_level_members
perimeter_name = var.perimeter_name
}

module "ekm_resources" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ This module provides the project infrastructure setup (creation and/or API servi

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| access\_context\_manager\_policy\_id | Access context manager access policy ID. Used only when enable\_vpc\_sc flag is true. If empty, a new access context manager access policy will be created. | `string` | `""` | no |
| access\_level\_members | Condition - An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}. | `list(string)` | `[]` | no |
| access\_level\_members\_name | Description of the AccessLevel and its use. Does not affect behavior. | `string` | `"ekm_vpc_sc_access_level_member"` | no |
| billing\_account | Billing Account for the customer | `string` | `""` | no |
| create\_kms\_project | If true, a project for KMS will be created automatically | `bool` | `true` | no |
| create\_vpc\_project | If true, a project for VPC will be created automatically | `bool` | `true` | no |
| enable\_vpc\_sc | VPC Service Controls define a security perimeter around Google Cloud resources to constrain data within a VPC and mitigate data exfiltration risks. | `bool` | n/a | yes |
| folder\_id | (Optional) The ID of the GCP folder to create the projects | `string` | `""` | no |
| kms\_project\_id | ID of the KMS project you would like to create | `string` | `""` | no |
| kms\_project\_name | Name of the KMS project you would like to create | `string` | n/a | yes |
| organization\_id | The ID of the existing GCP organization | `string` | n/a | yes |
| perimeter\_name | Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores. | `string` | `"ekm_perimeter"` | no |
| random\_project\_suffix | If true, a suffix of 4 random characters will be appended to project names. Only applies when create project flag is true. | `bool` | `false` | no |
| vpc\_project\_id | ID of the VPC project, default to same as KMS | `string` | `""` | no |
| vpc\_project\_name | Name of the VPC project, default to same as KMS | `string` | `""` | no |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* 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.
*/

locals {
create_vpc_sc_resources = var.enable_vpc_sc && var.create_kms_project && var.create_vpc_project
access_context_manager_policy_id = var.access_context_manager_policy_id != "" ? var.access_context_manager_policy_id : module.access_context_manager_policy.policy_id
restricted_services = distinct(concat(local.apis_to_activate_in_kms_project, local.apis_to_activate_in_vpc_project))
}

module "access_context_manager_policy" {
count = local.create_vpc_sc_resources && var.access_context_manager_policy_id == "" ? 1 : 0

source = "terraform-google-modules/vpc-service-controls/google"
version = "~> 6.0"

parent_id = var.organization_id
policy_name = "default policy"
}

module "access_level_members" {
count = local.create_vpc_sc_resources ? 1 : 0
source = "terraform-google-modules/vpc-service-controls/google//modules/access_level"
version = "~> 6.0"

policy = local.access_context_manager_policy_id
name = var.access_level_members_name
members = var.access_level_members
}

module "regular_service_perimeter_dry_run" {
count = local.create_vpc_sc_resources ? 1 : 0
source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter"
version = "~> 6.0"

policy = var.access_context_manager_policy_id
perimeter_name = var.perimeter_name
description = "EKM Perimeter shielding projects"
access_levels_dry_run = [module.access_level_members[0].name]
restricted_services_dry_run = local.restricted_services
resources_dry_run = [module.vpc_project[0].project_number, module.kms_project[0].project_number]

shared_resources = {
all = [module.vpc_project[0].project_number, module.kms_project[0].project_number]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,32 @@ variable "random_project_suffix" {
default = false
description = "If true, a suffix of 4 random characters will be appended to project names. Only applies when create project flag is true."
}

variable "access_context_manager_policy_id" {
type = string
default = ""
description = "Access context manager access policy ID. Used only when enable_vpc_sc flag is true. If empty, a new access context manager access policy will be created."
}

variable "enable_vpc_sc" {
type = bool
description = "VPC Service Controls define a security perimeter around Google Cloud resources to constrain data within a VPC and mitigate data exfiltration risks."
}

variable "access_level_members_name" {
type = string
default = "ekm_vpc_sc_access_level_member"
description = "Description of the AccessLevel and its use. Does not affect behavior."
}

variable "access_level_members" {
type = list(string)
default = []
description = "Condition - An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}."
}

variable "perimeter_name" {
type = string
default = "ekm_perimeter"
description = "Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores."
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
module "vpc-network" {
source = "terraform-google-modules/network/google"
version = "~> 9.0"
project_id = data.google_project.vpc_project.number
project_id = var.vpc_project_id
network_name = var.network_name
mtu = 1460
routing_mode = "REGIONAL"
Expand All @@ -37,7 +37,7 @@ module "firewall_rules" {
source = "terraform-google-modules/network/google//modules/firewall-rules"
version = "9.1.0"

project_id = data.google_project.vpc_project.number
project_id = var.vpc_project_id
network_name = module.vpc-network.network_name

rules = [{
Expand Down
2 changes: 2 additions & 0 deletions ekm-over-vpc-onboarding/terraform.example.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ external_provider_hostname = "REPLACE-WITH-YOUR-EKM-HOSTNAME"
external_provider_raw_der = "REPLACE-WITH-YOUR-RAW-DER" # The raw certificate bytes in DER format. A base64-encoded string. For more info see: https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.ekmConnections#Certificate
external_key_manager_ip = "REPLACE-WITH-YOUR-EKM-IP" # Set with a Private IP address of the EKM or an IP address for the load balancer pointing to the EKM
ekm_connection_key_path = "REPLACE-WITH-YOUR-KEY-PATH" # Set with Cloud EKM key version.

access_level_members = ["user:REPLACE-WITH-YOUR-USER-EMAIL"] # Set to add your user to the VPC SC perimeter. This will be used only when both create project flags are true.access_context_manager_policy_id = "value"
30 changes: 30 additions & 0 deletions ekm-over-vpc-onboarding/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,33 @@ variable "ekm_connection_key_path" {
type = string
description = "Each Cloud EKM key version contains either a key URI or a key path. This is a unique identifier for the external key material that Cloud EKM uses when requesting cryptographic operations using the key. When key_management_mode is CLOUD_KMS, this variable will be equals to crypto_space_path"
}

variable "access_context_manager_policy_id" {
type = string
default = ""
description = "Access context manager access policy ID. Used only when enable_vpc_sc flag is true. If empty, a new access context manager access policy will be created."
}

variable "enable_vpc_sc" {
type = bool
default = true
description = "VPC Service Controls define a security perimeter around Google Cloud resources to constrain data within a VPC and mitigate data exfiltration risks. VPC SC structure will be created only when both create project flags (create_kms_project and create_vpc_project) are true."
}

variable "access_level_members_name" {
type = string
default = "ekm_vpc_sc_access_level_member"
description = "Description of the AccessLevel and its use. Does not affect behavior."
}

variable "access_level_members" {
type = list(string)
default = []
description = "Condition - An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid}."
}

variable "perimeter_name" {
type = string
default = "ekm_perimeter"
description = "Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores."
}