diff --git a/lamp-ubuntu22.sh b/lamp-ubuntu22.sh
index 78ede35..91ece94 100644
--- a/lamp-ubuntu22.sh
+++ b/lamp-ubuntu22.sh
@@ -165,6 +165,10 @@ REPLACE=${REPLACE//$'\n'/\\n} # Escape the new line characters
REPLACE=${REPLACE//\$/\\$} # Escape the $ characters
perl -pi -e "s/$FIND/$REPLACE/m" /etc/apache2/apache2.conf
+printf "Generating AES-256-CBC encryption key\n"
+ENCRYPTION_KEY=$(openssl rand -base64 32)
+IV=$(openssl rand -base64 16)
+
printf "Adding configuration for /srv/www...\n"
FIND="#<\/Directory>"
REPLACE="$(cat << 'EOF'
@@ -181,6 +185,8 @@ REPLACE="$(cat << 'EOF'
Header set X-Frame-Options sameorigin
Header unset X-Powered-By
Header set X-XSS-Protection "1; mode=block"
+ SetEnv WPCONFIG_ENCKEY ENCRYPTION_KEY
+ SetEnv WPCONFIG_IV ENCRYPTION_IV
# Disable unused HTTP request methods
@@ -189,6 +195,9 @@ REPLACE="$(cat << 'EOF'
EOF
)"
+# Replace the placeholders with actual values
+REPLACE=${REPLACE//ENCRYPTION_KEY/$ENCRYPTION_KEY}
+REPLACE=${REPLACE//ENCRYPTION_IV/$IV}
REPLACE=${REPLACE//\//\\\/} # Escape the / characters
REPLACE=${REPLACE//$'\n'/\\n} # Escape the new line characters
perl -pi -e "s/$FIND/$REPLACE/m" /etc/apache2/apache2.conf
@@ -319,6 +328,41 @@ while true; do
esac
done
+
+# Suggest stg and dev domains
+stg_domain="stg.$domain"
+dev_domain="dev.$domain"
+
+echo "Suggested staging domain: $stg_domain"
+while true; do
+ read -p "Would you like to keep this as your staging domain? [Y/n] " answer
+ case "$answer" in
+ [Yy]*|"") # Accept default or yes
+ break;;
+ [Nn]* ) # Enter a new value
+ read -p "Enter your staging domain: " stg_domain
+ break;;
+ * ) echo "Please answer yes or no.";;
+ esac
+done
+
+echo "Suggested development domain: $dev_domain"
+while true; do
+ read -p "Would you like to keep this as your development domain? [Y/n] " answer
+ case "$answer" in
+ [Yy]*|"") # Accept default or yes
+ break;;
+ [Nn]* ) # Enter a new value
+ read -p "Enter your development domain: " dev_domain
+ break;;
+ * ) echo "Please answer yes or no.";;
+ esac
+done
+
+printf "Main domain: $domain\n"
+printf "Staging domain: $stg_domain\n"
+printf "Development domain: $dev_domain\n"
+
# Get IPv4
IPV4=$(ip -4 addr | grep inet | grep -v '127.0.0.1' | awk -F '[ \t]+|/' '{print $3}' | grep -v ^127.2.1)
@@ -327,6 +371,14 @@ if [ -f /etc/apache2/sites-available/$domain.conf ]; then
printf "Backing up existing virtual host configuration file to /etc/apache2/sites-available/$domain.conf.bak\n"
cp /etc/apache2/sites-available/$domain.conf /etc/apache2/sites-available/$domain.conf.bak
fi
+if [ -f /etc/apache2/sites-available/$stg_domain.conf ]; then
+ printf "Backing up existing virtual host configuration file to /etc/apache2/sites-available/$stg_domain.conf.bak\n"
+ cp /etc/apache2/sites-available/$stg_domain.conf /etc/apache2/sites-available/$stg_domain.conf.bak
+fi
+if [ -f /etc/apache2/sites-available/$dev_domain.conf ]; then
+ printf "Backing up existing virtual host configuration file to /etc/apache2/sites-available/$dev_domain.conf.bak\n"
+ cp /etc/apache2/sites-available/$dev_domain.conf /etc/apache2/sites-available/$dev_domain.conf.bak
+fi
# Production
VIRTUALHOST="
@@ -335,28 +387,43 @@ VIRTUALHOST="
DocumentRoot /srv/www/$domain/public_html/
ErrorLog /srv/www/$domain/logs/error.log
CustomLog /srv/www/$domain/logs/access.log combined
+ SetEnv WPCONFIG_ENVNAME production
\n";
echo -e "$VIRTUALHOST" > /etc/apache2/sites-available/$domain.conf
+# Staging
+VIRTUALHOST="
+ ServerName $stg_domain
+ DocumentRoot /srv/www/$stg_domain/public_html/
+ ErrorLog /srv/www/$stg_domain/logs/error.log
+ CustomLog /srv/www/$stg_domain/logs/access.log combined
+ SetEnv WPCONFIG_ENVNAME staging
+\n";
+echo -e "$VIRTUALHOST" > /etc/apache2/sites-available/$stg_domain.conf
+
# Development
VIRTUALHOST="
- ServerName dev.$domain
- DocumentRoot /srv/www/dev.$domain/public_html/
- ErrorLog /srv/www/dev.$domain/logs/error.log
- CustomLog /srv/www/dev.$domain/logs/access.log combined
+ ServerName $dev_domain
+ DocumentRoot /srv/www/$dev_domain/public_html/
+ ErrorLog /srv/www/$dev_domain/logs/error.log
+ CustomLog /srv/www/$dev_domain/logs/access.log combined
+ SetEnv WPCONFIG_ENVNAME development
\n";
-echo -e "$VIRTUALHOST" > /etc/apache2/sites-available/dev.$domain.conf
+echo -e "$VIRTUALHOST" > /etc/apache2/sites-available/$dev_domain.conf
# Create directories
mkdir -p /srv/www/$domain/public_html
mkdir -p /srv/www/$domain/logs
-mkdir -p /srv/www/dev.$domain/public_html
-mkdir -p /srv/www/dev.$domain/logs
+mkdir -p /srv/www/$stg_domain/public_html
+mkdir -p /srv/www/$stg_domain/logs
+mkdir -p /srv/www/$dev_domain/public_html
+mkdir -p /srv/www/$dev_domain/logs
chown -R www-data:www-data /srv/www
# Enable sites
a2ensite $domain
-a2ensite dev.$domain
+a2ensite $stg_domain
+a2ensite $dev_domain
service apache2 reload
# PHP
@@ -524,6 +591,28 @@ while true; do
* ) break;;
esac
done
+while true; do
+ printf "\n"
+ read -p "Staging database name (recommended: use domain without TLD followed by _stg, for mydomain.com use mydomain_stg): " stgdbname
+ case $stgdbname in
+ "" ) printf "Database name may not be left blank\n";;
+ * ) break;;
+ esac
+done
+while true; do
+ read -p "Staging database user (recommended: use same as database name, max 16 characters): " stgdbuser
+ case $stgdbuser in
+ "" ) printf "User name may not be left blank\n";;
+ * ) break;;
+ esac
+done
+while true; do
+ read -sp "Staging database password: " stgdbpass
+ case $stgdbpass in
+ "" ) printf "\nPassword may not be left blank\n";;
+ * ) break;;
+ esac
+done
while true; do
printf "\n"
read -p "Development database name (recommended: use domain without TLD followed by _dev, for mydomain.com use mydomain_dev): " devdbname
@@ -547,18 +636,52 @@ while true; do
esac
done
-printf "Create database $dbname...\n"
-mysql -u root -p$mysqlrootpsw -e "CREATE DATABASE $dbname;"
-printf "Create user $dbuser...\n"
-mysql -u root -p$mysqlrootpsw -e "CREATE USER '$dbuser'@localhost IDENTIFIED BY '$dbpass';"
-printf "Grant $dbuser all privileges on $dbname...\n"
-mysql -u root -p$mysqlrootpsw -e "GRANT ALL PRIVILEGES ON $dbname.* TO '$dbuser'@localhost;"
-printf "Create database $devdbname...\n"
-mysql -u root -p$mysqlrootpsw -e "CREATE DATABASE $devdbname;"
-printf "Create user $devdbuser...\n"
-mysql -u root -p$mysqlrootpsw -e "CREATE USER '$devdbuser'@localhost IDENTIFIED BY '$devdbpass';"
-printf "Grant $devdbuser all privileges on $devdbname...\n"
-mysql -u root -p$mysqlrootpsw -e "GRANT ALL PRIVILEGES ON $devdbname.* TO '$devdbuser'@localhost;"
+# Declare associative arrays for each environment
+declare -A production=( [dbname]=$dbname [dbuser]=$dbuser [dbpass]=$dbpass [key]="DB_" )
+declare -A staging=( [dbname]=$stgdbname [dbuser]=$stgdbuser [dbpass]=$stgdbpass [key]="STGDB_" )
+declare -A development=( [dbname]=$devdbname [dbuser]=$devdbuser [dbpass]=$devdbpass [key]="DEVDB_" )
+
+# Array of all environments
+environments=(production staging development)
+
+printf $environments
+
+# File to store encrypted credentials
+encrypted_credentials_file="encrypted_credentials.txt"
+key_hex=$(echo -n $ENCRYPTION_KEY | base64 -d | xxd -p -c 32)
+iv_hex=$(echo -n $IV | base64 -d | xxd -p -c 16)
+
+# Loop through each environment
+for env in "${environments[@]}"; do
+ # Dynamically access the associative array for the current environment
+ declare -n current="${env}"
+
+ printf "Create database ${current[dbname]}...\n"
+ mysql -u root -p"$mysqlrootpsw" -e "CREATE DATABASE ${current[dbname]};"
+
+ # Encrypt database name
+ value="${current[dbname]}"
+ encrypted_value=$(echo -n "$value" | openssl enc -aes-256-cbc -a -pbkdf2 -iter 10000 -K $key_hex -iv $iv_hex)
+ echo "${current[key]}NAME=$encrypted_value" >> "$encrypted_credentials_file"
+
+ printf "Create user ${current[dbuser]}...\n"
+ mysql -u root -p"$mysqlrootpsw" -e "CREATE USER '${current[dbuser]}'@'localhost' IDENTIFIED BY '${current[dbpass]}';"
+
+ # Encrypt database user
+ value="${current[dbuser]}"
+ encrypted_value=$(echo -n "$value" | openssl enc -aes-256-cbc -a -pbkdf2 -iter 10000 -K $key_hex -iv $iv_hex)
+ echo "${current[key]}USER=$encrypted_value" >> "$encrypted_credentials_file"
+
+ printf "Grant ${current[dbuser]} all privileges on ${current[dbname]}...\n"
+ mysql -u root -p"$mysqlrootpsw" -e "GRANT ALL PRIVILEGES ON ${current[dbname]}.* TO '${current[dbuser]}'@'localhost';"
+
+ # Encrypt database user
+ value="${current[dbpass]}"
+ encrypted_value=$(echo -n "$value" | openssl enc -aes-256-cbc -a -pbkdf2 -iter 10000 -K $key_hex -iv $iv_hex)
+ echo "${current[key]}PASSWORD=$encrypted_value" >> "$encrypted_credentials_file"
+done
+
+printf "Credentials have been encrypted and stored in $encrypted_credentials_file\n"
printf "Restart MySQL...\n"
service mysql restart
diff --git a/wp-secrets.dist.php b/wp-secrets.dist.php
new file mode 100644
index 0000000..dfa0fe5
--- /dev/null
+++ b/wp-secrets.dist.php
@@ -0,0 +1,100 @@
+~`+=,.;:/?|';
+
+ // Map domains to environment names
+ $environment = [
+ 'example.com' => 'production',
+ 'www.example.com' => 'production',
+ 'stg.example.com' => 'staging',
+ 'dev.example.com' => 'development',
+ 'example.test' => 'local'
+ ];
+
+ // Configuration of all environments
+ // - use arrays to map environments to different values
+ // - use strings when the value doesn't change between environments
+ // 'local' environment values, DB_HOST and WP_DEBUG_DISPLAY are never encrypted
+ $config = [
+ 'DB_NAME' => [
+ 'production' => '{{DB_NAME}}',
+ 'staging' => '{{STGDB_NAME}}',
+ 'development' => '{{DEVDB_NAME}}',
+ 'local' => 'dbname' // local environment is never encrypted
+ ],
+ 'DB_USER' => [
+ 'production' => '{{DB_USER}}',
+ 'staging' => '{{STGDB_USER}}',
+ 'development' => '{{DEVDB_USER}}',
+ 'local' => 'dbuser' // local environment is never encrypted
+ ],
+ 'DB_PASSWORD' => [
+ 'production' => '{{DB_PASSWORD}}',
+ 'staging' => '{{STGDB_PASSWORD}}',
+ 'development' => '{{DEVDB_PASSWORD}}',
+ 'local' => 'dbpassword' // local environment is never encrypted
+ ],
+ // DB_HOST is never encrypted
+ 'DB_HOST' => [
+ 'production' => 'localhost',
+ 'staging' => 'localhost',
+ 'development' => 'localhost',
+ 'local' => 'localhost'
+ ],
+ // WordPress keys and salts
+ 'AUTH_KEY' => '{{AUTH_KEY_ENC}}',
+ 'SECURE_AUTH_KEY' => '{{SECURE_AUTH_KEY_ENC}}',
+ 'LOGGED_IN_KEY' => '{{LOGGED_IN_KEY_ENC}}',
+ 'NONCE_KEY' => '{{NONCE_KEY_ENC}}',
+ 'AUTH_SALT' => '{{AUTH_SALT_ENC}}',
+ 'SECURE_AUTH_SALT' => '{{SECURE_AUTH_SALT_ENC}}',
+ 'LOGGED_IN_SALT' => '{{LOGGED_IN_SALT_ENC}}',
+ 'NONCE_SALT' => '{{NONCE_SALT_ENC}}',
+ // WP_DEBUG_DISPLAY is never encrypted
+ 'WP_DEBUG_DISPLAY' => [
+ 'production' => false,
+ 'staging' => false,
+ 'development' => true,
+ 'local' => true
+ ]
+ ];
+
+ // Determine the current environment
+ // - default to local
+ $env = 'local';
+ if (array_key_exists(strtolower($_SERVER['HTTP_HOST']), $environment)) {
+ $env = $environment[strtolower($_SERVER['HTTP_HOST'])];
+ }
+
+ // Get encryption key
+ $key = base64_decode(getenv('WPCONFIG_ENCKEY'));
+ $iv = base64_decode(getenv('WPCONFIG_IV'));
+
+ // Decrypt secrets (except for local environment)
+ $secrets = [];
+ foreach ($config as $var => $val) {
+ $secrets[$var] = is_array($val) ? $val[$env] : $val;
+ // Decrypt the value
+ if (!in_array($var, ['DB_HOST', 'WP_DEBUG_DISPLAY']) || $env !== 'local') {
+ $secrets[$var] = openssl_decrypt($secrets[$var], "AES-256-CBC", $key, 0, $iv);
+ }
+ // Generate the WordPress keys and salts for local environment
+ if ((strpos($var, '_KEY') !== false || strpos($var, '_SALT') !== false) && $env === 'local') {
+ // Generate a hash of the key name
+ $hash = hash('sha256', $key);
+ $hash .= hash('sha256', $hash);
+
+ // Convert the hash to the allowed characters
+ $secrets[$key] = '';
+ for ($i = 0; $i < 128; $i += 2) {
+ $hex = substr($hash, $i, 2);
+ $index = hexdec($hex) % 92;
+ $secrets[$key] .= $salt_chars[$index];
+ }
+ }
+ }
+
+ return $secrets;
+})();
\ No newline at end of file
diff --git a/wp-secrets.sh b/wp-secrets.sh
new file mode 100644
index 0000000..18abe45
--- /dev/null
+++ b/wp-secrets.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+
+# wp-secrets.php Setup Script
+# https://github.com/Lyquix/ubuntu-lamp
+
+# Check if script is being run by root
+if [[ $EUID -ne 0 ]]; then
+ printf "This script must be run as root!\n"
+ exit 1
+fi
+
+DIVIDER="\n***************************************\n\n"
+
+# Welcome and instructions
+printf $DIVIDER
+printf "Lyquix wp-secrets.php Setup Script\n"
+printf $DIVIDER
+
+# Prompt to continue
+while true; do
+ read -p "Continue [Y/N]? " cnt1
+ case $cnt1 in
+ [Yy]* ) break;;
+ [Nn]* ) exit;;
+ * ) printf "Please answer Y or N\n";;
+ esac
+done
+
+CREDENTIALS_FILE="./encrypted_credentials.txt"
+# Path to the apache2.conf file
+apache_conf="/etc/apache2/apache2.conf"
+
+# Extract the ENCRYPTION_KEY
+ENCRYPTION_KEY=$(grep -oP 'SetEnv WPCONFIG_ENCKEY \K.*' $apache_conf)
+
+# Extract the ENCRYPTION_IV
+ENCRYPTION_IV=$(grep -oP 'SetEnv WPCONFIG_IV \K.*' $apache_conf)
+
+while IFS='=' read -r key value; do
+ case "$key" in
+ DB_NAME) DB_NAME="$value" ;;
+ DB_USER) DB_USER="$value" ;;
+ DB_PASSWORD) DB_PASSWORD="$value" ;;
+ STGDB_NAME) STGDB_NAME="$value" ;;
+ STGDB_USER) STGDB_USER="$value" ;;
+ STGDB_PASSWORD) STGDB_PASSWORD="$value" ;;
+ DEVDB_NAME) DEVDB_NAME="$value" ;;
+ DEVDB_USER) DEVDB_USER="$value" ;;
+ DEVDB_PASSWORD) DEVDB_PASSWORD="$value" ;;
+ esac
+done < encrypted_credentials.txt
+
+SALTS=$(curl -s https://api.wordpress.org/secret-key/1.1/salt/)
+
+# Declare an associative array to hold the salt names
+declare -A salt_names=(
+ [AUTH_KEY]="AUTH_KEY"
+ [SECURE_AUTH_KEY]="SECURE_AUTH_KEY"
+ [LOGGED_IN_KEY]="LOGGED_IN_KEY"
+ [NONCE_KEY]="NONCE_KEY"
+ [AUTH_SALT]="AUTH_SALT"
+ [SECURE_AUTH_SALT]="SECURE_AUTH_SALT"
+ [LOGGED_IN_SALT]="LOGGED_IN_SALT"
+ [NONCE_SALT]="NONCE_SALT"
+)
+
+key_hex=$(echo -n $ENCRYPTION_KEY | base64 -d | xxd -p -c 32)
+iv_hex=$(echo -n $ENCRYPTION_IV | base64 -d | xxd -p -c 16)
+
+# Loop through the salt names and process each one
+for salt in "${!salt_names[@]}"; do
+ # Extract the salt value
+ value=$(echo "$SALTS" | grep "$salt" | awk -F"'" '{print $4}')
+
+ # Encrypt the value
+ encrypted_value=$(echo $value | openssl enc -aes-256-cbc -a -pbkdf2 -iter 10000 -K $key_hex -iv $iv_hex)
+
+ # Dynamically assign the encrypted value to the variable intended for the placeholder
+ declare ${salt_names[$salt]}="$encrypted_value"
+done
+
+# Path to your template and the output file
+template_file="./wp-secrets.dist.php"
+output_file="./wp-secrets.php"
+
+# Update the placeholders associative array with the newly assigned encrypted salt values
+declare -A placeholders=(
+ ["{{DB_NAME}}"]=$DB_NAME
+ ["{{DB_USER}}"]=$DB_USER
+ ["{{DB_PASSWORD}}"]=$DB_PASSWORD
+ ["{{STGDB_NAME}}"]=$STGDB_NAME
+ ["{{STGDB_USER}}"]=$STGDB_USER
+ ["{{STGDB_PASSWORD}}"]=$STGDB_PASSWORD
+ ["{{DEVDB_NAME}}"]=$DEVDB_NAME
+ ["{{DEVDB_USER}}"]=$DEVDB_USER
+ ["{{DEVDB_PASSWORD}}"]=$DEVDB_PASSWORD
+ ["{{AUTH_KEY_ENC}}"]=$AUTH_KEY
+ ["{{SECURE_AUTH_KEY_ENC}}"]=$SECURE_AUTH_KEY
+ ["{{LOGGED_IN_KEY_ENC}}"]=$LOGGED_IN_KEY
+ ["{{NONCE_KEY_ENC}}"]=$NONCE_KEY
+ ["{{AUTH_SALT_ENC}}"]=$AUTH_SALT
+ ["{{SECURE_AUTH_SALT_ENC}}"]=$SECURE_AUTH_SALT
+ ["{{LOGGED_IN_SALT_ENC}}"]=$LOGGED_IN_SALT
+ ["{{NONCE_SALT_ENC}}"]=$NONCE_SALT
+)
+
+# Prepare the output file by clearing its contents or creating it if it doesn't exist
+> "$output_file"
+
+# Read template and replace placeholders
+while IFS= read -r line || [[ -n "$line" ]]; do
+ for placeholder in "${!placeholders[@]}"; do
+ line="${line//${placeholder}/${placeholders[$placeholder]}}"
+ done
+ # Append the processed line to the output file
+ echo "$line" >> "$output_file"
+done < "$template_file"
+
+printf "wp-secrets.php has been generated successfully.\n"