From 06b787ef97b87c94bf97cd85c52b37c2cafdeea2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 16:55:55 -0400 Subject: [PATCH 01/11] Start a module for creating a workload identity service account. --- terraform/basic_gke/main.tf | 13 +++++++------ .../workload_identity_account.tf | 8 ++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 terraform/modules/workload_identity_account/workload_identity_account.tf diff --git a/terraform/basic_gke/main.tf b/terraform/basic_gke/main.tf index 747dcba..7af54f9 100644 --- a/terraform/basic_gke/main.tf +++ b/terraform/basic_gke/main.tf @@ -105,6 +105,13 @@ module "cloudsql" { ] } +# Create a workload identity service account for IAM authentication to +# cloudsql +module "cloudsql_test_sa" { + source = "../modules/workload_identity_account" + project = var.project +} + #################### Redis ################################ module "redis" { @@ -127,9 +134,3 @@ output "redis_port" { description = "Port for redis database." value = module.redis.redis_port } - - - - - - diff --git a/terraform/modules/workload_identity_account/workload_identity_account.tf b/terraform/modules/workload_identity_account/workload_identity_account.tf new file mode 100644 index 0000000..c6c10e6 --- /dev/null +++ b/terraform/modules/workload_identity_account/workload_identity_account.tf @@ -0,0 +1,8 @@ +# Requires a google_iam_workload_identity_pool to exist, but it is not +# referenced in this module. + + +variable "project" { + description = "Project ID." + type = string +} From e17e2f24a7b23b8655e47133a2279e27a03c0e61 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 17:03:14 -0400 Subject: [PATCH 02/11] Create the google service account. --- terraform/basic_gke/main.tf | 5 +++-- .../workload_identity_account.tf | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/terraform/basic_gke/main.tf b/terraform/basic_gke/main.tf index 7af54f9..91b1a2e 100644 --- a/terraform/basic_gke/main.tf +++ b/terraform/basic_gke/main.tf @@ -108,8 +108,9 @@ module "cloudsql" { # Create a workload identity service account for IAM authentication to # cloudsql module "cloudsql_test_sa" { - source = "../modules/workload_identity_account" - project = var.project + source = "../modules/workload_identity_account" + project = var.project + k8s_service_account = "test-sa" } #################### Redis ################################ diff --git a/terraform/modules/workload_identity_account/workload_identity_account.tf b/terraform/modules/workload_identity_account/workload_identity_account.tf index c6c10e6..c79e3ce 100644 --- a/terraform/modules/workload_identity_account/workload_identity_account.tf +++ b/terraform/modules/workload_identity_account/workload_identity_account.tf @@ -6,3 +6,19 @@ variable "project" { description = "Project ID." type = string } + +variable "k8s_namespace" { + description = "Name of the kubernetes namespace containing the service account." + type = string + default = "default" +} + +variable "k8s_service_account" { + description = "Service account name from kubernetes." + type = string +} + +resource "google_service_account" "service_account" { + account_id = "wi-${var.k8s_namespace}-${var.k8s_service_account}" + display_name = "Workload identity account for GKE [${var.k8s_namespace}/${var.k8s_service_account}]" +} From 94b6a187e035d0e857796f9d01e3ba755e23dacd Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 17:04:51 -0400 Subject: [PATCH 03/11] Add an output for the generated service account. --- .../workload_identity_account/workload_identity_account.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/terraform/modules/workload_identity_account/workload_identity_account.tf b/terraform/modules/workload_identity_account/workload_identity_account.tf index c79e3ce..81705ca 100644 --- a/terraform/modules/workload_identity_account/workload_identity_account.tf +++ b/terraform/modules/workload_identity_account/workload_identity_account.tf @@ -18,6 +18,11 @@ variable "k8s_service_account" { type = string } +output "service_account" { + description = "The google_service_account that has been bound to the kubernetes service account." + value = google_service_account.service_account +} + resource "google_service_account" "service_account" { account_id = "wi-${var.k8s_namespace}-${var.k8s_service_account}" display_name = "Workload identity account for GKE [${var.k8s_namespace}/${var.k8s_service_account}]" From 349d40c8f6973db02b359e845c26a5359e9fa0ad Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 17:16:34 -0400 Subject: [PATCH 04/11] Add binding. --- .../workload_identity_account.tf | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/terraform/modules/workload_identity_account/workload_identity_account.tf b/terraform/modules/workload_identity_account/workload_identity_account.tf index 81705ca..2ea3d24 100644 --- a/terraform/modules/workload_identity_account/workload_identity_account.tf +++ b/terraform/modules/workload_identity_account/workload_identity_account.tf @@ -27,3 +27,18 @@ resource "google_service_account" "service_account" { account_id = "wi-${var.k8s_namespace}-${var.k8s_service_account}" display_name = "Workload identity account for GKE [${var.k8s_namespace}/${var.k8s_service_account}]" } + +data "google_iam_policy" "policy" { + binding { + role = "roles/iam.workloadIdentityUser" + + members = [ + "serviceAccount:${var.project}.svc.id.goog[${var.k8s_namespace}/${var.k8s_service_account}]", + ] + } +} + +resource "google_service_account_iam_policy" "policy_binding" { + service_account_id = google_service_account.service_account.name + policy_data = data.google_iam_policy.policy.policy_data +} From 5945ad86a8c5fcc7f8ef8cdbfe45e30b9641cff3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 18:33:30 -0400 Subject: [PATCH 05/11] Add the sqladmin google project service. --- terraform/modules/cloudsql/cloudsql.tf | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/terraform/modules/cloudsql/cloudsql.tf b/terraform/modules/cloudsql/cloudsql.tf index b6d2546..ad86b28 100644 --- a/terraform/modules/cloudsql/cloudsql.tf +++ b/terraform/modules/cloudsql/cloudsql.tf @@ -30,6 +30,13 @@ output "connection_name" { value = google_sql_database_instance.instance.connection_name } +# Needed for CloudSQL Auth Proxy +resource "google_project_service" "sqladmin" { + project = var.project + service = "sqladmin.googleapis.com" + disable_dependent_services = true +} + resource "random_id" "cloudsql" { byte_length = 4 } From 78b497783b17409e15c101d429e27069334fc381 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 18:45:27 -0400 Subject: [PATCH 06/11] Add a cloudsql username output from the workload identity module. --- .../workload_identity_account/workload_identity_account.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/terraform/modules/workload_identity_account/workload_identity_account.tf b/terraform/modules/workload_identity_account/workload_identity_account.tf index 2ea3d24..c06aff6 100644 --- a/terraform/modules/workload_identity_account/workload_identity_account.tf +++ b/terraform/modules/workload_identity_account/workload_identity_account.tf @@ -23,6 +23,11 @@ output "service_account" { value = google_service_account.service_account } +output "cloudsql_username" { + description = "If this service account is to be used with IAM database authentication, this would be the username for the user. Note that the google_sql_user is not created by this module." + value = trimsuffix(google_service_account.service_account.email, ".gserviceaccount.com") +} + resource "google_service_account" "service_account" { account_id = "wi-${var.k8s_namespace}-${var.k8s_service_account}" display_name = "Workload identity account for GKE [${var.k8s_namespace}/${var.k8s_service_account}]" From 8a7fa68a6e2935febf9b3c465bf718ec2e5838a9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 20:49:50 -0400 Subject: [PATCH 07/11] Add IAM auth and set postgres user password. --- terraform/modules/cloudsql/cloudsql.tf | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/terraform/modules/cloudsql/cloudsql.tf b/terraform/modules/cloudsql/cloudsql.tf index ad86b28..b2b5cd8 100644 --- a/terraform/modules/cloudsql/cloudsql.tf +++ b/terraform/modules/cloudsql/cloudsql.tf @@ -1,3 +1,5 @@ +# For the cloudsql auth proxy grant roles/cloudsql.instanceUser and +# roles/cloudsql.client roles to the service account for the proxy. variable "project" { description = "Project ID." type = string @@ -25,11 +27,22 @@ variable "private_network_id" { type = string } +variable "postgres_password" { + description = "Password for the default postgres user." + type = string + default = "hunter2" +} + output "connection_name" { description = "The connection string for connecting to the cloudsql instance (for example, through cloudsql proxy)." value = google_sql_database_instance.instance.connection_name } +output "instance" { + description = "The google_sql_database_instance object." + value = google_sql_database_instance.instance +} + # Needed for CloudSQL Auth Proxy resource "google_project_service" "sqladmin" { project = var.project @@ -55,8 +68,20 @@ resource "google_sql_database_instance" "instance" { private_network = var.private_network_id require_ssl = true } + + database_flags { + name = "cloudsql.iam_authentication" + value = "on" + } } deletion_protection = "false" # deletion_protection = "true" } + +resource "google_sql_user" "postgres" { + project = var.project + name = "postgres" + instance = google_sql_database_instance.instance.name + password = var.postgres_password +} From fd63ea2c434252519fa2dd4536434da8e3f6a538 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 21:19:08 -0400 Subject: [PATCH 08/11] Generate a postgresql certificate. --- .gitignore | 4 +++ terraform/basic_gke/main.tf | 39 ++++++++++++++++++++++++++ terraform/modules/cloudsql/cloudsql.tf | 10 +++++++ 3 files changed, 53 insertions(+) diff --git a/.gitignore b/.gitignore index 55c0266..a72fd7c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ .terraform/ terraform.tfstate terraform.tfstate.backup + +pgclient.crt +pgclient.key +pgserver.crt diff --git a/terraform/basic_gke/main.tf b/terraform/basic_gke/main.tf index 91b1a2e..117fb28 100644 --- a/terraform/basic_gke/main.tf +++ b/terraform/basic_gke/main.tf @@ -105,6 +105,45 @@ module "cloudsql" { ] } +output "cloudsql_server_certificate" { + description = "CA certificate" + value = module.cloudsql.certificate.server_ca_cert + sensitive = true +} + +output "cloudsql_client_certificate" { + description = "CA certificate" + value = module.cloudsql.certificate.cert + sensitive = true +} + +output "cloudsql_client_key" { + description = "CA certificate" + value = module.cloudsql.certificate.private_key + sensitive = true +} + +resource "local_file" "pgserver_crt" { + sensitive_content = module.cloudsql.certificate.server_ca_cert + filename = "${path.module}/pgserver.crt" + file_permission = "0600" + directory_permission = "0700" +} + +resource "local_file" "pgclient_crt" { + sensitive_content = module.cloudsql.certificate.cert + filename = "${path.module}/pgclient.crt" + file_permission = "0600" + directory_permission = "0700" +} + +resource "local_file" "pgclient_key" { + sensitive_content = module.cloudsql.certificate.private_key + filename = "${path.module}/pgclient.key" + file_permission = "0600" + directory_permission = "0700" +} + # Create a workload identity service account for IAM authentication to # cloudsql module "cloudsql_test_sa" { diff --git a/terraform/modules/cloudsql/cloudsql.tf b/terraform/modules/cloudsql/cloudsql.tf index b2b5cd8..4059dc3 100644 --- a/terraform/modules/cloudsql/cloudsql.tf +++ b/terraform/modules/cloudsql/cloudsql.tf @@ -43,6 +43,11 @@ output "instance" { value = google_sql_database_instance.instance } +output "certificate" { + description = "TLS certificate for connecting to the database." + value = google_sql_ssl_cert.client_cert +} + # Needed for CloudSQL Auth Proxy resource "google_project_service" "sqladmin" { project = var.project @@ -85,3 +90,8 @@ resource "google_sql_user" "postgres" { instance = google_sql_database_instance.instance.name password = var.postgres_password } + +resource "google_sql_ssl_cert" "client_cert" { + common_name = "client-name" + instance = google_sql_database_instance.instance.name +} From dfb51918682cd9dc289ae8ab218ae6072cb155ec Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 21:26:21 -0400 Subject: [PATCH 09/11] Add cloudsql ip address output. --- terraform/basic_gke/main.tf | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/terraform/basic_gke/main.tf b/terraform/basic_gke/main.tf index 117fb28..ffd0417 100644 --- a/terraform/basic_gke/main.tf +++ b/terraform/basic_gke/main.tf @@ -105,20 +105,25 @@ module "cloudsql" { ] } +output "cloudsql_ip_address" { + description = "IP address for cloudsql database." + value = module.cloudsql.instance.ip_address.0.ip_address +} + output "cloudsql_server_certificate" { - description = "CA certificate" + description = "CA certificate." value = module.cloudsql.certificate.server_ca_cert sensitive = true } output "cloudsql_client_certificate" { - description = "CA certificate" + description = "Client certificate." value = module.cloudsql.certificate.cert sensitive = true } output "cloudsql_client_key" { - description = "CA certificate" + description = "Client key." value = module.cloudsql.certificate.private_key sensitive = true } From b087d1eed9340c3a52a6cec564011bd74c036174 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 21:47:20 -0400 Subject: [PATCH 10/11] Output a connection URL for cloudsql. --- terraform/basic_gke/main.tf | 5 +++++ terraform/modules/cloudsql/cloudsql.tf | 1 + 2 files changed, 6 insertions(+) diff --git a/terraform/basic_gke/main.tf b/terraform/basic_gke/main.tf index ffd0417..3606993 100644 --- a/terraform/basic_gke/main.tf +++ b/terraform/basic_gke/main.tf @@ -149,6 +149,11 @@ resource "local_file" "pgclient_key" { directory_permission = "0700" } +output "cloudsql_connection_string" { + description = "Connection URL for main user in cloudsql." + value = "postgresql://postgres@${module.cloudsql.instance.ip_address.0.ip_address}/postgres?ssl=true&sslmode=verify-ca&sslcert=${urlencode(abspath(local_file.pgclient_crt.filename))}&sslkey=${urlencode(abspath(local_file.pgclient_key.filename))}&sslrootcert=${urlencode(abspath(local_file.pgserver_crt.filename))}" +} + # Create a workload identity service account for IAM authentication to # cloudsql module "cloudsql_test_sa" { diff --git a/terraform/modules/cloudsql/cloudsql.tf b/terraform/modules/cloudsql/cloudsql.tf index 4059dc3..35d68c0 100644 --- a/terraform/modules/cloudsql/cloudsql.tf +++ b/terraform/modules/cloudsql/cloudsql.tf @@ -92,6 +92,7 @@ resource "google_sql_user" "postgres" { } resource "google_sql_ssl_cert" "client_cert" { + project = var.project common_name = "client-name" instance = google_sql_database_instance.instance.name } From c3e5b70a84ab4fe6b0a317ad6969b84ee102c693 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 18 Jul 2021 22:11:22 -0400 Subject: [PATCH 11/11] Add require_tls param to cloudsql module. --- terraform/modules/cloudsql/cloudsql.tf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/terraform/modules/cloudsql/cloudsql.tf b/terraform/modules/cloudsql/cloudsql.tf index 35d68c0..9d85a5f 100644 --- a/terraform/modules/cloudsql/cloudsql.tf +++ b/terraform/modules/cloudsql/cloudsql.tf @@ -33,6 +33,12 @@ variable "postgres_password" { default = "hunter2" } +variable "require_tls" { + description = "Whether or not we should require TLS when connecting to cloudsql." + type = bool + default = false +} + output "connection_name" { description = "The connection string for connecting to the cloudsql instance (for example, through cloudsql proxy)." value = google_sql_database_instance.instance.connection_name @@ -71,7 +77,7 @@ resource "google_sql_database_instance" "instance" { ip_configuration { ipv4_enabled = false private_network = var.private_network_id - require_ssl = true + require_ssl = var.require_tls } database_flags {