Install LEMP + Wordpress in Ubuntu 16.04 LTS step by step

Install LEMP + Wordpress in Ubuntu 16.04 LTS

User and authentications

Create user

Login as root

1
2
adduser --gecos "" yuxi [OR YOUR_USER_NAME]
gpasswd -a yuxi sudo

OR

1
usermod -aG sudo yuxi

Get your public keys ready, e.g. running “ssh-keygen” in Linux machines which will be connecting to your WP server via SSH.

Login as the new user just being created, and type:

1
2
3
4
mkdir ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Add your public keys to authorized_keys. It can be done manually or run the following command:

1
2
3
cat >> ~/.ssh/authorized_keys << EOF
YOUR_PUBLIC_KEYS
EOF

Switch to root user and run:

1
echo 'YOUR_USER_NAME ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers

System update:

1
2
3
4
5
6
7
8
sudo apt-get -y update
sudo apt-get -y upgrade
```bash
Update user profile
```bash
echo 'export LS_COLORS=$(echo $LS_COLORS | sed "s/di=\(..\);../di=\1;32/") ' >> ~/.bashrc
echo "PS1='\\$ '" >> ~/.bashrc

Authentications

Run

1
sudo nano /etc/ssh/sshd_config

Then make the following changes:

1
2
3
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

Also, change port from default ‘22’ to other number, e.g. 2244

Reload sshd service:

1
sudo systemctl reload sshd

Basic firewall settings

1
2
3
4
sudo ufw enable
sudo ufw delete allow OpenSSH
sudo ufw allow from any to any port 2224
sudo ufw status

System

Basic libraries

1
2
3
4
5
6
7
8
9
10
11
12
sudo apt-get install -qq -y curl
sudo apt-get install -qq -y lynx-cur
sudo apt-get install -qq -y git
sudo apt-get install -qq -y whois
sudo apt-get install -qq -y apache2-utils
sudo apt-get install -qq -y unzip
sudo -H -u root bash -c "echo 'Europe/London' > /etc/timezone"
sudo dpkg-reconfigure -f noninteractive tzdata
sudo apt-get install -qq -y ntp > /dev/null 2>err.log
git config --global user.email "YOUR_EMAIL"
git config --global user.name "YOUR NAME"

Swap space

You may want to create a swap space:

1
2
3
4
5
sudo fallocate -l $SWAP_SPACE_SIZE /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo sh -c 'echo "/swapfile none swap sw 0 0" >> /etc/fstab'

Nginx

1
2
3
sudo apt-get update
sudo apt-get install nginx
sudo ufw allow 'Nginx HTTP'

To find out the IP of VM, run:

1
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

Change server_name in Nginx:

1
nano /etc/nginx/sites-enabled/default

MySQL

1
2
sudo apt-get install mysql-server
sudo mysql_secure_installation

PHP

1
sudo apt-get install -y php-fpm php-mysql

Change PHP configuration:

1
sudo nano /etc/php/7.0/fpm/php.ini

Change this:
cgi.fix_pathinfo=0

1
sudo systemctl restart php7.0-fpm

Change Nginx settings to support PHP:

1
nano /etc/nginx/sites-available/default

With content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name server_domain_or_IP;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}

PHP Extensions

1
2
3
sudo apt-get -y update
sudo apt-get -y install php-curl php-gd php-mbstring php-mcrypt php-xml php-xmlrpc
sudo systemctl restart php7.0-fpm

Wordpress

First, create database for wordpress:

1
2
3
4
5
mysql -u root -p
CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
exit

Modify Nginx configuration:

1
2
3
4
5
6
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { log_not_found off; access_log off; allow all; }
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}

And:

1
2
3
location / {
try_files $uri $uri/ /index.php$is_args$args;
}

Reload:

1
sudo systemctl reload nginx

Install Wordpress

1
2
3
4
5
6
7
cd /tmp
curl -O https://wordpress.org/latest.tar.gz
tar xvf latest.tar.gz
cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php
sudo cp -a /tmp/wordpress/. /var/www/html
mkdir /var/www/html/wp-content/uploads
mkdir /var/www/html/wp-content/upgrade

Change permissions:

1
sudo chown -R yuxi:www-data /var/www/html

Next, we will set the setgid bit on each of the directories within the document root. This causes new files created within these directories to inherit the group of the parent directory (which we just set to www-data) instead of the creating user’s primary group.

1
sudo find /var/www/html -type d -exec chmod g+s {} \;

There are a few other fine-grained permissions we’ll adjust. First, we’ll give group write access to the wp-content directory so that the web interface can make theme and plugin changes:

1
sudo chmod g+w /var/www/html/wp-content

As part of this process, we will give the web server write access to all of the content in these two directories:

1
2
sudo chmod -R g+w /var/www/html/wp-content/themes
sudo chmod -R g+w /var/www/html/wp-content/plugins

When we open the file, our first order of business will be to adjust some secret keys to provide some security for our installation. WordPress provides a secure generator for these values so that you do not have to try to come up with good values on your own.

To grab secure values from the WordPress secret key generator, type:

1
curl -s https://api.wordpress.org/secret-key/1.1/salt/

Open /var/www/html/wp-config.php, update these lines with the keys just being generated:

1
2
3
4
5
6
7
8
define('AUTH_KEY', 'put your unique phrase here');
define('SECURE_AUTH_KEY', 'put your unique phrase here');
define('LOGGED_IN_KEY', 'put your unique phrase here');
define('NONCE_KEY', 'put your unique phrase here');
define('AUTH_SALT', 'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT', 'put your unique phrase here');
define('NONCE_SALT', 'put your unique phrase here');

Config database: change the following lines in /var/www/html/wp-config.php:

1
2
3
define('DB_NAME', 'wordpress');
define('DB_USER', 'wordpressuser');
define('DB_PASSWORD', 'password');

The other change we need to make is to set the method that WordPress should use to write to the filesystem. Since we’ve given the web server permission to write where it needs to, we can explicitly set the filesystem method to “direct”. Failure to set this with our current settings would result in WordPress prompting for FTP credentials when we perform some actions.

Just after DB settings, add:

1
define('FS_METHOD', 'direct');

Complete the Installation via Web UI

WP tool

wp-cli

1
2
3
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

You can install WP if you haven’t done it via Web Admin UI:

1
2
3
4
5
wp core install --url=http://ADDRESS --title="" --admin_user= --admin_password= --admin_email=
wp core language install zh_CN
wp core language activate zh_CN
wp option set blog_public 1

WP plugins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cd /var/www/html
wp plugin delete hello
wp plugin install wordfence
wp plugin activate wordfence
wp plugin install wordpress-seo
wp plugin activate wordpress-seo
wp plugin install google-sitemap-generator
wp plugin activate google-sitemap-generator
wp plugin install disable-google-fonts
wp plugin activate disable-google-fonts
wp plugin install miniorange-2-factor-authentication
wp plugin activate miniorange-2-factor-authentication
wp plugin install nofollow-for-external-link
wp plugin activate nofollow-for-external-link
wp plugin install wp-super-cache
wp plugin activate wp-super-cache
wp plugin update --all

Go to web admin UI to configure these plugins

WP themes

1
2
wp theme delete twentyfifteen
wp theme delete twentysixteen

Install your own themes

Backups

Install Amazon command line tool

1
2
3
4
5
6
7
8
sudo apt-get install -qq -y python-pip
sudo pip install --upgrade pip
sudo pip install -U setuptools
sudo pip -q install awscli
sudo apt-get install -qq -y awscli
mkdir ~/.aws
nano ~/.aws/credentials

Content of credentials:

1
2
3
[default]
aws_access_key_id =
aws_secret_access_key =

nano ~/.aws/config

1
2
[default]
region = eu-central-1

nano ~/scripts/syncbackups.sh

The content of syncbackups.sh is:

1
2
3
4
5
6
7
8
DATE=`date +%Y%m%d`
cd /var/www/html
wp db export ~/backups/db$DATE.sql
cd ~/backups
aws s3 sync . s3://YOUR_S3_BUCKET/YOUR_DB_FOLDER_IN_S3
cd /var/www/html/wp-content/uploads
aws s3 sync . s3://YOUR_S3_BUCKET/YOUR_UPLOAD_FOLDER_IN_S3 --delete

1
chmod 700 ~/scripts/syncbackups.sh

Run crontab -e to add a cron job

Security settings

System updates

nano ~/scripts/systemupdats.sh

1
2
3
cd /var/www/html
wp plugin update --all
wp core update
1
chmod 700 ~/scripts/systemupdats.sh

You may want to add it into cron job list

fails2ban

1
2
sudo apt-get install -qq -y fail2ban
sudo nano /etc/fail2ban/jail.local

Content of jail.local

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[DEFAULT]
bantime = 3600
ignoreip = 127.0.0.1/8 YOUR_IPs
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = %(nginx_error_log)s
[nginx-badbots]
enabled = true
port = http,https
filter = nginx-badbots
logpath = /var/log/nginx/access.log
maxretry = 2
[nginx-noproxy]
enabled = true
port = http,https
filter = nginx-noproxy
logpath = /var/log/nginx/access.log
maxretry = 2
1
sudo nano /etc/fail2ban/filter.d/nginx-http-auth.conf

Update file with:

1
2
failregex = ^ \[error\] \d+#\d+: \*\d+ user "\S+":? (password mismatch|was not found in ".*"), client: <HOST>, server: \S+, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"\s*$
^ \[error\] \d+#\d+: \*\d+ no user/password was provided for basic authentication, client: <HOST>, server: \S+, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"\s*$
1
2
3
sudo cp /etc/fail2ban/filter.d/apache-badbots.conf /etc/fail2ban/filter.d/nginx-badbots.conf
cd /etc/fail2ban/filter.d
sudo nano nginx-nohome.conf

Content of nginx-nohome.conf

1
2
3
4
5
[Definition]
failregex = ^<HOST> -.*GET .*/~.*
ignoreregex =
1
sudo nano nginx-noproxy.conf

Content:

1
2
3
4
5
[Definition]
failregex = ^<HOST> -.*GET http.*
ignoreregex =

Run:

1
sudo service fail2ban restart

Protect WP pages with basic auth

1
2
3
4
5
6
sudo apt-get update
sudo apt-get install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd sammy
sudo htpasswd /etc/nginx/.htpasswd another_user
cat /etc/nginx/.htpasswd

sudo nano /etc/nginx/sites-enabled/default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
location /wp-admin {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
}
location /wp-admin/admin-ajax.php {
allow all;
}
location = /wp-login.php {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
include fastcgi_params;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Disable xml-rpc.php by using plugin:

1
2
3
wp plugin install disable-xml-rpc
wp plugin activate disable-xml-rpc
sudo service nginx restart

Disable ping responses

To temporarily disable ping, under root run:

1
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

To permanently disable ping:

1
sudo nano /etc/sysctl.conf

Then add:

1
net.ipv4.icmp_echo_ignore_all = 1
Share