Difference between revisions of ""Secure" Word Press on Amazon Linux"
Jump to navigation
Jump to search
Michael.mast (talk | contribs) |
Michael.mast (talk | contribs) |
||
(16 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | + | WORK IN PROGRESS: Amazon only allows ssh access without a console, you can easily get yourself locked out.<br> | |
− | + | ==Purpose== | |
− | + | To run a simple wordpress site as "securely" as possible from the server side. | |
− | + | ==Process== | |
− | + | ==Non SELinux== | |
− | + | When this was written, we started using PHP 7 and needed Apache 2.4. Neither of these are standard installs for Amazon Linux. | |
− | <ref>https://www. | + | ===OS Prep=== |
+ | ====Create SWAP==== | ||
+ | *Create SWAP file (Cloud hosted instances lack these). The following will create a 6GB swap file.<ref>https://www.centos.org/docs/5/html/5.2/Deployment_Guide/s2-swap-creating-file.html</ref> | ||
+ | <pre> | ||
+ | sudo dd if=/dev/zero of=/SWAP bs=1024 count=6291453 | ||
+ | sudo chmod 0600 /SWAP | ||
+ | sudo mkswap /SWAP | ||
+ | sudo swapon /SWAP | ||
+ | </pre> | ||
+ | *Add the swap file to FSTAB | ||
+ | <pre> | ||
+ | /SWAP swap swap defaults 0 0 | ||
+ | </pre> | ||
+ | |||
+ | ====Install packages==== | ||
+ | Feel free to install updated php, this section will be updated as further testing is done. Also note the time zone if you are on the east coast. | ||
<pre> | <pre> | ||
sudo yum -y update | sudo yum -y update | ||
− | sudo yum -y install | + | sudo yum -y install fail2ban httpd24 mysql mysql-server php70 php70-mysqlnd mod24_ssl yum-cron php70-mbstring php70-zip php-xml htop |
+ | sudo yum -y install https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_x86_64.rpm | ||
sudo ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime | sudo ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime | ||
− | sudo sed -i 's/ | + | sudo sed -i 's/update_cmd\ =\ default/update_cmd\ =\ security/' /etc/yum/yum-cron.conf |
− | + | </pre> | |
− | + | ||
+ | ====Hide Apache\PHP==== | ||
+ | <pre> | ||
sudo rm -f /etc/httpd/conf.d/welcome.conf | sudo rm -f /etc/httpd/conf.d/welcome.conf | ||
− | sudo sed -i 's/expose_php\ =\ On/expose_php\ =\ off/' /etc/php.ini | + | sudo sed -i 's/expose_php\ =\ On/expose_php\ =\ off/; s/upload_max_filesize\ =\ 2M/upload_max_filesize\ =\ 128M/; s/post_max_size\ =\ 8M/post_max_size\ =\ 64M/; s/max_execution_time\ =\ 30/max_execuion_time\ =\ 60/' /etc/php.ini |
+ | </pre> | ||
+ | *Append /etc/httpd/conf/httpd.conf with | ||
+ | <pre> | ||
+ | ServerTokens Prod | ||
+ | ServerSignature Off | ||
+ | LoadModule deflate_module modules/mod_deflate.so | ||
+ | </pre> | ||
+ | ====Startup Services==== | ||
+ | *Make sure services are set to start at boot, then reboot. | ||
+ | <pre> | ||
sudo chkconfig fail2ban on | sudo chkconfig fail2ban on | ||
sudo chkconfig mysqld on | sudo chkconfig mysqld on | ||
sudo chkconfig httpd on | sudo chkconfig httpd on | ||
− | sudo | + | sudo chkconfig yum-cron on |
sudo reboot | sudo reboot | ||
</pre> | </pre> | ||
− | *After | + | ====Configure Database==== |
+ | *After reboot prep the database. Start with the standard secure installation. Password does not need to be super secure as this is not a shared database, or remotely accessible. | ||
<pre> | <pre> | ||
− | sudo | + | sudo mysql_secure_installation |
− | sudo | + | [ec2-user@ip-172-26-8-112 ~]$ sudo mysql_secure_installation |
− | + | ||
+ | Set root password? [Y/n] Y | ||
+ | New password: | ||
+ | Re-enter new password: | ||
+ | |||
+ | Remove anonymous users? [Y/n] Y | ||
+ | |||
+ | Disallow root login remotely? [Y/n] Y | ||
+ | |||
+ | Remove test database and access to it? [Y/n] Y | ||
+ | |||
+ | Reload privilege tables now? [Y/n] Y | ||
</pre> | </pre> | ||
− | * | + | *Create the databse |
<pre> | <pre> | ||
− | |||
mysql -uroot -p | mysql -uroot -p | ||
+ | create database wordpress; | ||
+ | grant all privileges on wordpress.* to 'wordpress'@'localhost' identified by 'password'; | ||
+ | exit | ||
</pre> | </pre> | ||
− | *Grab | + | Append the following to /etc/my.cnf in the mysqld section. |
+ | <pre>max_allowed_packet=32M</pre> | ||
+ | |||
+ | ====vhost Config==== | ||
+ | We do not want to use htaccess as this would allow bad modifications to be made if compromised. Instead we stick to vhost config files (It also performs better). | ||
+ | *Prep for using SSL. The following will provide a self signed cert that can be used for initial configuration of the site. When the URL is known, you will want to replace with a trusted cert. LetsEncrypt is a good resource for this. | ||
+ | *In the following example we have the Google Analytics modules commented out. Make sure to have your Analytics ID ready before enabling.<ref>https://www.modpagespeed.com/doc/filter-insert-ga</ref> | ||
+ | <pre> | ||
+ | sudo mv /etc/httpd/conf.d/ssl.conf ./ | ||
+ | sudo nano /etc/httpd/conf.d/vhost.conf | ||
+ | |||
+ | LoadModule ssl_module modules/mod_ssl.so | ||
+ | Listen 443 | ||
+ | SSLPassPhraseDialog builtin | ||
+ | SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) | ||
+ | SSLSessionCacheTimeout 300 | ||
+ | SSLRandomSeed startup file:/dev/urandom 256 | ||
+ | SSLRandomSeed connect builtin | ||
+ | SSLCryptoDevice builtin | ||
+ | |||
+ | SSLStaplingCache shmcb:/var/cache/httpd/stapling_cache(128000) | ||
+ | <VirtualHost *:80> | ||
+ | RewriteEngine On | ||
+ | RewriteCond %{HTTPS} off | ||
+ | RewriteRule (.*) https://host.tld:443%{REQUEST_URI} | ||
+ | </VirtualHost> | ||
+ | |||
+ | #ModPagespeedEnableFilters insert_ga | ||
+ | #ModPagespeedAnalyticsID <Analytics ID> | ||
+ | |||
+ | <VirtualHost _default_:443> | ||
+ | <If "%{HTTP_HOST} != 'host.tld'"> | ||
+ | Redirect "/" "https://host.tld" | ||
+ | </If> | ||
+ | <Files wp-config.php> | ||
+ | Order allow,deny | ||
+ | Deny from all | ||
+ | </Files> | ||
+ | DocumentRoot /var/www/html/wordpress | ||
+ | ErrorLog logs/ssl_error_log | ||
+ | TransferLog logs/ssl_access_log | ||
+ | LogLevel warn | ||
+ | SSLEngine on | ||
+ | SSLProtocol +TLSv1.2 | ||
+ | SSLHonorCipherOrder on | ||
+ | SSLCipherSuite EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:HIGH:!MEDIUM:!aNULL:!MD5:!SEED:!IDEA | ||
+ | SSLCertificateFile /etc/pki/tls/certs/localhost.crt | ||
+ | SSLCertificateKeyFile /etc/pki/tls/private/localhost.key | ||
+ | SSLUseStapling on | ||
+ | Header always set Strict-Transport-Security "max-age=15638400;" | ||
+ | |||
+ | AddOutputFilterByType DEFLATE text/plain | ||
+ | AddOutputFilterByType DEFLATE text/html | ||
+ | AddOutputFilterByType DEFLATE text/xml | ||
+ | AddOutputFilterByType DEFLATE text/css | ||
+ | AddOutputFilterByType DEFLATE text/javascript | ||
+ | AddOutputFilterByType DEFLATE application/xml | ||
+ | AddOutputFilterByType DEFLATE application/xhtml+xml | ||
+ | AddOutputFilterByType DEFLATE application/rss+xml | ||
+ | AddOutputFilterByType DEFLATE application/javascript | ||
+ | AddOutputFilterByType DEFLATE application/x-javascript | ||
+ | |||
+ | <Directory "/var/www/html/wordpress"> | ||
+ | RewriteEngine On | ||
+ | RewriteBase / | ||
+ | |||
+ | RewriteRule ^index\.php$ - [L] | ||
+ | RewriteCond %{REQUEST_FILENAME} !-f | ||
+ | RewriteCond %{REQUEST_FILENAME} !-d | ||
+ | RewriteRule . /index.php [L] | ||
+ | </Directory> | ||
+ | |||
+ | <filesmatch ".svg$"> | ||
+ | SetOutputFilter DEFLATE | ||
+ | </filesmatch> | ||
+ | |||
+ | ExpiresActive On | ||
+ | <filesMatch ".(svg|css|jpg|jpeg|png|gif|js|ico)$"> | ||
+ | ExpiresDefault A604800 | ||
+ | Header unset Cache-Control | ||
+ | Header unset Vary | ||
+ | </filesMatch> | ||
+ | |||
+ | </VirtualHost> | ||
+ | </pre> | ||
+ | *Update settings with correct url. | ||
+ | sudo sed -i 's/host\.tld/<yourdomainhere>/g' /etc/httpd/conf.d/vhost.conf | ||
+ | *Restart Apache and start the WordPress Installation | ||
+ | sudo apachectl graceful | ||
+ | |||
+ | ====IPTABLES==== | ||
+ | *Change default input to drop, allow new and established ssh and web connections. | ||
+ | <pre> | ||
+ | sudo /sbin/iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT | ||
+ | sudo /sbin/iptables -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT | ||
+ | sudo /sbin/iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT | ||
+ | sudo /sbin/iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT | ||
+ | sudo /sbin/iptables -P INPUT DROP | ||
+ | sudo /sbin/iptables save | ||
+ | </pre> | ||
+ | |||
+ | ===WordPress Server Config=== | ||
+ | *<b>NOTE</b>: Lightsail/EC2 instances do not have static IPs by default. When creating the wordpress site it likes to create database entries based on the current url. You will want to make sure to provide a static IP to the instance before continuing. | ||
+ | *Grab latest WordPress files and place in web directory | ||
<pre> | <pre> | ||
wget https://wordpress.org/latest.tar.gz | wget https://wordpress.org/latest.tar.gz | ||
Line 39: | Line 184: | ||
tar -xf ../latest.tar.gz | tar -xf ../latest.tar.gz | ||
sudo mv wordpress /var/www/html/wordpress | sudo mv wordpress /var/www/html/wordpress | ||
+ | |||
</pre> | </pre> | ||
− | * | + | *Fix permissions |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<pre> | <pre> | ||
− | sudo | + | sudo find /var/www/html/wordpress/ -type d -exec chmod 755 {} \; |
− | sudo | + | sudo find /var/www/html/wordpress/ -type f -exec chmod 644 {} \; |
− | + | sudo find /var/www/html/wordpress/ -maxdepth 1 -type f -exec chmod 444 {} \; | |
− | + | sudo chown -R apache:apache /var/www/html/wordpress | |
− | sudo | ||
</pre> | </pre> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ===WordPress Site Setup=== | |
− | + | *In your browser, open your site using the IP provided by Amazon. "https://xxx.xxx.xxx.xxx" | |
− | # | + | *Enter the database information from before and hit connect. If it fails, make sure you did not have any typos. You can always go back to the server and look at the MySQL history for what you entered. |
− | + | *Apache does not have write access based on the permissions we set earlier, so you will need to create the /var/www/html/wordpress/wp-config.php file with the contents displayed in the browser. | |
− | + | sudo nano /var/www/html/wordpress/wp-config.php | |
− | + | Paste into file, then save/exit | |
− | + | sudo chmod 640 /var/www/html/wordpress/wp-config.php | |
+ | *Enter site Title, admin username (admin), admin password, set email. | ||
+ | *When finished, log in with the credentials you just entered. Make sure to check for updates and that word press can install them. | ||
+ | ====Plugins==== | ||
+ | *Go to "Plugins" -> "Add New". Search for wordfence and install. | ||
+ | =====Wordfence Config===== | ||
+ | *It is important to have this plugin installed, especially if you have third parties managing things. | ||
+ | #Go to "Firewall", "Manage WAF", and under "Web Application Firewall Status" change to "Enabled and Protecting. | ||
+ | #Go to "Firewall", "Manage Brute Force Protection", and change the following | ||
+ | ##Lock out after how many login failures : 5 | ||
+ | ##Lock out after how many forgot passwords attempts : 5 | ||
+ | ##Count failures over what time period : 30 minutes | ||
+ | ##Amount of time a user is locked out : 30 minutes | ||
+ | ##Immediately lock out invalid usernames : Check this box |
Latest revision as of 18:01, 15 February 2019
WORK IN PROGRESS: Amazon only allows ssh access without a console, you can easily get yourself locked out.
Contents
Purpose
To run a simple wordpress site as "securely" as possible from the server side.
Process
Non SELinux
When this was written, we started using PHP 7 and needed Apache 2.4. Neither of these are standard installs for Amazon Linux.
OS Prep
Create SWAP
- Create SWAP file (Cloud hosted instances lack these). The following will create a 6GB swap file.[1]
sudo dd if=/dev/zero of=/SWAP bs=1024 count=6291453 sudo chmod 0600 /SWAP sudo mkswap /SWAP sudo swapon /SWAP
- Add the swap file to FSTAB
/SWAP swap swap defaults 0 0
Install packages
Feel free to install updated php, this section will be updated as further testing is done. Also note the time zone if you are on the east coast.
sudo yum -y update sudo yum -y install fail2ban httpd24 mysql mysql-server php70 php70-mysqlnd mod24_ssl yum-cron php70-mbstring php70-zip php-xml htop sudo yum -y install https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_x86_64.rpm sudo ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime sudo sed -i 's/update_cmd\ =\ default/update_cmd\ =\ security/' /etc/yum/yum-cron.conf
Hide Apache\PHP
sudo rm -f /etc/httpd/conf.d/welcome.conf sudo sed -i 's/expose_php\ =\ On/expose_php\ =\ off/; s/upload_max_filesize\ =\ 2M/upload_max_filesize\ =\ 128M/; s/post_max_size\ =\ 8M/post_max_size\ =\ 64M/; s/max_execution_time\ =\ 30/max_execuion_time\ =\ 60/' /etc/php.ini
- Append /etc/httpd/conf/httpd.conf with
ServerTokens Prod ServerSignature Off LoadModule deflate_module modules/mod_deflate.so
Startup Services
- Make sure services are set to start at boot, then reboot.
sudo chkconfig fail2ban on sudo chkconfig mysqld on sudo chkconfig httpd on sudo chkconfig yum-cron on sudo reboot
Configure Database
- After reboot prep the database. Start with the standard secure installation. Password does not need to be super secure as this is not a shared database, or remotely accessible.
sudo mysql_secure_installation [ec2-user@ip-172-26-8-112 ~]$ sudo mysql_secure_installation Set root password? [Y/n] Y New password: Re-enter new password: Remove anonymous users? [Y/n] Y Disallow root login remotely? [Y/n] Y Remove test database and access to it? [Y/n] Y Reload privilege tables now? [Y/n] Y
- Create the databse
mysql -uroot -p create database wordpress; grant all privileges on wordpress.* to 'wordpress'@'localhost' identified by 'password'; exit
Append the following to /etc/my.cnf in the mysqld section.
max_allowed_packet=32M
vhost Config
We do not want to use htaccess as this would allow bad modifications to be made if compromised. Instead we stick to vhost config files (It also performs better).
- Prep for using SSL. The following will provide a self signed cert that can be used for initial configuration of the site. When the URL is known, you will want to replace with a trusted cert. LetsEncrypt is a good resource for this.
- In the following example we have the Google Analytics modules commented out. Make sure to have your Analytics ID ready before enabling.[2]
sudo mv /etc/httpd/conf.d/ssl.conf ./ sudo nano /etc/httpd/conf.d/vhost.conf LoadModule ssl_module modules/mod_ssl.so Listen 443 SSLPassPhraseDialog builtin SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) SSLSessionCacheTimeout 300 SSLRandomSeed startup file:/dev/urandom 256 SSLRandomSeed connect builtin SSLCryptoDevice builtin SSLStaplingCache shmcb:/var/cache/httpd/stapling_cache(128000) <VirtualHost *:80> RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://host.tld:443%{REQUEST_URI} </VirtualHost> #ModPagespeedEnableFilters insert_ga #ModPagespeedAnalyticsID <Analytics ID> <VirtualHost _default_:443> <If "%{HTTP_HOST} != 'host.tld'"> Redirect "/" "https://host.tld" </If> <Files wp-config.php> Order allow,deny Deny from all </Files> DocumentRoot /var/www/html/wordpress ErrorLog logs/ssl_error_log TransferLog logs/ssl_access_log LogLevel warn SSLEngine on SSLProtocol +TLSv1.2 SSLHonorCipherOrder on SSLCipherSuite EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:HIGH:!MEDIUM:!aNULL:!MD5:!SEED:!IDEA SSLCertificateFile /etc/pki/tls/certs/localhost.crt SSLCertificateKeyFile /etc/pki/tls/private/localhost.key SSLUseStapling on Header always set Strict-Transport-Security "max-age=15638400;" AddOutputFilterByType DEFLATE text/plain AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE text/javascript AddOutputFilterByType DEFLATE application/xml AddOutputFilterByType DEFLATE application/xhtml+xml AddOutputFilterByType DEFLATE application/rss+xml AddOutputFilterByType DEFLATE application/javascript AddOutputFilterByType DEFLATE application/x-javascript <Directory "/var/www/html/wordpress"> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </Directory> <filesmatch ".svg$"> SetOutputFilter DEFLATE </filesmatch> ExpiresActive On <filesMatch ".(svg|css|jpg|jpeg|png|gif|js|ico)$"> ExpiresDefault A604800 Header unset Cache-Control Header unset Vary </filesMatch> </VirtualHost>
- Update settings with correct url.
sudo sed -i 's/host\.tld/<yourdomainhere>/g' /etc/httpd/conf.d/vhost.conf
- Restart Apache and start the WordPress Installation
sudo apachectl graceful
IPTABLES
- Change default input to drop, allow new and established ssh and web connections.
sudo /sbin/iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT sudo /sbin/iptables -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT sudo /sbin/iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT sudo /sbin/iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT sudo /sbin/iptables -P INPUT DROP sudo /sbin/iptables save
WordPress Server Config
- NOTE: Lightsail/EC2 instances do not have static IPs by default. When creating the wordpress site it likes to create database entries based on the current url. You will want to make sure to provide a static IP to the instance before continuing.
- Grab latest WordPress files and place in web directory
wget https://wordpress.org/latest.tar.gz mkdir build cd build tar -xf ../latest.tar.gz sudo mv wordpress /var/www/html/wordpress
- Fix permissions
sudo find /var/www/html/wordpress/ -type d -exec chmod 755 {} \; sudo find /var/www/html/wordpress/ -type f -exec chmod 644 {} \; sudo find /var/www/html/wordpress/ -maxdepth 1 -type f -exec chmod 444 {} \; sudo chown -R apache:apache /var/www/html/wordpress
WordPress Site Setup
- In your browser, open your site using the IP provided by Amazon. "https://xxx.xxx.xxx.xxx"
- Enter the database information from before and hit connect. If it fails, make sure you did not have any typos. You can always go back to the server and look at the MySQL history for what you entered.
- Apache does not have write access based on the permissions we set earlier, so you will need to create the /var/www/html/wordpress/wp-config.php file with the contents displayed in the browser.
sudo nano /var/www/html/wordpress/wp-config.php Paste into file, then save/exit sudo chmod 640 /var/www/html/wordpress/wp-config.php
- Enter site Title, admin username (admin), admin password, set email.
- When finished, log in with the credentials you just entered. Make sure to check for updates and that word press can install them.
Plugins
- Go to "Plugins" -> "Add New". Search for wordfence and install.
Wordfence Config
- It is important to have this plugin installed, especially if you have third parties managing things.
- Go to "Firewall", "Manage WAF", and under "Web Application Firewall Status" change to "Enabled and Protecting.
- Go to "Firewall", "Manage Brute Force Protection", and change the following
- Lock out after how many login failures : 5
- Lock out after how many forgot passwords attempts : 5
- Count failures over what time period : 30 minutes
- Amount of time a user is locked out : 30 minutes
- Immediately lock out invalid usernames : Check this box
- ↑ https://www.centos.org/docs/5/html/5.2/Deployment_Guide/s2-swap-creating-file.html
- ↑ https://www.modpagespeed.com/doc/filter-insert-ga