Scripts: Configure Wordpress wp-config.php using AWS Secrets Manager Script
Wordpress Script Description
This script was designed to run on freshly deployed Wordpress server. Its function is to make a request to the AWS Secrets Manager to get the proper database credentials, and then using the /var/www/html/wordpress/wp-config-sample.php file as a base, it will write a new /var/www/html/wordpress/wp-config.php file inputing the credentials stored in the Secrets Manager directly into the wp-config.php file. This negates the necessity of having a hard coded username and password sitting in the AMI. The script performs the following actions:
1. Environment Check:
When the Wordpress instance is instantiated, the following user-data is executed:
#cloud-boothook #!/bin/bash sudo echo "STAGE" | sudo tee --append /etc/environment sudo systemctl stop httpd sudo python3 /tmp/wp_config_secrets_manager.py sudo rm -fr /tmp/wp_config_secrets_manager.py sudo chown -R apache:apache /var/www/html/wordpress sudo chmod 664 /var/www/html/wordpress/.htaccess sed -i ':a;N;$!ba;s/AllowOverride\ None/AllowOverride\ ALL/2' /etc/httpd/conf/httpd.conf sudo systemctl start httpd sudo setenforce 0
The purpose of this user-data script is to set an environment type that the python script below will utilize to determine the proper deployment environment and type. In this case, the value of STAGE is written to the /etc/environment file. Apache is then stopped, this python script, which gathers the database credentials and writes the wp-config.php file is executed, and then removed. Next, permissions are set on the /var/www/html/wordpress directory, and the AllowOverride ALL apache directive is set. Last Apache is restarted, and SELinux is disabled.
1. get_env() Function:
Once executed the first operation that the script performs is to run the get_env() function.
# Determine the environment type that we are configuring wp_envion = get_env()
This function will read the contents of /etc/environment, and pull the value being set to DEV, STAGE or PROD. Based on the environment type set by the user-data script, the SecretId to query from the Secrets Manager service is retrieved, and then returned back to the calling variable. In this case after the function execution, wp_envion would contain Wordpress/Stage
as the value.
2. get_secret() Function:
Next, the script will pass the returned value, containing the SecretId (Wordpress/Stage
) to the get_secret() function.
# Get credentials from Creds store: cred_response = get_secret(wp_envion) wp_user = cred_response['username'] wp_pass = cred_response['password'] wp_db_name = cred_response['dbname'] wp_host = cred_response['host']
The get_secret() function will make the request to the Secret Manager service for the secrets key:value payload specified by the returned get_env() SecretId value (Wordpress/Stage
). It will take the response of that request, being a JSON object that contains the returned secrets stored values along with the secrets meta-data, and it will pull the SecretString key:value pair from the object, which contains the unencrypted values of all key:pairs stored in the returned Secrets Manager Secret, jsonify it, and return the new JSON object that now only contains the SecretString data. The SecretString data is simply a JSON object containing all of the secret key value pairs that were retrieved from the Secrets Manager Service. Next the script will parse the cred_response variable that now contains that JSON object and will parse it to set the values of the wp_user, wp_pass, wp_db_name, and wp_host variables based on the returned payload. After this functions execution cred_response would contain:
{ "username": "secret_username_value", "password": "secret_password_value", "dbname": "database_name", "host": "host_name" }
The wp_user variable would contain just the username value being secret_username_value
, wp_password would contain secret_password_value
, etc.. These values all correlate back to the object that is stored in the Secrets Manager Service
3. wp_config() Function:
Last, the script will pass the wp_user, wp_pass, wp_db_name and wp_host values, to the wp_config() function.
# Write the new config write_config = wp_config(wp_user, wp_pass, wp_db_name, wp_host)
This function will read the /var/www/html/wordpress/wp-config-sample.php file, and line by line write each read line to a new file located in /var/www/html/wordpress/wp-config.php, replacing the username, password, database_name, and database_host values collected from the Secrets Manager service, directly into the new wp-config.php file. At this point the script execution would halt successfully, a new wp-config.php will have been written to the Wordpress directory, and when apache is restarted via the user-data script, Wordpress should be up, running, and properly connected to its database.
Python Script
import boto3 # Required to interact with AWS import json # Required for return object parsing import re # Required for line substitution in config parsing from botocore.exceptions import ClientError def get_env(): '''Function to determine Environment variable''' # Get the environment type environment_file = "/etc/environment" environment = open(environment_file, "r") for line in environment: local_env = line local_env = str(local_env).upper() # Return the environment type if 'DEV' in local_env: secret_name = 'Wordpress/Dev' elif 'STAGE' in local_env: secret_name = 'Wordpress/Stage' else: secret_name = 'Wordpress/Prod' # Return the environment value: return secret_name def get_secret(secret_name): '''This function will grab the username and password from the secure creds store''' endpoint_url = "https://secretsmanager.us-east-2.amazonaws.com" region_name = "us-east-2" session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name=region_name, endpoint_url=endpoint_url ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: if e.response['Error']['Code'] == 'ResourceNotFoundException': print("The requested secret " + secret_name + " was not found") elif e.response['Error']['Code'] == 'InvalidRequestException': print("The request was invalid due to:", e) elif e.response['Error']['Code'] == 'InvalidParameterException': print("The request had invalid params:", e) else: # Decrypted secret using the associated KMS CMK # Depending on whether the secret was a string or binary, one of these fields will be populated if 'SecretString' in get_secret_value_response: secret = json.loads(get_secret_value_response['SecretString']) return secret else: binary_secret_data = get_secret_value_response['SecretBinary'] return binary_secret_data def wp_config(user, password, db_name, host): '''This function will parse and replace the values in the wp-config files from the values stored in the credentials store''' # Define the input and output files: sample_file = open("/var/www/html/wordpress/wp-config-sample.php", "r") config_file = open("/var/www/html/wordpress/wp-config.php", "w") # sample_file = open("wp-config-sample.php", "r") # config_file = open("wp-config.php", "w") # Now lets edit the sample file and write the new file: for line in sample_file: if 'database_name_here' in line: new_line = re.sub('database_name_here', db_name, line) config_file.write(new_line) elif 'username_here' in line: new_line = re.sub('username_here', user, line) config_file.write(new_line) elif 'password_here' in line: new_line = re.sub('password_here', password, line) config_file.write(new_line) elif 'localhost' in line: new_line = re.sub('localhost', host, line) config_file.write(new_line) else: config_file.write(line) # Determine the environment type that we are configuring wp_envion = get_env() # Get credentials from Creds store: cred_response = get_secret(wp_envion) wp_user = cred_response['username'] wp_pass = cred_response['password'] wp_db_name = cred_response['dbname'] wp_host = cred_response['host'] # Write the new config write_config = wp_config(wp_user, wp_pass, wp_db_name, wp_host)