From 8c353af68364da3abb2bed0e307b246c3e15d38b Mon Sep 17 00:00:00 2001 From: Ryan Shatford Date: Wed, 17 Apr 2024 10:41:28 -0700 Subject: [PATCH 1/2] Helm deployment parameters and template updates --- deployment/chart/templates/config.yaml | 21 +++------- deployment/chart/templates/deployment.yaml | 3 +- deployment/chart/values.yaml | 47 +++++++++++++++------- deployment/env/base.yaml | 19 +++++++++ deployment/env/production.yaml | 14 +++++++ 5 files changed, 72 insertions(+), 32 deletions(-) create mode 100644 deployment/env/base.yaml create mode 100644 deployment/env/production.yaml diff --git a/deployment/chart/templates/config.yaml b/deployment/chart/templates/config.yaml index 19f094bc..45035c0d 100644 --- a/deployment/chart/templates/config.yaml +++ b/deployment/chart/templates/config.yaml @@ -3,25 +3,14 @@ kind: ConfigMap metadata: name: {{ .Release.Name }}-config data: - RAILS_ENV: "{{ .Values.ticketsEnvironment }}" - SMTP_ADDRESS: "{{ .Values.ticketsSMTPHost }}" - SMTP_PORT: "{{ .Values.ticketsSMTPPort }}" - SMTP_DOMAIN: "{{ .Values.ticketsSMTPDomain }}" - TICKETS_HOST: "{{ .Values.ticketsHost }}" - ACTION_MAILER_DEFAULT_FROM: "{{ .Values.ticketsEmailAddress }}" - ACTION_MAILER_DEFAULT_TO: "{{ .Values.ticketsEmailAddress }}" - STRIPE_PUBLIC_KEY: "{{ .Values.stripePublicKey }}" - WEB_CONCURRENCY: "{{ .Values.pumaConcurrency }}" - RAILS_MAX_THREADS: "{{ .Values.pumaThreads }}" + {{ .Values.config | nindent 2 }} --- +{{- if .Values.secret.create }} apiVersion: v1 kind: Secret metadata: - name: {{ .Release.Name }}-secrets + name: {{ default {{ .Release.Name }}"-secrets" .Values.secret.name }} type: Opaque stringData: - SECRET_KEY_BASE: "{{ .Values.ticketsSecretToken }}" - RAILS_SERVE_STATIC_FILES: "{{ .Values.ticketsServeStaticFiles }}" - SMTP_USERNAME: "{{ .Values.ticketsSMTPUser }}" - SMTP_PASSWORD: "{{ .Values.ticketsSMTPPassword }}" - STRIPE_SECRET_KEY: "{{ .Values.stripeSecretKey }}" + {{ .Values.secret.data | nindent 2 }} +{{- end }} diff --git a/deployment/chart/templates/deployment.yaml b/deployment/chart/templates/deployment.yaml index 9e19a0f7..b10ec896 100644 --- a/deployment/chart/templates/deployment.yaml +++ b/deployment/chart/templates/deployment.yaml @@ -50,6 +50,7 @@ spec: resources: {{- toYaml .Values.resources | nindent 12 }} env: + # These should really come from a autogenerate value - name: TICKET_DB_HOST value: {{ include "chart.postgresql.fullname" . }} - name: TICKET_DB_PORT @@ -62,7 +63,7 @@ spec: value: {{ .Values.postgresql.auth.password }} envFrom: - secretRef: - name: {{ .Release.Name }}-secrets + name: {{ default {{ .Release.Name }}"-secrets" .Value.secrets.name }} - configMapRef: name: {{ .Release.Name }}-config # Fix for: https://github.com/puma/puma/issues/2343 diff --git a/deployment/chart/values.yaml b/deployment/chart/values.yaml index fa65146d..63835cc9 100644 --- a/deployment/chart/values.yaml +++ b/deployment/chart/values.yaml @@ -71,22 +71,39 @@ tolerations: [] affinity: {} -ticketsEnvironment: "production" -ticketsSecretToken: "this-is-a-very-bogus-key" -ticketsServeStaticFiles: "true" -ticketsSMTPHost: "" -ticketsSMTPPort: "" -ticketsSMTPDomain: "" -ticketsSMTPUser: "" -ticketsSMTPPassword: "" -ticketsDBSeed: "" -ticketsHost: "" -ticketsEmailAddress: "" -stripeSecretKey: "" -stripePublicKey: "" -pumaConcurrency: 2 -pumaThreads: 2 +config: + ticketsEnvironment: "production" + ticketsSMTPHost: "" + ticketsSMTPPort: "" + ticketsSMTPDomain: "" + ticketsDBSeed: "" + ticketsHost: "" + ticketsEmailAddress: "" + stripePublicKey: "" + pumaConcurrency: 2 + pumaThreads: 2 + +secret: + # The secret should never be created with Helm because the values get stored + # someplace. Do this only with non-production resources. + create: false + + # Any secrets should be contained within GCP Se + + # The name of the secret to use. + # Default: {{ .Release.Name }}-secrets + #name: "" + + # Data to put into a created secret. Does not need to be base64 encoded + # data: + # ticketsSecretToken: "this-is-a-very-bogus-key" + # ticketsServeStaticFiles: "true" + # ticketsSMTPUser: "" + # ticketsSMTPPassword: "" + # stripeSecretKey: "" + + postgresql: auth: database: tickets diff --git a/deployment/env/base.yaml b/deployment/env/base.yaml new file mode 100644 index 00000000..8aebaec0 --- /dev/null +++ b/deployment/env/base.yaml @@ -0,0 +1,19 @@ +--- +image: + pullPolicy: Always + +serviceAccount: + create: true + +service: + type: NodePort + port: 3000 + +ingress: + enabled: true + className: nginx + annotations: + kubernetes.io/ingress.allow-http: "false" + # Associate a GCP FrontendConfig with the Ingress + # networking.gke.io/v1beta1.FrontendConfig: "" + diff --git a/deployment/env/production.yaml b/deployment/env/production.yaml new file mode 100644 index 00000000..4caed8da --- /dev/null +++ b/deployment/env/production.yaml @@ -0,0 +1,14 @@ +--- +replicaCount: 3 + +image: + # The tag should be provided on the cmdline to prevent accidental deployments + # of untesed code to production. + tag: null + +serviceAccount: + annotations: + # Any secrets should be contained within GCP managed secrets, so an IAM + # ServiceAccount is needed that has the permissions to read from the named + # secret + iam.gke.io/gcp-service-account: ticket-app-sa@fnf-apps-project.iam.gserviceaccount.com From dfa2b22327abca66d9b993a05a7e05971f64cf22 Mon Sep 17 00:00:00 2001 From: Ryan Shatford Date: Thu, 18 Apr 2024 04:45:55 -0700 Subject: [PATCH 2/2] linted and valiedated the terraform --- .gitignore | 1 + deployment/gcp/iam.tf | 38 ++++++++++++++++++++ deployment/gcp/secrets.tf | 28 +++++++++++++++ deployment/gcp/sql.tf | 72 +++++++++++++++++++++++++++++++++++++ deployment/gcp/terraform.tf | 19 ++++++++++ deployment/gcp/variables.tf | 14 ++++++++ 6 files changed, 172 insertions(+) create mode 100644 deployment/gcp/iam.tf create mode 100644 deployment/gcp/secrets.tf create mode 100644 deployment/gcp/sql.tf create mode 100644 deployment/gcp/terraform.tf create mode 100644 deployment/gcp/variables.tf diff --git a/.gitignore b/.gitignore index df3d2689..dc483c5c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ /vendor/bundle Downloads newrelic_agent.log +.terraform* diff --git a/deployment/gcp/iam.tf b/deployment/gcp/iam.tf new file mode 100644 index 00000000..77b2a531 --- /dev/null +++ b/deployment/gcp/iam.tf @@ -0,0 +1,38 @@ +# IAM roles and permissions for the app to talk to external GCP services +resource "google_project_iam_custom_role" "ticket_booth" { + role_id = "fnfTicketBoothAppRole" + title = "Fnf Ticket Booth App Role" + description = "Role that provides all the permissions needed by the app" + permissions = [ + "cloudsql.databases.get", + "cloudsql.databases.create", + "cloudsql.databases.delete", + "cloudsql.databases.get", + "cloudsql.databases.update", + "cloudsql.instances.connect", + "cloudsql.instances.executeSql", + "iam.serviceAccounts.get", + "iam.serviceAccounts.getAccessToken", + "iam.serviceAccounts.getOpenIdToken", + "iam.serviceAccounts.list", + ] +} + +resource "google_service_account" "ticket_booth_gke" { + account_id = var.project_id + description = "Ticket Booth GKE Service Account" + display_name = "ticket-booth-gke" + + create_ignore_already_exists = true +} + +resource "google_service_account_iam_binding" "ticket_booth_app" { + service_account_id = google_service_account.ticket_booth_gke.name + role = google_project_iam_custom_role.ticket_booth.name + + members = [ + "serviceAccount:${google_service_account.ticket_booth_gke.name}", + ] +} + + diff --git a/deployment/gcp/secrets.tf b/deployment/gcp/secrets.tf new file mode 100644 index 00000000..3eb9b3e9 --- /dev/null +++ b/deployment/gcp/secrets.tf @@ -0,0 +1,28 @@ +# This creates the secret store locations, entries will need to be manually updated + +resource "google_secret_manager_secret" "ticket_booth_db" { + secret_id = "ticket-booth-db" + + replication { + auto {} + } +} + +resource "google_secret_manager_secret_version" "ticket_booth_db" { + secret = google_secret_manager_secret.ticket_booth_db.id + + secret_data = base64encode(jsonencode({ + "TICKET_DB_HOST" = tostring(google_sql_database_instance.ticket_booth.connection_name) + "TICKET_DB_NAME" = tostring(google_sql_database.ticket_booth.name) + "TICKET_DB_USER" = "fnf-breakglass" + "TICKET_DB_PASSWORD" = tostring(random_password.ticket_booth_db.result) + })) +} + +resource "google_secret_manager_secret" "ticket_booth_app" { + secret_id = "ticket-booth-app" + + replication { + auto {} + } +} diff --git a/deployment/gcp/sql.tf b/deployment/gcp/sql.tf new file mode 100644 index 00000000..cc4f941e --- /dev/null +++ b/deployment/gcp/sql.tf @@ -0,0 +1,72 @@ +locals { + db_admins = [ + "evilmonkey@gmail.com", + ] +} + +resource "google_sql_database_instance" "ticket_booth" { + name = "fnf-apps-db-instance" + database_version = "POSTGRES_15" + region = var.location + + settings { + tier = "e2-small" + + # Only allow access from the GKE cluster + ip_configuration { + enable_private_path_for_google_cloud_services = true + # authorized_networks { + # name = "" + # value = "" + # } + } + + database_flags { + name = "cloudsql.iam_authentication" + value = "on" + } + + activation_policy = "ON_DEMAND" + deletion_protection_enabled = true + + backup_configuration { + enabled = true + point_in_time_recovery_enabled = true + + backup_retention_settings { + retained_backups = 10 + } + } + } +} + +resource "google_sql_database" "ticket_booth" { + name = "ticket-booth" + instance = google_sql_database_instance.ticket_booth.name +} + +resource "google_sql_user" "fnf_apps_admin_users" { + for_each = toset(local.db_admins) + name = each.value + instance = google_sql_database_instance.ticket_booth.name + type = "CLOUD_IAM_GROUP" +} + +resource "google_sql_user" "fnf_apps_gke_sa" { + name = trimsuffix(google_service_account.ticket_booth_gke.email, ".gserviceaccount.com") + instance = google_sql_database_instance.ticket_booth.name + type = "CLOUD_IAM_SERVICE_ACCOUNT" +} + +resource "google_sql_user" "breakglass" { + name = "fnf-breakglass" + instance = google_sql_database_instance.ticket_booth.name + password = random_password.ticket_booth_db.result +} + +resource "random_password" "ticket_booth_db" { + length = 20 + special = true + override_special = "!#$%&*()-_=+[]{}<>:?" +} + diff --git a/deployment/gcp/terraform.tf b/deployment/gcp/terraform.tf new file mode 100644 index 00000000..c7fa7ff3 --- /dev/null +++ b/deployment/gcp/terraform.tf @@ -0,0 +1,19 @@ +terraform { + backend "gcs" { + bucket = "fnf-apps-terraform-state" + prefix = "ticket-booth" + } + required_providers { + google = { + source = "hashicorp/google" + version = "5.25.0" + } + } +} + +provider "google" { + project = var.project_id + region = var.location + + impersonate_service_account = var.tf_service_account != "" ? var.tf_service_account : "" +} diff --git a/deployment/gcp/variables.tf b/deployment/gcp/variables.tf new file mode 100644 index 00000000..9b080644 --- /dev/null +++ b/deployment/gcp/variables.tf @@ -0,0 +1,14 @@ +variable "tf_service_account" { + default = "sa-cluster@fnf-apps-341500.iam.gserviceaccount.com" + type = string +} + +variable "project_id" { + default = "fnf-apps-341500" + type = string +} + +variable "location" { + default = "us-central1" + type = string +}