#!/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 < .htaccess <<'EOL' # Protect this file Order allow,deny Deny from all # Prevent directory listing Options -Indexes ## BEGIN 6G Firewall from https://perishablepress.com/6g/ # 6G:[QUERY STRINGS] 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] # 6G:[REQUEST METHOD] RewriteCond %{REQUEST_METHOD} ^(connect|debug|delete|move|put|trace|track) [NC] RewriteRule .* - [F] # 6G:[REFERRERS] RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000}) [NC,OR] RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC] RewriteRule .* - [F] # 6G:[REQUEST STRINGS] 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)/(\$(\&)?|\*|\"|\.|,|&|&?)/?$ 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 # 6G:[USER AGENTS] 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 Order Allow,Deny Allow from All Deny from env=bad_bot # 6G:[BAD IPS] Order Allow,Deny Allow from All # uncomment/edit/repeat next line to block IPs # Deny from 123.456.789 ## 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 # # AuthType Basic # AuthUserFile /etc/apache2/wp-login # AuthName "Please Authenticate" # Require valid-user # # Exclude the file upload and WP CRON scripts from authentication Satisfy Any Order allow,deny Allow from all Deny from none ## END htauth ## BEGIN WP file protection order allow,deny deny from all # WP includes directories 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] ## 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 Order Deny,Allow Deny from All # whitelist file extensions to prevent executables being # accessed if they get uploaded order deny,allow deny from all allow from all 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 --admin_email=admin@admin.com --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