diff --git a/cloud-operations/onprem-sa-key-management/README.md b/cloud-operations/onprem-sa-key-management/README.md new file mode 100644 index 00000000..6661133d --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/README.md @@ -0,0 +1,54 @@ +# Generationg and uploading public keys for a service accounts + +This example shows how to manage IAM Service Account Keys by generating a key pair and uploading public keys to GCP. + +By generating a key inside a `box` where the key is intended to be used we AVOID: + - [passing keys between users](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#pass-between-users) or systems + - having SA key stored in the terraform state (only public part in the state) + - having SA key with no expiration period + +TODO (averbukh) +## Running the example +# cleaning up example keys +- rm -f /public-keys/data-uploader/ +- rm -f /public-keys/prisma-security/ + +# generate your keys +- mkdir keys && cd keys +- openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ + -keyout data_uploader_private_key.pem \ + -out ../public-keys/data-uploader/public_key.pem \ + -subj "/CN=unused" +- openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \ + -keyout prisma_security_private_key.pem \ + -out ../public-keys/prisma-security/public_key.pem \ + -subj "/CN=unused" + +- cd .. +- terraform init +- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT + +- terraform show -json | jq '.values.outputs."data-uploader-credentials".value."public_key.pem" | fromjson' > data-uploader.json +- terraform show -json | jq '.values.outputs."prisma-security-credentials".value."public_key.pem" | fromjson' > prisma-security.json + +- contents=$(jq --arg key "$(cat keys/data_uploader_private_key.pem)" '.private_key=$key' data-uploader.json) && echo "$contents" > data-uploader.json +- contents=$(jq --arg key "$(cat keys/prisma_security_private_key.pem)" '.private_key=$key' prisma-security.json) && echo "$contents" > prisma-security.json + +- gcloud auth activate-service-account --key-file prisma-security.json +- gcloud auth activate-service-account --key-file data-uploader.json + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| project_id | Project id. | string | ✓ | | +| *project_create* | Create project instead ofusing an existing one. | bool | | false | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| data-uploader-credentials | Data Uploader SA json key templates. | | +| prisma-security-credentials | Prisma Security SA json key templates. | | + diff --git a/cloud-operations/onprem-sa-key-management/backend.tf.sample b/cloud-operations/onprem-sa-key-management/backend.tf.sample new file mode 100644 index 00000000..9eebc1ba --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/backend.tf.sample @@ -0,0 +1,23 @@ +# Copyright 2021 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. + +# set a valid bucket below and rename this file to backend.tf + +terraform { + backend "gcs" { + bucket = "" + prefix = "fabric/operations/onprem-sa-key-management" + } +} + diff --git a/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt new file mode 100644 index 00000000..da5b32f0 --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/cloud-shell-readme.txt @@ -0,0 +1,5 @@ + + +################################# Quickstart ################################# +TODO(averbukh) +Refer to the README.md file for more info and testing flow. diff --git a/cloud-operations/onprem-sa-key-management/main.tf b/cloud-operations/onprem-sa-key-management/main.tf new file mode 100644 index 00000000..302574d8 --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/main.tf @@ -0,0 +1,48 @@ +/** + * Copyright 2021 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 "project" { + source = "../../modules/project" + name = var.project_id + project_create = var.project_create +} + +module "onprem-data-uploader" { + source = "../../modules/iam-service-account" + project_id = module.project.project_id + name = "onprem-data-uploader" + iam_project_roles = { + (module.project.project_id) = [ + "roles/bigquery.dataOwner", + "roles/bigquery.jobUser", + "roles/storage.objectAdmin" + ] + } + public_keys_directory = "public-keys/data-uploader/" +} + +module "onprem-prisma-security" { + source = "../../modules/iam-service-account" + project_id = module.project.project_id + name = "onprem-prisma-security" + iam_project_roles = { + (module.project.project_id) = [ + "roles/iam.securityReviewer" + ] + } + public_keys_directory = "public-keys/prisma-security/" +} diff --git a/cloud-operations/onprem-sa-key-management/outputs.tf b/cloud-operations/onprem-sa-key-management/outputs.tf new file mode 100644 index 00000000..539f27fd --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/outputs.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2021 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 "data-uploader-credentials" { + description = "Data Uploader SA json key templates." + value = module.onprem-data-uploader.service_account_credentials +} + +output "prisma-security-credentials" { + description = "Prisma Security SA json key templates." + value = module.onprem-prisma-security.service_account_credentials +} diff --git a/cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem b/cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem new file mode 100644 index 00000000..ad5bc9fe --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/public-keys/data-uploader/public_key.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICnjCCAYYCCQDhgw8htVCGmTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZ1 +bnVzZWQwHhcNMjExMjA2MDgwNTAyWhcNMzExMjA0MDgwNTAyWjARMQ8wDQYDVQQD +DAZ1bnVzZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0xlwdjkBS +1ovANJ1RXKpFdbPQWYlqKUUo+/KLClNYC9KxRqrc+u5FtPIdCPv5WRH5sz+z8gcf +3zJMht0dO7fOwJ9wSDKzvHkMUdXTGBPbm2i9PNA6f+YEwJQjWJlAHFH4Lp3x6ddT +4KO4FRQEkN/5V1+sfmyGGFaSXaoi+PcDcQHvfUUlp5iyX4I+8tqwh1kdg1M5orkE +7iBG0wHWzfOSmZq5in6t9+lWzOZeYapi8bVBm7Vz+dmHZPKS6EGmAXS1wpLCSKHB +uv23KXY4gAXOPHiDI70JpeNiSJBE9WgXs+nL78vNjLTvDhpC10b9nOxLjRc6wA5b +3q2Am0dW1DPRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAJqyTIibNZM/q30Fn+vR +V9q++19CIervZig1uCarH1M86cpPYRfKcYHOi6tnoCTL9VG8Ky8pbmkZNkET7vnN +OQirpsPmqu3d+FBoqXUt8w1mT1JVr0YiTo3i07zTH8rvQKHjEfPxR73IAyYNvJ3D +k3SdUvU3xXOa+otOQcBKIxX6mJPLhzXgZd144KCfD95qOvpoQOsNW4UWXZ3sPC0k +VcMlN5O8/+D65y63nNtyECXvLicLdn/cdpA2H7Tqhz2ZZR+6tLcDW1kSsA8b6+rQ +1IaKpF+TYo0jMD+WLatRrOHXOWije8871zooAXq9MLVJrT889TdsmEIYT7YPWIeJ +Jcg= +-----END CERTIFICATE----- diff --git a/cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem b/cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem new file mode 100644 index 00000000..5da20f0b --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/public-keys/prisma-security/public_key.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICnjCCAYYCCQDXMv59IiZqfTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZ1 +bnVzZWQwHhcNMjExMjA2MDgwNjE3WhcNMzExMjA0MDgwNjE3WjARMQ8wDQYDVQQD +DAZ1bnVzZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOK6XwgTzL +icSITBrQBmhnYNOuggDhQr40j8/pIuTOiFZbd+ne3MhcFxpE58T9cOXgR0i/S4ok ++kcGE74H2U7RsRpNi7fJhi62T9e2CXpibURQNJD6y0lXBQkfx6kCrhyvXqHbTxm5 +J0f5mpLlze+w7ATikmYI0mrU9XjtnRJOdxtGfiIaQ/suGTaZ0z4tZgAXy9RnwUAb +LPXn0BD1+GYpCs82+1q7HpMIf343VRH0AdsQJteQSj5LKfaZZTNUF9NIgKtMylck +z0Pt8TmBU0GtJX/XkSWCwMUdqdedXkvhY1XoAZPjaEBSSwq6P15PmdpDq9q2TYjD +8U3kuCX0AlWjAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADAmjI1sg150MK97DCSl +d5OpEShCypaEZSLb/mFONW6mTX2OSdF9ipd9B07BQ2DrL8Xou2/V1aDtQZOWPIGu +Hlm1LKw8sZY2rWX0Rq/v/NxY5iGRlwPMh7Rn9fnpHgaC1PktoDJEcvNMpzBjtfKn +beKP9MNSChAFTTbJVWO5xT/ljE/yoPL3jyJKzKHH7y7AfbonrbQjAENbX/WCRYh3 +zOEWZG/fusRcKkZ/cO7wFFP1gzJFE9wFRu7LOA/FntCixtVSnclsOnunQfqQEVmp +Y0IjfceIerJysCTo0I5HfRw0DOFfZimallOa4Mv5BDmzMWWyX9TvppHCnmqvM2El +ISY= +-----END CERTIFICATE----- diff --git a/cloud-operations/onprem-sa-key-management/variables.tf b/cloud-operations/onprem-sa-key-management/variables.tf new file mode 100644 index 00000000..3740814b --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/variables.tf @@ -0,0 +1,26 @@ +/** + * Copyright 2021 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_create" { + description = "Create project instead ofusing an existing one." + type = bool + default = false +} + +variable "project_id" { + description = "Project id." + type = string +} diff --git a/cloud-operations/onprem-sa-key-management/versions.tf b/cloud-operations/onprem-sa-key-management/versions.tf new file mode 100644 index 00000000..1cc6bf89 --- /dev/null +++ b/cloud-operations/onprem-sa-key-management/versions.tf @@ -0,0 +1,29 @@ +# Copyright 2021 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. + +terraform { + required_version = ">= 1.0.0" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 4.0.0" + } + } +} + + diff --git a/modules/iam-service-account/README.md b/modules/iam-service-account/README.md index 12030e19..0a3b57d9 100644 --- a/modules/iam-service-account/README.md +++ b/modules/iam-service-account/README.md @@ -1,6 +1,6 @@ # Google Service Account Module -This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs. +This module allows simplified creation and management of one a service account and its IAM bindings. A key can optionally be generated and will be stored in Terraform state. To use it create a sensitive output in your root modules referencing the `key` output, then extract the private key from the JSON formatted outputs. Alternatively, the `key` can be generated with `openssl` library and only public part uploaded to the Service Account, for more refere to the [Onprem SA Key Management](../../cloud-operations/onprem-sa-key-management/README.md)) example. ## Example @@ -42,6 +42,7 @@ module "myproject-default-service-accounts" { | *iam_project_roles* | Project roles granted to the service account, by project id. | map(list(string)) | | {} | | *iam_storage_roles* | Storage roles granted to the service account, by bucket name. | map(list(string)) | | {} | | *prefix* | Prefix applied to service account names. | string | | null | +| *public_keys_directory* | Path to public keys data files to upload to the service account (should have `.crt` extension). | string | | | | *service_account_create* | Create service account. When set to false, uses a data source to reference an existing service account. | bool | | true | ## Outputs @@ -52,4 +53,5 @@ module "myproject-default-service-accounts" { | iam_email | IAM-format service account email. | | | key | Service account key. | ✓ | | service_account | Service account resource. | | +| service_account_credentials | Service account json credential templates for uploaded public keys data. | | diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index 244f182e..005ee12c 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -65,6 +65,29 @@ locals { ? try(google_service_account.service_account.0, null) : try(data.google_service_account.service_account.0, null) ) + service_account_credential_templates = { + for file, _ in local.public_keys_data : file => jsonencode( + { + type: "service_account", + project_id: var.project_id, + private_key_id: split("/",google_service_account_key.upload_key[file].id)[5] + private_key: "REPLASE_ME_WITH_PRIVATE_KEY_DATA" + client_email: local.resource_email_static + client_id: local.service_account.unique_id, + auth_uri: "https://accounts.google.com/o/oauth2/auth", + token_uri: "https://oauth2.googleapis.com/token", + auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs", + client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/${urlencode(local.resource_email_static)}" + } + ) + } + public_keys_data = ( + var.public_keys_directory != "" + ? { + for file in fileset("${path.root}/${var.public_keys_directory}", "*.pem") + : file => filebase64("${path.root}/${var.public_keys_directory}/${file}")} + : {} + ) } @@ -87,6 +110,12 @@ resource "google_service_account_key" "key" { service_account_id = local.service_account.email } +resource "google_service_account_key" "upload_key" { + for_each = local.public_keys_data + service_account_id = local.service_account.email + public_key_data = each.value +} + resource "google_service_account_iam_binding" "roles" { for_each = var.iam service_account_id = local.service_account.name diff --git a/modules/iam-service-account/outputs.tf b/modules/iam-service-account/outputs.tf index 45faa5f3..cf12f530 100644 --- a/modules/iam-service-account/outputs.tf +++ b/modules/iam-service-account/outputs.tf @@ -40,3 +40,8 @@ output "service_account" { description = "Service account resource." value = local.service_account } + +output "service_account_credentials" { + description = "Service account json credential templates for uploaded public keys data." + value = local.service_account_credential_templates +} diff --git a/modules/iam-service-account/variables.tf b/modules/iam-service-account/variables.tf index 59579c7a..d1f5d3e3 100644 --- a/modules/iam-service-account/variables.tf +++ b/modules/iam-service-account/variables.tf @@ -84,6 +84,12 @@ variable "project_id" { type = string } +variable "public_keys_directory" { + description = "Path to public keys data files to upload to the service account (should have `.pem` extension)." + type = string + default = "" +} + variable "service_account_create" { description = "Create service account. When set to false, uses a data source to reference an existing service account." type = bool