diff --git a/bin/data-replication b/bin/data-replication new file mode 100755 index 0000000000..015b2c5a88 --- /dev/null +++ b/bin/data-replication @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Data replication script - creates read-only database role and keeps container running + +set -e + +echo "Starting data replication setup..." + +if [ -z "$READ_ONLY_DB_PASSWORD" ]; then + echo "Error: READ_ONLY_DB_PASSWORD environment variable is not set" + exit 1 +fi + +echo "Creating read-only database role..." + +bundle exec rails runner " +begin + ActiveRecord::Base.connection.execute(\" + DO \$\$ + BEGIN + IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'grafana_ro') THEN + CREATE ROLE grafana_ro WITH LOGIN PASSWORD '#{ENV['READ_ONLY_DB_PASSWORD']}'; + ELSE + ALTER ROLE grafana_ro WITH PASSWORD '#{ENV['READ_ONLY_DB_PASSWORD']}'; + END IF; + END + \$\$; + \") + + ActiveRecord::Base.connection.execute(\" + GRANT CONNECT ON DATABASE #{ActiveRecord::Base.connection.current_database} TO grafana_ro; + GRANT USAGE ON SCHEMA public TO grafana_ro; + GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana_ro; + ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO grafana_ro; + \") + + puts 'Read-only role created/updated successfully' +rescue => e + puts \"Error creating read-only role: \#{e.message}\" + exit 1 +end +" + +echo "Data replication setup completed. Keeping container running..." + +exec tail -f /dev/null \ No newline at end of file diff --git a/bin/docker-start b/bin/docker-start index 17fa5448d7..bee134e957 100755 --- a/bin/docker-start +++ b/bin/docker-start @@ -11,10 +11,13 @@ elif [ "$SERVER_TYPE" == "good-job" ]; then elif [ "$SERVER_TYPE" == "sidekiq" ]; then echo "Starting sidekiq server..." exec "$BIN_DIR"/sidekiq +elif [ "$SERVER_TYPE" == "data-replication" ]; then + echo "Starting data replication server..." + exec "$BIN_DIR"/data-replication elif [ "$SERVER_TYPE" == "none" ]; then echo "No server started" exec tail -f /dev/null # Keep container running else - echo "SERVER_TYPE variable: '$SERVER_TYPE' unknown. Allowed values ['web','good-job', 'none']" + echo "SERVER_TYPE variable: '$SERVER_TYPE' unknown. Allowed values ['web','good-job', 'sidekiq', 'data-replication', 'none']" exit 1 fi diff --git a/terraform/data_replication/ecs.tf b/terraform/data_replication/ecs.tf index 8b6cd87b4c..bbfd27f5fa 100644 --- a/terraform/data_replication/ecs.tf +++ b/terraform/data_replication/ecs.tf @@ -26,8 +26,7 @@ module "db_access_service" { subnets = local.subnet_list vpc_id = aws_vpc.vpc.id } - server_type = "none" - server_type_name = "data-replication" + server_type = "data-replication" task_config = { environment = local.task_envs secrets = local.task_secrets diff --git a/terraform/data_replication/iam.tf b/terraform/data_replication/iam.tf index 099c05e31e..33251ce855 100644 --- a/terraform/data_replication/iam.tf +++ b/terraform/data_replication/iam.tf @@ -11,7 +11,8 @@ data "aws_iam_policy_document" "ecs_permissions" { sid = "dbSecretSid" actions = ["secretsmanager:GetSecretValue"] resources = [ - var.db_secret_arn + var.db_secret_arn, + aws_secretsmanager_secret.ro_db_password.arn ] effect = "Allow" } diff --git a/terraform/data_replication/rds.tf b/terraform/data_replication/rds.tf index 0c1515c318..730e9850d1 100644 --- a/terraform/data_replication/rds.tf +++ b/terraform/data_replication/rds.tf @@ -36,7 +36,7 @@ resource "aws_rds_cluster" "cluster" { } lifecycle { - ignore_changes = [cluster_identifier] + ignore_changes = [cluster_identifier, snapshot_identifier] } } diff --git a/terraform/data_replication/ssm_parameters.tf b/terraform/data_replication/ssm_parameters.tf new file mode 100644 index 0000000000..b344279bc3 --- /dev/null +++ b/terraform/data_replication/ssm_parameters.tf @@ -0,0 +1,26 @@ +# Create a password that is automatically populated in secrets manager using the random password generator for aws + +# Generate a random password for the read-only database user +ephemeral "aws_secretsmanager_random_password" "ro_db_password" { +} + +# Store the generated password in AWS Secrets Manager +resource "aws_secretsmanager_secret" "ro_db_password" { + name = "${local.name_prefix}-ro-db-password-${substr(uuid(), 0, 4)}" + description = "Read-only database user password for data replication" + recovery_window_in_days = 7 + + tags = { + Name = "${local.name_prefix}-ro-db-password" + } + lifecycle { + ignore_changes = [name] + replace_triggered_by = [aws_rds_cluster.cluster] + } +} + +resource "aws_secretsmanager_secret_version" "ro_db_password" { + secret_id = aws_secretsmanager_secret.ro_db_password.id + secret_string_wo = ephemeral.aws_secretsmanager_random_password.ro_db_password.random_password + secret_string_wo_version = 1 +} diff --git a/terraform/data_replication/variables.tf b/terraform/data_replication/variables.tf index 7bdea5566e..983580a189 100644 --- a/terraform/data_replication/variables.tf +++ b/terraform/data_replication/variables.tf @@ -124,6 +124,10 @@ locals { { name = "RAILS_MASTER_KEY" valueFrom = var.rails_master_key_path + }, + { + name = "READ_ONLY_DB_PASSWORD" + valueFrom = aws_secretsmanager_secret.ro_db_password.arn } ] }