From ecc15923e075090aa1d0855da0df2dfddd36cfce Mon Sep 17 00:00:00 2001 From: Leonardo Romanini <97033241+romanini-ciandt@users.noreply.github.com> Date: Wed, 3 Apr 2024 18:53:51 -0300 Subject: [PATCH] feat: Add sharing data with partner automation (#3) --- .gitignore | 3 + build/int.cloudbuild.yaml | 12 +++ .../main.tf | 25 +++++ .../variables.tf | 20 ++++ share-encrypted-data-with-partners/README.md | 21 +++++ .../consumer/0-bootstrap/README.md | 63 +++++++++++++ .../consumer/0-bootstrap/main.tf | 93 +++++++++++++++++++ .../consumer/0-bootstrap/null_resources.tf | 39 ++++++++ .../consumer/0-bootstrap/outputs.tf | 40 ++++++++ .../0-bootstrap/terraform.example.tfvars | 20 ++++ .../consumer/0-bootstrap/variables.tf | 65 +++++++++++++ .../consumer/0-bootstrap/versions.tf | 41 ++++++++ .../consumer/1-key-import/README.md | 46 +++++++++ .../consumer/1-key-import/main.tf | 40 ++++++++ .../1-key-import/terraform.example.tfvars | 17 ++++ .../consumer/1-key-import/variables.tf | 26 ++++++ .../consumer/1-key-import/versions.tf | 27 ++++++ .../examples/python/README.md | 52 +++++++++++ .../examples/python/decrypt.py | 67 +++++++++++++ .../examples/python/encrypt.py | 53 +++++++++++ .../examples/python/requirements.txt | 2 + .../producer/README.md | 64 +++++++++++++ .../producer/main.tf | 23 +++++ .../producer/terraform.example.tfvars | 19 ++++ .../producer/variables.tf | 31 +++++++ .../producer/versions.tf | 27 ++++++ 26 files changed, 936 insertions(+) create mode 100644 examples/share_encrypted_data_with_partners_bootstrap/main.tf create mode 100644 examples/share_encrypted_data_with_partners_bootstrap/variables.tf create mode 100644 share-encrypted-data-with-partners/README.md create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/README.md create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/main.tf create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/null_resources.tf create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/outputs.tf create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/terraform.example.tfvars create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/variables.tf create mode 100644 share-encrypted-data-with-partners/consumer/0-bootstrap/versions.tf create mode 100644 share-encrypted-data-with-partners/consumer/1-key-import/README.md create mode 100644 share-encrypted-data-with-partners/consumer/1-key-import/main.tf create mode 100644 share-encrypted-data-with-partners/consumer/1-key-import/terraform.example.tfvars create mode 100644 share-encrypted-data-with-partners/consumer/1-key-import/variables.tf create mode 100644 share-encrypted-data-with-partners/consumer/1-key-import/versions.tf create mode 100644 share-encrypted-data-with-partners/examples/python/README.md create mode 100644 share-encrypted-data-with-partners/examples/python/decrypt.py create mode 100644 share-encrypted-data-with-partners/examples/python/encrypt.py create mode 100644 share-encrypted-data-with-partners/examples/python/requirements.txt create mode 100644 share-encrypted-data-with-partners/producer/README.md create mode 100644 share-encrypted-data-with-partners/producer/main.tf create mode 100644 share-encrypted-data-with-partners/producer/terraform.example.tfvars create mode 100644 share-encrypted-data-with-partners/producer/variables.tf create mode 100644 share-encrypted-data-with-partners/producer/versions.tf diff --git a/.gitignore b/.gitignore index 543ad6af..25536bf5 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,9 @@ crash.log # version control. **/*.tfvars +# Add an exception for *.example.tfvars (These should be included in git) +!*.example.tfvars + credentials.json # tf lock file diff --git a/build/int.cloudbuild.yaml b/build/int.cloudbuild.yaml index e734d840..dd098fdb 100644 --- a/build/int.cloudbuild.yaml +++ b/build/int.cloudbuild.yaml @@ -21,6 +21,18 @@ steps: - 'TF_VAR_org_id=$_ORG_ID' - 'TF_VAR_folder_id=$_FOLDER_ID' - 'TF_VAR_billing_account=$_BILLING_ACCOUNT' +- id: create + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run all --stage init --verbose'] +- id: converge + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run all --stage apply --verbose'] +- id: verify + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run all --stage verify --verbose'] +- id: destroy + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run all --stage teardown --verbose'] tags: - 'ci' - 'integration' diff --git a/examples/share_encrypted_data_with_partners_bootstrap/main.tf b/examples/share_encrypted_data_with_partners_bootstrap/main.tf new file mode 100644 index 00000000..a2999af2 --- /dev/null +++ b/examples/share_encrypted_data_with_partners_bootstrap/main.tf @@ -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. + */ + +module "bootstrap" { + source = "../../share-encrypted-data-with-partners/consumer/0-bootstrap" + + project_id = var.project_id + keyring = "simple-example-keyring" + key = "simple-example-key" + import_job_public_key_path = "./wrapping-key.pem" + prevent_destroy = false +} diff --git a/examples/share_encrypted_data_with_partners_bootstrap/variables.tf b/examples/share_encrypted_data_with_partners_bootstrap/variables.tf new file mode 100644 index 00000000..1b8c53a6 --- /dev/null +++ b/examples/share_encrypted_data_with_partners_bootstrap/variables.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +variable "project_id" { + description = "GCP project ID to use for the creation of resources." + type = string +} diff --git a/share-encrypted-data-with-partners/README.md b/share-encrypted-data-with-partners/README.md new file mode 100644 index 00000000..a1cb065e --- /dev/null +++ b/share-encrypted-data-with-partners/README.md @@ -0,0 +1,21 @@ +# Raw Encryption: Building infrastructure for sharing encrypted data with partners + +## Overview + +This guide provides patterns that can be used to continuously share sensitive data in encrypted form from external sources (on-prem or other clouds) with partners using the raw encryption feature in Cloud KMS with Terraform. + +## Context + +This automation is based in two personas: **producer** and **consumer**. + +**Producer** is the entity that will generate the DEK (Data Encryption Key) and wants to send the encrypted data, while the **consumer** is the entity that will create the required GCP resources in order to receive the wrapped DEK together with the data to be decrypted. + +## Expected workflow + +1. Consumer [0-bootstrap terraform module](./consumer/0-bootstrap/README.md) execution; +1. Consumer sends the Import Job public key generated to Producer; +1. Producer [terraform module](./producer/README.md) execution; +1. Producer [encrypt process](./examples/python/README.md) execution; +1. Producer sends the wrapped DEK and the encrypted data to Consumer; +1. Consumer [1-key-import terraform module](./consumer/0-bootstrap/README.md) execution; +1. Consumer [decrypt process](./examples/python/README.md) execution; diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/README.md b/share-encrypted-data-with-partners/consumer/0-bootstrap/README.md new file mode 100644 index 00000000..c25b0471 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/README.md @@ -0,0 +1,63 @@ +# [Consumer 0-bootstrap module] Raw Encryption: Building infrastructure for sharing encrypted data with partners + +## Overview + +This module provides the Terraform bootstrap infrastructure creation (keyring, key and import job) needed to exchange encrypted data with partners through Cloud KMS raw encryption. + +## Prerequisites + +- [Terraform](https://developer.hashicorp.com/terraform/downloads); +- [Google Cloud CLI (`gcloud`)](https://cloud.google.com/sdk/docs/install-sdk); + - You must be authenticated in your GCP account. If you're not you should run `gcloud auth login`; +- An existing [GCP project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project); +- Enable GCP services in the project created above: + - cloudkms.googleapis.com + +**Note:** You can enable these services using `gcloud services enable ` command or terraform automation would auto-enable them for you. + +## Deploy infrastructure + +1. Rename `terraform.example.tfvars` to `terraform.tfvars`: + ```sh + mv terraform.example.tfvars terraform.tfvars + ``` + +1. Update `terraform.tfvars` file with the required values. + +1. Create the infrastructure. + + ```sh + terraform init + terraform plan + terraform apply + ``` + **Note:** Copy all the outputs provided by `terraform apply`. They will be required during the encrypt/decrypt process. + +1. All the bootstrap infrastructure required to receive encrypted data (wrapped DEK and ciphertext) from the sender using raw encryption is deployed. You can now send the Import Job public key file (stored in `import_job_public_key_path`) to the sender so that they can go through the DEK (Data Encryption Key) wrapping process. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| crypto\_key\_algorithm\_template | Algorithm to use when creating a key template. See more: https://cloud.google.com/kms/docs/reference/rest/v1/CryptoKeyVersionAlgorithm. | `string` | `"AES_256_GCM"` | no | +| import\_job\_method | Wrapping method to be used for incoming key material. See more: https://cloud.google.com/kms/docs/key-wrapping. | `string` | `"rsa-oaep-4096-sha256"` | no | +| import\_job\_public\_key\_path | Path to import job public key that will be auto-generated. The DEK is encrypted (also known as wrapped) by a key encryption key (KEK) provided by import job. | `string` | n/a | yes | +| key | Name of the key to be created. | `string` | n/a | yes | +| keyring | Name of the keyring to be created. | `string` | n/a | yes | +| location | Location for the keyring. For available KMS locations see: https://cloud.google.com/kms/docs/locations. | `string` | `"us-central1"` | no | +| prevent\_destroy | Set the prevent\_destroy lifecycle attribute on keys. | `bool` | `true` | no | +| project\_id | GCP project ID to use for the creation of resources. | `string` | n/a | yes | +| suffix | A suffix to be used as an identifier for resources. (e.g., suffix for KMS Key, Keyring, SAs, etc.). If not provided, a 4 character random one will be generated. | `string` | `""` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| import\_job\_id | ID of the Import Job created. | +| key | Name of the key created. | +| keyring | Name of the keyring. | +| location | Location of the keyring created. | +| project\_id | ID of the GCP project being used. | + + diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/main.tf b/share-encrypted-data-with-partners/consumer/0-bootstrap/main.tf new file mode 100644 index 00000000..c5223bcf --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/main.tf @@ -0,0 +1,93 @@ +/** + * 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 { + default_suffix = var.suffix != "" ? var.suffix : random_string.suffix.result + apis_to_enable = [ + "cloudkms.googleapis.com", + "cloudresourcemanager.googleapis.com" + ] +} + +resource "google_project_service" "apis_to_enable" { + for_each = toset(local.apis_to_enable) + + project = var.project_id + service = each.key + disable_on_destroy = false +} + +resource "time_sleep" "enable_projects_apis_sleep" { + create_duration = "30s" + + depends_on = [google_project_service.apis_to_enable] +} + +resource "random_string" "suffix" { + length = 4 + special = false + upper = false +} + +resource "google_kms_key_ring" "keyring" { + name = "${var.keyring}-${local.default_suffix}" + location = var.location + project = var.project_id + + depends_on = [time_sleep.enable_projects_apis_sleep] +} + +resource "google_kms_crypto_key" "dek-prevent-destroy-true" { + count = var.prevent_destroy ? 1 : 0 + + name = "${var.key}-${local.default_suffix}" + key_ring = google_kms_key_ring.keyring.id + purpose = "RAW_ENCRYPT_DECRYPT" + import_only = true + skip_initial_version_creation = true + + version_template { + algorithm = var.crypto_key_algorithm_template + protection_level = "HSM" + } + + lifecycle { + prevent_destroy = true + } + + depends_on = [time_sleep.enable_projects_apis_sleep] +} + +resource "google_kms_crypto_key" "dek-prevent-destroy-false" { + count = var.prevent_destroy ? 0 : 1 + + name = "${var.key}-${local.default_suffix}" + key_ring = google_kms_key_ring.keyring.id + purpose = "RAW_ENCRYPT_DECRYPT" + import_only = true + skip_initial_version_creation = true + + version_template { + algorithm = var.crypto_key_algorithm_template + protection_level = "HSM" + } + + lifecycle { + prevent_destroy = false + } + + depends_on = [time_sleep.enable_projects_apis_sleep] +} diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/null_resources.tf b/share-encrypted-data-with-partners/consumer/0-bootstrap/null_resources.tf new file mode 100644 index 00000000..e55dfb34 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/null_resources.tf @@ -0,0 +1,39 @@ +/** + * 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 { + import_job_id = "import-job-${local.default_suffix}" +} + +// Create the import job into cloud KMS +resource "null_resource" "gcloud-import-job-creation" { + + provisioner "local-exec" { + command = "gcloud kms import-jobs create ${local.import_job_id} --location ${var.location} --keyring ${google_kms_key_ring.keyring.name} --import-method ${var.import_job_method} --protection-level hsm --project ${var.project_id}" + } + + depends_on = [google_kms_key_ring.keyring] +} + +// Retrieve the wrapping (public) key of the import job from cloud KMS +resource "null_resource" "extract-pem-from-import-job" { + + provisioner "local-exec" { + command = "gcloud kms import-jobs describe ${local.import_job_id} --project=${var.project_id} --location=${var.location} --keyring=${google_kms_key_ring.keyring.name} --format=\"value(publicKey.pem)\" > ${var.import_job_public_key_path}" + } + + depends_on = [null_resource.gcloud-import-job-creation] +} diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/outputs.tf b/share-encrypted-data-with-partners/consumer/0-bootstrap/outputs.tf new file mode 100644 index 00000000..8cd1fe09 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/outputs.tf @@ -0,0 +1,40 @@ +/** + * 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. + */ + +output "keyring" { + description = "Name of the keyring." + value = google_kms_key_ring.keyring.name +} + +output "location" { + description = "Location of the keyring created." + value = google_kms_key_ring.keyring.location +} + +output "key" { + description = "Name of the key created." + value = "${var.key}-${local.default_suffix}" +} + +output "project_id" { + description = "ID of the GCP project being used." + value = google_kms_key_ring.keyring.project +} + +output "import_job_id" { + description = "ID of the Import Job created." + value = local.import_job_id +} diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/terraform.example.tfvars b/share-encrypted-data-with-partners/consumer/0-bootstrap/terraform.example.tfvars new file mode 100644 index 00000000..01d405c5 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/terraform.example.tfvars @@ -0,0 +1,20 @@ +/** + * 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. + */ + +project_id = "REPLACE-WITH-YOUR-PROJECT-ID" +keyring = "example-keyring-terraform" +key = "example-key-terraform" +import_job_public_key_path = "./wrapping-key.pem" diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/variables.tf b/share-encrypted-data-with-partners/consumer/0-bootstrap/variables.tf new file mode 100644 index 00000000..d6aff6e8 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/variables.tf @@ -0,0 +1,65 @@ +/** + * 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. + */ + +variable "suffix" { + description = "A suffix to be used as an identifier for resources. (e.g., suffix for KMS Key, Keyring, SAs, etc.). If not provided, a 4 character random one will be generated." + type = string + default = "" +} + +variable "project_id" { + description = "GCP project ID to use for the creation of resources." + type = string +} + +variable "location" { + description = "Location for the keyring. For available KMS locations see: https://cloud.google.com/kms/docs/locations." + type = string + default = "us-central1" +} + +variable "keyring" { + description = "Name of the keyring to be created." + type = string +} + +variable "key" { + description = "Name of the key to be created." + type = string +} + +variable "import_job_public_key_path" { + description = "Path to import job public key that will be auto-generated. The DEK is encrypted (also known as wrapped) by a key encryption key (KEK) provided by import job." + type = string +} + +variable "prevent_destroy" { + description = "Set the prevent_destroy lifecycle attribute on keys." + type = bool + default = true +} + +variable "crypto_key_algorithm_template" { + description = "Algorithm to use when creating a key template. See more: https://cloud.google.com/kms/docs/reference/rest/v1/CryptoKeyVersionAlgorithm." + type = string + default = "AES_256_GCM" +} + +variable "import_job_method" { + description = "Wrapping method to be used for incoming key material. See more: https://cloud.google.com/kms/docs/key-wrapping." + type = string + default = "rsa-oaep-4096-sha256" +} diff --git a/share-encrypted-data-with-partners/consumer/0-bootstrap/versions.tf b/share-encrypted-data-with-partners/consumer/0-bootstrap/versions.tf new file mode 100644 index 00000000..46393644 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/0-bootstrap/versions.tf @@ -0,0 +1,41 @@ +/** + * 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. + */ + +terraform { + required_version = ">= 1.5.7" + required_providers { + + google = { + source = "hashicorp/google" + version = ">= 5.11, < 6" + } + + random = { + source = "hashicorp/random" + version = "3.6.0" + } + + null = { + source = "hashicorp/null" + version = "3.2.2" + } + + time = { + source = "hashicorp/time" + version = "0.10.0" + } + } +} diff --git a/share-encrypted-data-with-partners/consumer/1-key-import/README.md b/share-encrypted-data-with-partners/consumer/1-key-import/README.md new file mode 100644 index 00000000..bd9e2b7d --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/1-key-import/README.md @@ -0,0 +1,46 @@ +# [Consumer 1-key-import module] Raw Encryption: Building infrastructure for sharing encrypted data with partners + +## Overview + +This module provides the key import process for an existing import job and raw encryption key, using Cloud KMS with Terraform. + +## Prerequisites + +- [Terraform](https://developer.hashicorp.com/terraform/downloads); +- [Google Cloud CLI (`gcloud`)](https://cloud.google.com/sdk/docs/install-sdk); + - You must be authenticated in your GCP account. If you're not you should run `gcloud auth login`; +- [OpenSSL](https://www.openssl.org/source/index.html); +- Wrapped Data Encryption Key (DEK) file; + +## Deploy infrastructure + +1. Rename `terraform.example.tfvars` to `terraform.tfvars`: + ```sh + mv terraform.example.tfvars terraform.tfvars + ``` + +1. Update `terraform.tfvars` file with the required values. + +1. Create the infrastructure. + + ```sh + terraform init + terraform plan + terraform apply + ``` + +1. The DEK (Data Encryption Key) is now imported in GCP and ready to be used. You can go to [examples folder](../examples/) and choose one of the available decrypt approaches in order to test the decryption process. Wrapped key file, ciphertext, IV, and AAD will be required. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| crypto\_key\_algorithm\_import | Algorithm to use when creating a crypto key version through import. See more: https://cloud.google.com/sdk/gcloud/reference/kms/keys/versions/import. | `string` | `"aes-256-gcm"` | no | +| wrapped\_key\_path | Path to the wrapped key file. | `string` | n/a | yes | + +## Outputs + +No outputs. + + diff --git a/share-encrypted-data-with-partners/consumer/1-key-import/main.tf b/share-encrypted-data-with-partners/consumer/1-key-import/main.tf new file mode 100644 index 00000000..715a080f --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/1-key-import/main.tf @@ -0,0 +1,40 @@ +/** + * 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. + */ + +data "terraform_remote_state" "bootstrap" { + backend = "local" + + config = { + path = "../0-bootstrap/terraform.tfstate" + } +} + +locals { + project_id = data.terraform_remote_state.bootstrap.outputs.project_id + import_job_id = data.terraform_remote_state.bootstrap.outputs.import_job_id + keyring = data.terraform_remote_state.bootstrap.outputs.keyring + key = data.terraform_remote_state.bootstrap.outputs.key + location = data.terraform_remote_state.bootstrap.outputs.location +} + + +// Import wrapped key into the existing import job in Cloud KMS +resource "null_resource" "gcloud-import-wrapped-key-into-an-existing-job" { + + provisioner "local-exec" { + command = "gcloud kms keys versions import --import-job ${local.import_job_id} --location ${local.location} --keyring ${local.keyring} --key ${local.key} --algorithm ${var.crypto_key_algorithm_import} --wrapped-key-file ${var.wrapped_key_path} --project ${local.project_id}" + } +} diff --git a/share-encrypted-data-with-partners/consumer/1-key-import/terraform.example.tfvars b/share-encrypted-data-with-partners/consumer/1-key-import/terraform.example.tfvars new file mode 100644 index 00000000..0af6f81c --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/1-key-import/terraform.example.tfvars @@ -0,0 +1,17 @@ +/** + * 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. + */ + +wrapped_key_path = "REPLACE-WITH-YOUR-WRAPPED-DEK-PATH" diff --git a/share-encrypted-data-with-partners/consumer/1-key-import/variables.tf b/share-encrypted-data-with-partners/consumer/1-key-import/variables.tf new file mode 100644 index 00000000..b069b3b6 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/1-key-import/variables.tf @@ -0,0 +1,26 @@ +/** + * 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. + */ + +variable "wrapped_key_path" { + description = "Path to the wrapped key file." + type = string +} + +variable "crypto_key_algorithm_import" { + description = "Algorithm to use when creating a crypto key version through import. See more: https://cloud.google.com/sdk/gcloud/reference/kms/keys/versions/import." + type = string + default = "aes-256-gcm" +} diff --git a/share-encrypted-data-with-partners/consumer/1-key-import/versions.tf b/share-encrypted-data-with-partners/consumer/1-key-import/versions.tf new file mode 100644 index 00000000..0ca56b22 --- /dev/null +++ b/share-encrypted-data-with-partners/consumer/1-key-import/versions.tf @@ -0,0 +1,27 @@ +/** + * 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. + */ + +terraform { + required_version = ">= 1.5.7" + required_providers { + + null = { + source = "hashicorp/null" + version = "3.2.2" + } + + } +} diff --git a/share-encrypted-data-with-partners/examples/python/README.md b/share-encrypted-data-with-partners/examples/python/README.md new file mode 100644 index 00000000..1b3de08c --- /dev/null +++ b/share-encrypted-data-with-partners/examples/python/README.md @@ -0,0 +1,52 @@ +# Raw Encryption: Example of sharing encrypted data with partners using Python + +## Overview + +This guide provides a raw encryption data sharing example using Python. + +## Prerequisites + +- [Python 3+](https://www.python.org/downloads/); +- [pip](https://pip.pypa.io/en/stable/installation/); +- (For consumer/decrypt only) [Google Cloud CLI (`gcloud`)](https://cloud.google.com/sdk/docs/install-sdk); + - You must be authenticated in your GCP account. If you're not, you should run `gcloud auth login`; + +**Note:** It is recommended that you create and enable a Python [virtual environment](https://docs.python.org/3/library/venv.html) before starting this tutorial. + +## Glossary + +- AAD: Additional Authenticated Data + - The AAD is used for authentication purposes, to ensure the ciphertext has not been altered. +- IV: Initialization Vector + - The IV, which should be generated randomly, is used as a parameter to make the encryption semantically secure. + +## Install dependencies + +1. Install dependencies using `pip`. + ```sh + pip install -r requirements.txt + ``` + +## Encrypt and decrypt sensitive data + +1. (For producer only) Run the encrypt script passing DEK file, data, and AAD as inputs. + ```sh + python encrypt.py \ + --data_encryption_key_path "REPLACE-WITH-YOUR-DEK-PATH" \ + --data "a secret message to be shared" \ + --aad "pre-determined authenticated but unencrypted data" + ``` + **Note:** The outputs provided by this script would be the ciphertext, IV, and AAD. These should be shared with consumer. They will be required to run the decrypt script. + +1. (For consumer only) Run the decrypt script passing ciphertext, IV and AAD and your GCP info. + ```sh + python decrypt.py \ + --ciphertext "REPLACE-WITH-YOUR-CIPHERTEXT" \ + --iv "REPLACE-WITH-YOUR-IV" \ + --aad "pre-determined authenticated but unencrypted data" \ + --gcp_project "REPLACE-WITH-YOUR-PROJECT-ID" \ + --gcp_location "REPLACE-WITH-YOUR-KMS-LOCATION" \ + --gcp_keyring "REPLACE-WITH-YOUR-KEYRING" \ + --gcp_key "REPLACE-WITH-YOUR-KEY" + ``` + **Note:** `--gcp_key_version` parameter is optional. If not provided the default will be "1". diff --git a/share-encrypted-data-with-partners/examples/python/decrypt.py b/share-encrypted-data-with-partners/examples/python/decrypt.py new file mode 100644 index 00000000..021a7d57 --- /dev/null +++ b/share-encrypted-data-with-partners/examples/python/decrypt.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +# 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 +# +# https://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. + +import argparse +from google.cloud import kms_v1 +from base64 import b64decode + +# Define the argument parser +parser = argparse.ArgumentParser() + +# Add arguments +parser.add_argument("--ciphertext", required=True, help="Ciphertext in base64") +parser.add_argument( + "--iv", required=True, help="Initialization Vector in base64" +) +parser.add_argument( + "--aad", required=True, help="Additional Authenticated Data" +) +parser.add_argument("--gcp_project", required=True, help="GCP Project ID") +parser.add_argument("--gcp_location", required=True, help="GCP KMS Location") +parser.add_argument("--gcp_keyring", required=True, help="GCP KMS Keyring") +parser.add_argument("--gcp_key", required=True, help="GCP Key") +parser.add_argument("--gcp_key_version", help="GCP Key Version", default="1") + +# Parse the arguments +args = parser.parse_args() + +# Access the parameters and convert what is needed +ciphertext = b64decode(args.ciphertext) +iv = b64decode(args.iv) +aad = str(args.aad).encode() + +# Create a client +client = kms_v1.KeyManagementServiceClient() + +# Initialize request argument(s) +key_identifier = ( + f"projects/{args.gcp_project}/locations/{args.gcp_location}/" + f"keyRings/{args.gcp_keyring}/cryptoKeys/{args.gcp_key}/" + f"cryptoKeyVersions/{args.gcp_key_version}" +) + +request = kms_v1.RawDecryptRequest( + name=key_identifier, + ciphertext=ciphertext, + initialization_vector=iv, + additional_authenticated_data=aad, +) + +# Make the decrypt request +response = client.raw_decrypt(request=request) + +# Handle the response +print(response) diff --git a/share-encrypted-data-with-partners/examples/python/encrypt.py b/share-encrypted-data-with-partners/examples/python/encrypt.py new file mode 100644 index 00000000..06cdeef6 --- /dev/null +++ b/share-encrypted-data-with-partners/examples/python/encrypt.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# 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 +# +# https://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. + +import os +from cryptography.hazmat.primitives.ciphers.aead import AESGCM +import argparse +from base64 import b64encode + +# Define the argument parser +parser = argparse.ArgumentParser() + +# Add arguments +parser.add_argument( + "--data_encryption_key_path", required=True, help="DEK path" +) +parser.add_argument("--data", required=True, help="Sensitive data") +parser.add_argument( + "--aad", required=True, help="Additional Authenticated Data" +) + +# Parse the arguments +args = parser.parse_args() + +# Access the parameters +data_encryption_key_path = args.data_encryption_key_path +data = str(args.data).encode() +aad = str(args.aad).encode() + +# Reading key bytes +key = open(data_encryption_key_path, "rb").read() +aesgcm = AESGCM(key) +nonce = os.urandom(12) + +# Encrypting data with key bytes +ciphertext = aesgcm.encrypt(nonce, data, aad) + +# Printing the outputs needed for decrypt process +print(f"Ciphertext base 64: {b64encode(ciphertext)}") +print(f"Generated IV base64 for encryption: {b64encode(nonce)}") +print(f"Additional Authenticated Data (AAD): {aad}") diff --git a/share-encrypted-data-with-partners/examples/python/requirements.txt b/share-encrypted-data-with-partners/examples/python/requirements.txt new file mode 100644 index 00000000..d4c09efd --- /dev/null +++ b/share-encrypted-data-with-partners/examples/python/requirements.txt @@ -0,0 +1,2 @@ +cryptography==42.0.5 +google-cloud-kms==2.21.2 diff --git a/share-encrypted-data-with-partners/producer/README.md b/share-encrypted-data-with-partners/producer/README.md new file mode 100644 index 00000000..ba2dcbc4 --- /dev/null +++ b/share-encrypted-data-with-partners/producer/README.md @@ -0,0 +1,64 @@ +# [Producer module] Raw Encryption: Building infrastructure for sharing encrypted data with partners + +## Overview + +This module provides the DEK (Data Encryption Key) wrapping process using OpenSSL for raw encryption feature in Cloud KMS with Terraform. + +## Prerequisites + +- [Terraform](https://developer.hashicorp.com/terraform/downloads); +- [OpenSSL](https://www.openssl.org/source/index.html); +- Data Encryption Key (DEK) file generated on key management system (HSM); + - **Note:** Use your company’s approved method to generate a data encryption key in a secure environment; + - **Note 2:** For testing purposes a random key generation step will be provided. **Random key created this way is not appropriate for production use.** +- Key Encryption Key (KEK) file; + - **Note:** Usually an Import Job public key file received from consumer. + +## Glossary + +- AAD: Additional Authenticated Data + - The AAD is used for authentication purposes, to ensure the ciphertext has not been altered. +- IV: Initialization Vector + - The IV, which should be generated randomly, is used as a parameter to make the encryption semantically secure. + +## Deploy infrastructure + +1. (Optional) **For testing purposes only:** Generate a random DEK (Data Encryption Key) using OpenSSL. + ```sh + openssl rand 32 > ./random_datakey.bin + ``` + **Note:** For production, use your company’s approved method to generate a data encryption key in a secure environment; + +1. You can go to [examples folder](../examples/) and choose one of the available encrypt approaches in order to test the generation of the ciphertext, IV and AAD. + +1. Rename `terraform.example.tfvars` to `terraform.tfvars`: + ```sh + mv terraform.example.tfvars terraform.tfvars + ``` + +1. Update `terraform.tfvars` file with the required values. + +1. Run the Terraform workflow. + + ```sh + terraform init + terraform plan + terraform apply + ``` + +1. The DEK (Data Encryption Key) is now wrapped and ready to be sent to consumer. Wrapped key file, ciphertext, IV, and AAD should be sent to consumer. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| data\_encryption\_key\_path | Path to the key used to encrypt data itself (DEK). A random key can be generated with OpenSSL, see step 1 on README. Use the random key for testing only. A the random key created this way is not appropriate for production use, use your company's approved method to generate a data encryption key in a secure environment instead. | `string` | n/a | yes | +| key\_encryption\_key\_path | Path to where the KEK (Key Encryption Key) is stored. This is usually a public key file extracted from an Import Job received from consumer. | `string` | n/a | yes | +| wrapped\_key\_path | Path to where the wrapped key file should be created. | `string` | n/a | yes | + +## Outputs + +No outputs. + + diff --git a/share-encrypted-data-with-partners/producer/main.tf b/share-encrypted-data-with-partners/producer/main.tf new file mode 100644 index 00000000..8cee2ff5 --- /dev/null +++ b/share-encrypted-data-with-partners/producer/main.tf @@ -0,0 +1,23 @@ +/** + * 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. + */ + +// Wrapping a DEK (Data Encryption Key) using OpenSSL. See more in: https://cloud.google.com/kms/docs/wrapping-a-key#wrap_key +resource "null_resource" "openssl-dek-wrap-process" { + + provisioner "local-exec" { + command = "openssl pkeyutl -encrypt -pubin -inkey ${var.key_encryption_key_path} -in ${var.data_encryption_key_path} -out ${var.wrapped_key_path} -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256" + } +} diff --git a/share-encrypted-data-with-partners/producer/terraform.example.tfvars b/share-encrypted-data-with-partners/producer/terraform.example.tfvars new file mode 100644 index 00000000..73dd910f --- /dev/null +++ b/share-encrypted-data-with-partners/producer/terraform.example.tfvars @@ -0,0 +1,19 @@ +/** + * 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. + */ + +data_encryption_key_path = "REPLACE-WITH-YOUR-DEK-PATH" # Replace with "./random_datakey.bin" if you ran the testing optional step. +key_encryption_key_path = "REPLACE-WITH-YOUR-KEK-PATH" # Replace with a public key file path that you might have received from consumer. +wrapped_key_path = "./wrapped-key" # Desired wrapped key path diff --git a/share-encrypted-data-with-partners/producer/variables.tf b/share-encrypted-data-with-partners/producer/variables.tf new file mode 100644 index 00000000..0d30d22a --- /dev/null +++ b/share-encrypted-data-with-partners/producer/variables.tf @@ -0,0 +1,31 @@ +/** + * 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. + */ + + +variable "data_encryption_key_path" { + description = "Path to the key used to encrypt data itself (DEK). A random key can be generated with OpenSSL, see step 1 on README. Use the random key for testing only. A the random key created this way is not appropriate for production use, use your company's approved method to generate a data encryption key in a secure environment instead." + type = string +} + +variable "wrapped_key_path" { + description = "Path to where the wrapped key file should be created." + type = string +} + +variable "key_encryption_key_path" { + description = "Path to where the KEK (Key Encryption Key) is stored. This is usually a public key file extracted from an Import Job received from consumer." + type = string +} diff --git a/share-encrypted-data-with-partners/producer/versions.tf b/share-encrypted-data-with-partners/producer/versions.tf new file mode 100644 index 00000000..0ca56b22 --- /dev/null +++ b/share-encrypted-data-with-partners/producer/versions.tf @@ -0,0 +1,27 @@ +/** + * 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. + */ + +terraform { + required_version = ">= 1.5.7" + required_providers { + + null = { + source = "hashicorp/null" + version = "3.2.2" + } + + } +}