diff --git a/.gitignore b/.gitignore index cc61db5..135a083 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ VpnCerts # ignore tfenv pinned version file .terraform-version + +/.db_connection* diff --git a/Makefile b/Makefile index c0540ad..ad49c26 100644 --- a/Makefile +++ b/Makefile @@ -120,8 +120,20 @@ output: ## terraform output (make output OUTPUT_ARGUMENT='--raw dns_dhcp_vpc_id' output-bastion-rds-admin: ## terraform output (make output-bastion-rds-admin) $(DOCKER_RUN) /bin/bash -c "terraform output -no-color -json rds_bastion | jq -r .admin[][]" -.PHONY: output-bastion-rds-server -output-bastion-rds-server: ## terraform output (make output-bastion-rds-server) +.PHONY: rds-admin +rds-admin: ## Get RDS admin connection details (make rds-admin) + $(DOCKER_RUN) /bin/bash -c "./scripts/create_db_connection_details.sh admin" + +.PHONY: rds-admin-password +rds-admin-password: ## Get RDS admin password (make rds-admin-password) + $(DOCKER_RUN) /bin/bash -c "./scripts/get_db_credentials.sh admin" + +.PHONY: instanceid-bastion-rds-admin +instanceid-bastion-rds-admin: ## Get RDS Admin bastion Instance ID (make instanceid-bastion-rds-admin) + $(DOCKER_RUN) /bin/bash -c "terraform output -no-color -json rds_bastion | jq -r .admin[][]" + +.PHONY: instanceid-bastion-rds-server +instanceid-bastion-rds-server: ## Get RDS server bastion Instance ID (make instanceid-bastion-rds-server) $(DOCKER_RUN) /bin/bash -c "terraform output -no-color -json rds_bastion | jq -r .server[][]" .PHONY: apply diff --git a/documentation/rds-bastion.md b/documentation/rds-bastion.md index c24d0ea..b693302 100644 --- a/documentation/rds-bastion.md +++ b/documentation/rds-bastion.md @@ -1,5 +1,7 @@ # RDS Bastion +# RDS Bastion + In order to carry out various maintenance tasks such as obtaining a database dump for loading into a local development DB; or obtain data that currently isn't available via an export mechanism; a bastion is created. The bastion doesn't have any service exposed to the public like a "jump box" bastion e.g. SSH on port 22 as it is only accessible via the AWS Session Manager. @@ -8,14 +10,21 @@ The routine is - Enable - - Enable the bastion via an "enable" flag set in AWS SSM Parameter Store to `true`. - - Deploy by running the CI pipeline. - - Create an SSM Session. - - Carry out required procedure + - Spin up a bastion + - Enable the bastion via an "enable" flag set in AWS SSM Parameter Store to `true`. + - Deploy by running the CI pipeline. - Configure - - Simple set up to enable assuming a role + - Prepare Terraform locally for the environment. + +- Action + + - Create an SSM Session. + - Retrieve connection details. + - Carry out required procedure. + - Get DB Dump. + - Query DB. - Removal - Disallow the bastion via an "enable" flag set in AWS SSM Parameter Store to `false`. @@ -25,15 +34,36 @@ The routine is ### Spin up a bastion -Set the boolean value in parameter store to `true` -run the pipeline +Navigate to the ssm parameter store in the Shared Services AWS account. +Set the boolean value for +Set the boolean value for + +- NAC Admin DB: `/moj-network-access-control/{environment}/enable_rds_admin_bastion` in [AWS SSM Parameter Store](https://eu-west-2.console.aws.amazon.com/systems-manager/parameters/?region=eu-west-2&tab=Table) to `true` +- Run the `network-access-control-infrastructure` [pipeline](https://eu-west-2.console.aws.amazon.com/codesuite/codepipeline/pipelines/network-access-control-infrastructure/view?region=eu-west-2) to create the bastion instance. + +## Configure -### Get environment details for the target env +### Prepare Terraform locally for the environment. + +We will need to query the Terraform state for the environment we need to run the init command, which will get then necessary env vars and terraform providers and modules. +For the `development` environment we do not need to add an ENV_ARGUMENT + +``` +make clean +make init +make init +``` + +For pre-production and production we do add the ENV_ARGUMENT as shown below. ``` -make gen-env ENV_ARGUMENT=production +make clean +make init ENV_ARGUMENT=production +make init ENV_ARGUMENT=production ``` +## Action + ### run the script to identify the bastion instance id ``` @@ -54,9 +84,13 @@ Run make command with instance id make aws_ssm_start_session INSTANCE_ID=i-019174128cf7b4563 ``` -## Configure +When the SSM session starts issue `sudo su -` command. + +### Configure -First we need to enable an AWS role to transfer files to (or from) an S3 transfer bucket. +The bastions are now configured at deployment time with the following AWS role to transfer files to (or from) an S3 transfer bucket. + +Should this not be the case for any reason here is how ``` ####################### @@ -81,50 +115,40 @@ aws sts get-caller-identity then access to the s3 bucket ``` -aws s3 ls s3://mojo-file-transfer/ --profile s3-role; +aws s3 ls s3://mojo-file-transfer/ --profile s3-role ``` -## Get a DB dump - from another terminal window in the root of the project run -```shell -make shell -``` +## Get a DB dump -the issue a terraform command to get the database details +In order to connect to the database the following items will be needed. -Admin (dhcp & dns) +- fqdn e.g. `"fqdn": "dhcp-dns-admin-dhcp-db.dev.staff.service.justice.gov.uk",` +- username e.g. `"username": "adminuser"` +- password -```shell -terraform output -json terraform_outputs | jq '.admin.db' -``` +### Retrieve connection details -DHCP +Connection strings for testing connectivity and accessing the DBs are described below, however you can obtain ready baked dynamically created versions by running: ```shell -terraform output -json terraform_outputs | jq '.dhcp.db' +make rds-admin ``` -Admin (NAC)\* note: NAC code used `rds` as module name. +To get the password run ```shell -terraform output -json terraform_outputs | jq '.admin.rds' +make rds-admin-password ``` -To get the password run +A file will be created and shown on the terminal with all the correct details retrieved from Terraform outputs for the environment. You can view that file at any time it will be named `.db_connection.{ENV}.admin`. ```shell -./scripts/get_db_parameters.sh +cat .db_connection.{ENV}.admin ``` -## DHCP Database Backup and Restore - -In order to connect to the database the following items will be needed. - -- fqdn e.g. `"fqdn": "dhcp-dns-admin-dhcp-db.dev.staff.service.justice.gov.uk",` -- username e.g. `"username": "adminuser"` -- password +> Do Not Copy Paste examples below. Use the generated file. ### Test connection @@ -180,7 +204,9 @@ show tables; ### Get a DB dump -Create a timestamped database dump and upload it to S3 transfer bucket (copy and paste as below, update variable values as required.) +Create a timestamped database dump and upload it to S3 transfer bucket (copy and paste from your local `.db_connection.{env}.admin` file). + +Example below for illustration only. ```shell env="DEVELOPMENT"; \ diff --git a/scripts/create_db_connection_details.sh b/scripts/create_db_connection_details.sh new file mode 100755 index 0000000..798841d --- /dev/null +++ b/scripts/create_db_connection_details.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +db_type=${1} +file_name=".db_connection.${ENV}.${db_type}" +terraform_outputs=$(terraform output -json) + +if [ ${db_type} == "admin" ]; then + ## Admin RDS + admin_db_username=admin + admin_db_fqdn=$(echo ${terraform_outputs} | jq -r '.terraform_outputs.value.admin.rds.fqdn') + admin_db_port=$(echo ${terraform_outputs} | jq -r '.terraform_outputs.value.admin.rds.port') + admin_db_name=$(echo ${terraform_outputs} | jq -r '.terraform_outputs.value.admin.rds.name') + +cat << EOF > ./${file_name} +Connections strings for ${ENV} environment RDS + +NAC Admin RDS: +Test connection: +Copy command below to test RDS DB access from Admin RDS Bastion. +---- +curl -v telnet://${admin_db_fqdn}:${admin_db_port} --output rds.admin.txt + + + +Connect to DB with MySQL client: +Copy command below to test RDS DB access from Admin RDS Bastion. +----- +mysql --user=${admin_db_username} --host=${admin_db_fqdn} --port=${admin_db_port} --password + + +Create DB dump and push to S3 +-------- +filename="\`date "+%Y_%m_%d-%H_%M_%S"\`_${ENV}_${admin_db_name}_rds-dump.sql"; \\ +mysqldump \\ + -u "${admin_db_username}" \\ + -p \\ + --set-gtid-purged=OFF \\ + --triggers --routines --events \\ + -h "${admin_db_fqdn}" \\ + "${admin_db_name}" > ~/\${filename}; \\ + ls -al; \\ +aws s3 cp ~/\${filename} s3://mojo-file-transfer/ --profile s3-role; \\ +aws s3 ls s3://mojo-file-transfer/ --profile s3-role; + +EOF +fi + +cat ./${file_name} diff --git a/scripts/get_db_credentials.sh b/scripts/get_db_credentials.sh new file mode 100755 index 0000000..c34b2be --- /dev/null +++ b/scripts/get_db_credentials.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +aws_secretsmanager_get_secret_value() { + db_type=${1} + + if [ ${db_type} == "admin" ]; then + aws secretsmanager get-secret-value \ + --secret-id /moj-network-access-control/${ENV}/admin/db | jq --raw-output '.SecretString' | jq -r .password + aws secretsmanager get-secret-value \ + --secret-id /moj-network-access-control/${ENV}/admin/db | jq --raw-output '.SecretString' | jq -r .username + fi +} + +assume_role_in_environment() { + export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \ + $(aws sts assume-role \ + --role-arn "${TF_VAR_assume_role}" \ + --role-session-name MySessionName \ + --query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]" \ + --output text)) +} + +main() { + assume_role_in_environment + aws_secretsmanager_get_secret_value "${1}" +} + +main "${1}" diff --git a/scripts/get_db_parameters.sh b/scripts/get_db_parameters.sh deleted file mode 100755 index db7f486..0000000 --- a/scripts/get_db_parameters.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -export PARAM=$(aws ssm get-parameters --region eu-west-2 --with-decryption --names \ - "/moj-network-access-control/$ENV/admin_db_username" \ - "/moj-network-access-control/$ENV/admin_db_password" \ - --query Parameters) - -echo $ENV -echo $PARAM - -declare -A params - -params["admin_db_password"]="$(echo $PARAM | jq '.[] | select(.Name | test("admin_db_password")) | .Value' --raw-output)" -params["admin_db_username"]="$(echo $PARAM | jq '.[] | select(.Name | test("admin_db_username")) | .Value' --raw-output)" - - -for key in "${!params[@]}" -do - echo "${key}=${params[${key}]}" -done