Skip to content

Instantly share code, notes, and snippets.

@phlbnks
Created June 8, 2017 08:52
Show Gist options
  • Save phlbnks/94773378dce06336d05f35a88d88d9dd to your computer and use it in GitHub Desktop.
Save phlbnks/94773378dce06336d05f35a88d88d9dd to your computer and use it in GitHub Desktop.
#!/bin/bash -e
#
# Description:
# This will deploy WordPress in the current directory.
# Without modification it:
# - will configure basic security:
# - remove initial user created
# - deploy 6G firewall in .htaccess
# - attempt to prevent user enumeration in .htaccess
# - protect sensitive files and disallow executables in /wp-uploads
# - presumes a htpasswd file called wp-login is located in /etc/apache2/
# - disallows theme/plugin editor in wp-admin
# - only allows core/plugin/theme updates in wp-admin when using a cookie set using &key=xxx
# - presumes it is a staging environment so sets 'discourage search engines'
# - deletes akismet and hello_dolly plugins
# - deletes the default page and post
# - creates a blank page called Home and sets it as the frontpage
# - sets permalinks to /%postaname%/
# - optionally it:
# - bootstraps an _S theme
# - installs some plugins - advanced-custom-fields autoptimize better-wp-security breadcrumb-navxt broken-link-checker cms-tree-page-view custom-post-type-ui ewww-image-optimizer google-analytics-dashboard-for-wp google-captcha google-sitemap-generator gravity-forms-custom-post-types plugincheck query-monitor redirection re generate-thumbnails simple-local-avatars theme-check wp-fail2ban wp-pagenavi wp-super-cache
# - finally it will delete itself after it finishes.
clear
echo "============================================"
echo "WordPress Install Script"
echo "============================================"
echo "Do you need to setup new MySQL database? (y/n)"
read -e setupmysql
if [ "$setupmysql" == y ] ; then
echo "MySQL Admin User: "
read -e mysqluser
echo "MySQL Admin Password: "
read -s mysqlpass
echo "MySQL Host (Enter for default 'localhost'): "
read -e mysqlhost
mysqlhost=${mysqlhost:-localhost}
fi
echo "WP Database Name: "
read -e dbname
echo "WP Database User: "
read -e dbuser
echo "WP Database Password: "
read -s dbpass
echo "WP Database Table Prefix [numbers, letters, and underscores only] (Enter for default 'wp_'): "
read -e dbtable
dbtable=${dbtable:-wp_}
echo "Key for updating in wp-admin: "
read -e hardenkey
echo "Last chance - sure you want to run the install? (y/n)"
read -e run
if [ "$run" == y ] ; then
if [ "$setupmysql" == y ] ; then
echo "============================================"
echo "Setting up the database."
echo "============================================"
#login to MySQL, add database, add user and grant permissions
dbsetup="create database $dbname;GRANT ALL PRIVILEGES ON $dbname.* TO $dbuser@$mysqlhost IDENTIFIED BY '$dbpass';FLUSH PRIVILEGES;"
mysql -u $mysqluser -p$mysqlpass -e "$dbsetup"
if [ $? != "0" ]; then
echo "============================================"
echo "[Error]: Database creation failed. Aborting."
echo "============================================"
exit 1
fi
fi
# configure wp cli to allow htaccess modification / re-write flushing.
cat > wp-cli.local.yml <<'EOL'
apache_modules:
- mod_rewrite
EOL
echo "============================================"
echo "Downloading WordPress for you."
echo "============================================"
#download wordpress
wp core download --locale=en_GB
echo "+++ Configuring..."
wp core config --dbname=$dbname --dbprefix=$dbtable --dbuser=$dbuser --dbpass=$dbpass --extra-php <<PHP
/** WP_DEBUG setup **/
define( 'WP_DEBUG', false );
if ( WP_DEBUG ) {
define( 'WP_DEBUG_LOG', true );
define( 'SCRIPT_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );
}
/** Disallow theme and plugin editor in admin. Updates only with query var **/
define( 'DISALLOW_FILE_EDIT', true );
if ( $_REQUEST['key'] == "$hardenkey" ) {
setcookie( 'updatebypass', 1 );
} elseif ( ! $_COOKIE['updatebypass'] ) {
define( 'DISALLOW_FILE_MODS', true );
}
PHP
#create uploads folder and set permissions
mkdir -p wp-content/uploads
chmod 775 wp-content/uploads
#remove readme.html
rm readme.html
#create root .htaccess with some useful starters
cat > .htaccess <<'EOL'
# Protect this file
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
# Prevent directory listing
Options -Indexes
## BEGIN 6G Firewall from https://perishablepress.com/6g/
# 6G:[QUERY STRINGS]
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{QUERY_STRING} (eval\() [NC,OR]
RewriteCond %{QUERY_STRING} (127\.0\.0\.1) [NC,OR]
RewriteCond %{QUERY_STRING} ([a-z0-9]{2000}) [NC,OR]
RewriteCond %{QUERY_STRING} (javascript:)(.*)(;) [NC,OR]
RewriteCond %{QUERY_STRING} (base64_encode)(.*)(\() [NC,OR]
RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC,OR]
RewriteCond %{QUERY_STRING} (<|%3C)(.*)script(.*)(>|%3) [NC,OR]
RewriteCond %{QUERY_STRING} (\\|\.\.\.|\.\./|~|`|<|>|\|) [NC,OR]
RewriteCond %{QUERY_STRING} (boot\.ini|etc/passwd|self/environ) [NC,OR]
RewriteCond %{QUERY_STRING} (thumbs?(_editor|open)?|tim(thumb)?)\.php [NC,OR]
RewriteCond %{QUERY_STRING} (\'|\")(.*)(drop|insert|md5|select|union) [NC]
RewriteRule .* - [F]
</IfModule>
# 6G:[REQUEST METHOD]
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_METHOD} ^(connect|debug|delete|move|put|trace|track) [NC]
RewriteRule .* - [F]
</IfModule>
# 6G:[REFERRERS]
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000}) [NC,OR]
RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC]
RewriteRule .* - [F]
</IfModule>
# 6G:[REQUEST STRINGS]
<IfModule mod_alias.c>
RedirectMatch 403 (?i)([a-z0-9]{2000})
RedirectMatch 403 (?i)(https?|ftp|php):/
RedirectMatch 403 (?i)(base64_encode)(.*)(\()
RedirectMatch 403 (?i)(=\\\'|=\\%27|/\\\'/?)\.
RedirectMatch 403 (?i)/(\$(\&)?|\*|\"|\.|,|&|&amp;?)/?$
RedirectMatch 403 (?i)(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")
RedirectMatch 403 (?i)(~|`|<|>|:|;|,|%|\\|\s|\{|\}|\[|\]|\|)
RedirectMatch 403 (?i)/(=|\$&|_mm|cgi-|etc/passwd|muieblack)
RedirectMatch 403 (?i)(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)
RedirectMatch 403 (?i)\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$
RedirectMatch 403 (?i)/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php
</IfModule>
# 6G:[USER AGENTS]
<IfModule mod_setenvif.c>
SetEnvIfNoCase User-Agent ([a-z0-9]{2000}) bad_bot
SetEnvIfNoCase User-Agent (archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune) bad_bot
<limit GET POST PUT>
Order Allow,Deny
Allow from All
Deny from env=bad_bot
</limit>
</IfModule>
# 6G:[BAD IPS]
<Limit GET HEAD OPTIONS POST PUT>
Order Allow,Deny
Allow from All
# uncomment/edit/repeat next line to block IPs
# Deny from 123.456.789
</Limit>
## END 6G Firewall
## BEGIN htauth basic authentication
# STAGING
Require all denied
AuthType Basic
AuthUserFile /etc/apache2/wp-login
AuthName "Please Authenticate"
Require valid-user
# LIVE - prevent wp-login brute force attacks from causing load
#<FilesMatch "^(wp-login|xmlrpc)\.php$">
# AuthType Basic
# AuthUserFile /etc/apache2/wp-login
# AuthName "Please Authenticate"
# Require valid-user
#</FilesMatch>
# Exclude the file upload and WP CRON scripts from authentication
<FilesMatch "(async-upload\.php|wp-cron\.php)$">
Satisfy Any
Order allow,deny
Allow from all
Deny from none
</FilesMatch>
## END htauth
## BEGIN WP file protection
<Files wp-config.php>
order allow,deny
deny from all
</Files>
# WP includes directories
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
# note - comment out next line on multisite
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
## END WP file protection
# Prevent author enumeration
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} author=\d
RewriteRule ^ /? [L,R=301]
EOL
#create .htaccess to protect uploads directory
cat > wp-content/uploads/.htaccess <<'EOL'
# Protect this file
<Files .htaccess>
Order Deny,Allow
Deny from All
</Files>
# whitelist file extensions to prevent executables being
# accessed if they get uploaded
order deny,allow
deny from all
<Files ~ "(?i)\.(docx?|xlsx?|pptx?|txt|pdf|xml|css|jpe?g|png|gif)$">
allow from all
</Files>
EOL
echo "Setup WordPress to a 'standard install'? (y/n)"
read -e setupwp
if [ "$setupwp" == y ] ; then
echo "========================="
echo "Configuring WordPress."
echo "========================="
# useful ref: https://indigotree.co.uk/automated-wordpress-installation-with-bash-wp-cli/
echo "Site URL (no trailing slash): "
read -e siteurl
echo "Site name: "
read -e sitename
wp core install --url="${siteurl}/" --title="$sitename" --admin_user=admin --admin_password=admin [email protected] --skip-email
echo "Is this a sub-directory install? (y/n)"
read -e issubdir
if [ "$issubdir" == y ] ; then
read -e subdir
wp option update "${siteurl}/$issubdir"
fi
echo "+++ Setting up users..."
echo "Admin user name: "
read -e adminname
echo "Admin user email: "
read -e adminemail
wp user create $adminname $adminemail --role=administrator --send-email
echo "+++ Deleting dummy admin user..."
wp user delete admin
echo "+++ Discouraging search engines..."
wp option update blog_public 0
echo "+++ Setting permalinks to /%postaname%/..."
wp rewrite structure '/%postname%/' --hard
wp rewrite flush --hard
echo "+++ Sample page and post deleted when users cleaned; now create empty 'Home' page..."
#echo "+++ Deleting sample page and post; create empty 'Home' page..."
#wp post delete $(wp post list --post_type=page,post --field=ID --format=ids)
wp post create --post_type=page --post_title=Home --post_status=publish
echo "+++ Set frontpage setting to show a page..."
wp option update show_on_front 'page'
echo "+++ Set 'Home' to be the frontpage..."
wp option update page_on_front $(wp post list --post_type=page --pagename=home --field=ID --format=ids)
echo "Install _s theme? (y/n)"
read -e underscores
if [ "$underscores" == y ] ; then
echo "Theme slug: "
read -e themeslug
echo "Theme name: "
read -e themename
echo "Author: "
read -s themeauthor
echo "Author URI: "
read -s themeuri
wp scaffold _s $themeslug --theme_name="$themename" --author="$author" --author_uri="$themeuri" --activate --sassify
fi
echo "Install default plugins? (y/n)"
read -e plugins
if [ "$plugins" == y ] ; then
echo "+++ Deleting Akismet and Hello Dolly..."
wp plugin delete akismet hello
echo "+++ Installing standard plugins..."
wp plugin install advanced-custom-fields autoptimize better-wp-security breadcrumb-navxt broken-link-checker cms-tree-page-view custom-post-type-ui ewww-image-optimizer google-analytics-dashboard-for-wp google-captcha google-sitemap-generator gravity-forms-custom-post-types plugincheck query-monitor redirection regenerate-thumbnails simple-local-avatars theme-check wp-fail2ban wp-pagenavi wp-super-cache
fi
fi
echo "Cleaning..."
#remove bash script if it exists in this dir
[[ -f "wp_cli.sh" ]] && rm "wp_cli.sh"
echo "========================="
echo "[Success]: Installation is complete."
echo "========================="
else
exit
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment