Header Ads

Installing Nginx, MariaDB and PHP 7.3 (LEMP) on Debian 10

LEMP stack guide for installing PHP 7.3 alongside PhpMyAdmin and Let's Encypt SSL certificates.

Installing LEMP on Debian 10

 
Index
 


i. Prerequisites

1. Have deployed a Debian 10 image on your new server and have your root password ready. If not perform a full upgrade from Debian 9 or 8.

2. Have pointed your domain (example.com) to your server public ip address

3. Have booted your server and is up and running

4. This guide uses linux terminal to enter commands (or putty for windows).


A. Useful Linux Commands

Print Working Directory

`pwd`

Change Directory (to root)

`cd /`

List Current Directory

`ls -ahl`

-a : all the files
-h : human readable
-l : long list

Change Directory (to var)

`cd var`

Change Directory (to previous)

`cd ..`

Elevate to root privileges for whole session

`sudo -i`

To install sudo

`apt install sudo`

B. Instructions
  • Prefix a file path or file name with 'nano' and execute command
  • To save changes on nano, press ctrl+x and type 'y' (without quotes) and press enter. To discard changes type 'n' instead of 'y'.
  • To temporarily elevate to root privileges
  • Prefix a command with 'sudo' and execute command
  • Replace all placeholders such as 'example.com', 'example_user', 'nothingtosee' and so on with desired strings using string replacer tool

ii. String Replacer Tool



I. Upgrading Debian

If not using Debian 10 or if not sure, check the version by entering following commands

`lsb_release -a`

`uname -mrs`

Update and upgrade existing package

`apt update && apt upgrade && apt full-upgrade && apt --purge autoremove`

Backup and change version to 'buster' on sources file
(replace stretch with jessie for Debian 8)

`cp -v /etc/apt/sources.list /etc/apt/sources.list.bak`
`cp -rv /etc/apt/sources.list.d/ /etc/apt/sources.list.d.bak/`
`sed -i 's/stretch/buster/g' /etc/apt/sources.list`
`sed -i 's/stretch/buster/g' /etc/apt/sources.list.d/*`

Check if the packages are changed to buster

`cat /etc/apt/sources.list`

For locales related warnings you can explicitly set the locales by running the commands. This may happen when using a Linux terminal ssh.

`export LANGUAGE=en_US.UTF-8`
`export LC_ALL=en_US.UTF-8`
`export LANG=en_US.UTF-8`
`export LC_CTYPE=en_US.UTF-8`

Or use dpkg to configure locales

`locale-gen en_US en_US.UTF-8`
`dpkg-reconfigure locales`

Update and upgrade the packages and reboot to apply upgrade
(Enter 'Y' for every choice question except for 'Keeping local version...' which is N or O)

`apt update && apt upgrade && apt full-upgrade && apt --purge autoremove`


II. Configuring Network


II. A. Disabling IPv6

To set preference to ipv4 (optional)

`nano /etc/gai.conf`

un-comment "precedence ::ffff:0:0/96 100" by removing preceding pound

When prompted, type y and press enter.

To disable ipv6 completely add following lines to sysctl.conf
(optional)

NOTE : Nginx includes 'listen [::]:#' by default in configs. That line must be removed from all configs located /etc/nginx/sites-available for nginx to work. Make sure to skip this ipv6 listen line in configs mentioned below.

`nano /etc/sysctl.conf`

`net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1`

And execute

`sysctl -p`

Or alternatively edit grub and append "ipv6.disable=1", update and reboot

`nano /etc/default/grub`

GRUB_CMDLINE_LINUX_DEFAULT=".. .. ipv6.disable=1"
GRUB_CMDLINE_LINUX=".. .. ipv6.disable=1"

And execute update and reboot

`update-grub`


II. B. Installing IPtables for network security (optional)

 
`apt --reinstall install iptables`

Flush existing (iptables for ipv4 and ip6tables for ipv6)

`iptables -F`

`ip6tables -F`

Note: After adding the following rules do not use the above command to flush instead use the following command.

`iptables-save | awk '/^[*]/ { print $1 } /^:[A-Z]+ [^-]/ { print $1 " ACCEPT" ; } /COMMIT/ { print $0; }' | iptables-restore`

`ip6tables-save | awk '/^[*]/ { print $1 } /^:[A-Z]+ [^-]/ { print $1 " ACCEPT" ; } /COMMIT/ { print $0; }' | ip6tables-restore`

To show status

`iptables -S`

`ip6tables -S`

or

`iptables --list`

or

`iptables -L -n`

Create iptables script as shell script (replace x.x.x.x with your ipv4 address)

Note : the following rules do not guarantee anything, but provides basic protection from attacks.

Ports : remove port numbers or lines associated, to disable those ports
  • 22 - SSH
  • 80 - HTTP
  • 443 - HTTPS
  • 25, 465 - SMTP
  • 587 - SMTPS
  • 110 - POP3
  • 995 - POP3S
  • 143 - IMAP
  • 993 - IMAPS

`nano iptables.sh`

Rules for ipv4 (when enabled)

`::bash::iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

iptables -4 -A INPUT -s 127.0.0.0/8 ! -i lo -j DROP

iptables -4 -A INPUT -m addrtype --dst-type BROADCAST -j DROP
iptables -4 -A INPUT -m addrtype --dst-type MULTICAST -j DROP
iptables -4 -A INPUT -m addrtype --dst-type ANYCAST -j DROP
iptables -4 -A INPUT -d 224.0.0.0/4 -j DROP
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP

iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

iptables -A INPUT -i lo -j ACCEPT

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -N SSHBRUTE
iptables -A SSHBRUTE -m recent --name SSH --set
iptables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[SSH-brute]: "
iptables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
iptables -A SSHBRUTE -j ACCEPT

iptables -N ICMPFLOOD
iptables -A ICMPFLOOD -m recent --set --name ICMP --rsource
iptables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]: "
iptables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -j DROP
iptables -A ICMPFLOOD -j ACCEPT

iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSHBRUTE
iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

iptables -A OUTPUT -p tcp -m multiport --sports 22,80,443 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

iptables -A INPUT -p tcp -m tcp -m multiport --dports 110,143,465,587,993,995 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp -m multiport --sport 110,143,465,587,993,995 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

iptables -A INPUT -p tcp --dport 25 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 25 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

iptables -A INPUT -p udp --dport 1900 -j DROP
iptables -A INPUT -p udp --sport 53 -j DROP

iptables -A INPUT -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[DOS]: "

iptables -4 -A INPUT -p icmp --icmp-type 0 -m conntrack --ctstate NEW -j ACCEPT
iptables -4 -A INPUT -p icmp --icmp-type 3 -m conntrack --ctstate NEW -j ACCEPT
iptables -4 -A INPUT -p icmp --icmp-type 11 -m conntrack --ctstate NEW -j ACCEPT

iptables -A INPUT -p tcp -s x.x.x.x --sport 1024:65535 -d x.x.x.x --dport 3306 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp -s x.x.x.x --sport 3306 -d x.x.x.x --dport 1024:65535 -m state --state ESTABLISHED -j ACCEPT

iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 5 --connlimit-mask 32 -j REJECT --reject-with tcp-reset
iptables -A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 15 --connlimit-mask 32 -j REJECT --reject-with tcp-reset
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m limit --limit 150/second --limit-burst 160 -j ACCEPT`


Rules for ipv6 (when enabled)

`::bash::ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT

ip6tables -6 -A INPUT -s ::1/128 ! -i lo -j DROP

ip6tables -A INPUT -m conntrack --ctstate INVALID -j DROP

ip6tables -A INPUT -i lo -j ACCEPT

ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

ip6tables -N SSHBRUTE
ip6tables -A SSHBRUTE -m recent --name SSH --set

ip6tables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[SSH-brute]: "
ip6tables -A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
ip6tables -A SSHBRUTE -j ACCEPT

ip6tables -N ICMPFLOOD
ip6tables -A ICMPFLOOD -m recent --set --name ICMP --rsource

ip6tables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]: "
ip6tables -A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -j DROP
ip6tables -A ICMPFLOOD -j ACCEPT

ip6tables -A INPUT -p tcp -m multiport --dports 80,443 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

ip6tables -A OUTPUT -p tcp -m multiport --sports 80,443 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

ip6tables -A INPUT -p tcp -m tcp -m multiport --dports 110,143,465,587,993,995 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp -m tcp -m multiport --sport 110,143,465,587,993,995 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

ip6tables -A INPUT -p tcp --dport 25 --syn -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp --sport 25 --syn -m conntrack --ctstate ESTABLISHED -j ACCEPT

ip6tables -A INPUT -p udp --dport 1900 -j DROP

ip6tables -A INPUT -p udp --sport 53 -j DROP

ip6tables -A INPUT -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[DOS]: "

ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 1 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 3 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 4 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 133 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 134 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 135 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 136 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 137 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 141 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 142 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 130 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 131 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 132 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 143 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 148 -j ACCEPT
ip6tables -6 -A INPUT -p ipv6-icmp --icmpv6-type 149 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 151 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 152 -j ACCEPT
ip6tables -6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 153 -j ACCEPT

ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ICMPFLOOD

ip6tables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m limit --limit 150/second --limit-burst 160 -j ACCEPT`

Rules for ipv6 (when disabled)

`::bash::ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT DROP`

Execute script to add rules to iptables

`sh -x iptables.sh`

!IMPORTANT: Before proceeding with next step, logout ssh session and try logging in to check iptables. If unable to login, restart instance to clear iptables rules and correct iptables. Make sure the rules were entered correctly.

To save the rules and restore it after every boot

`iptables-save > /etc/iptables.rules`

`ip6tables-save > /etc/ip6tables.rules`

`iptables-restore < /etc/iptables.rules`

`ip6tables-restore < /etc/ip6tables.rules`

To restore it automatically after reboot, add to cron jobs

`nano /etc/crontab`

`@reboot root iptables-restore < /etc/iptables.rules
@reboot root ip6tables-restore < /etc/ip6tables.rules`


II. C. Installing Fail2Ban

Install fail2ban client

`apt install fail2ban -y`

Check fail2ban status

`service fail2ban status`

Copy config and edit to harden security

`cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local`

`nano /etc/fail2ban/jail.local`

Update following three values (under "DEFAULT" as required)

`[DEFAULT]
...
# MISCELLANEOUS OPTIONS...
bantime  = 86400
findtime  = 86400
maxretry = 2`
 
In above changes we've increased ban time to 1 day and maximum retry to 2.

Restart fail2ban client to confirm changes

`/etc/init.d/fail2ban restart`

Check status of sshd ban
 
`fail2ban-client status sshd`

This should be enough for sshd. However, it can be trained further using regex filters. All filters are located in /etc/fail2ban/filter.d

To test sshd filter against logs to see how many matched and failed
 
`fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf`

To unban all IPs

`fail2ban-client unban --all`


III. Configuring Nginx Server and SSL

To Set Hostname (replace example.com with your domain or a hostname, here we're using FQDN)

`hostnamectl set-hostname example.com`

Append to /etc/hosts (replace example.com and replace x.x.x.x [ipv4] and y:y:y:y:y:y:y [ipv6]).

`nano /etc/hosts`

`x.x.x.x example.com.example.com example.com
y:y:y:y:y:y:y example.com.example.com example.com`

Check Hostname

`hostname`

`hostname -f`

Set Localtime

`dpkg-reconfigure tzdata`


A. Installing Nginx

`apt update && apt upgrade`

`apt install nginx`

Edit nginx.conf

`nano /etc/nginx/nginx.conf`

And add the following lines within http { }

`server_tokens off;
server_names_hash_bucket_size 64;`

Create server block for first site (Replace example.com)

`nano /etc/nginx/sites-available/example.com`

Replace block contents with following and save file. (Replace example.com)

`server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com/site; index index.php index.html;
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
    }
    location ~ /\.ht {
       deny all;
    }
    location ~ /.well-known/acme-challenge/ {
        allow all;
    }
}::nginxconfig::`

Create directories for first site (replace example.com)

`mkdir -p /var/www/example.com/site`


Enable the site

`ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled`


Check for errors & restart nginx

`nginx -t`

`systemctl restart nginx`


Repeat the three step procedure to create multiple sites.
  • Create server block for site
  • Create directories for site
  • Enable the server block 

 

B. Installing SSL

Install certbot client to obtain ssl from letsencrypt.com

`apt install certbot python-certbot-nginx`

Getting a certificate using webroot and certbot. (Enter an email for updates)

Use --rsa-key-size 3072 parameter to change key size

To test a certificate using staging environment before using production environment (optional)

`certbot certonly --staging --webroot --agree-tos -w /var/www/example.com/site -d example.com`

To get a production level certificate using webroot (Limited to 5 attempts/hr)

`certbot certonly --webroot --agree-tos -w /var/www/example.com/site -d example.com`

To get certificates for sub domains (optional)

`certbot certonly --webroot --agree-tos -w /var/www/example.com/site -d example.com -d www.example.com -w /var/www/sub.example.com/site -d sub.example.com`

Note down the path, similar to '/etc/letsencrypt/live/example.com/'

Check for four files having placed in /etc/letsencrypt/archive with symbolic links in /etc/letsencrypt/live/example.com
  • cert.pem: Your domain's certificate
  • chain.pem: The Let's Encrypt chain certificate
  • fullchain.pem: cert.pem and chain.pem combined
  • privkey.pem: Your certificate's private key
`ls -l /etc/letsencrypt/live/example.com`

Replace default server block to return 404

`rm /etc/nginx/sites-available/default`

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

`server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 404;
    location ~ /\.ht {
        deny all;
    }
}::nginxconfig::`

Restart Nginx

`systemctl restart nginx`

To increase security, generate DH bit group

`openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048`

https://en.wikipedia.org/wiki/Key_size#Asymmetric_algorithm_key_lengths

"RSA claims that 1024-bit [asymmetric] keys are likely to become crackable some time between 2006 and 2010 and that 2048-bit keys are sufficient until 2030. An RSA key length of 3072 bits should be used if security is required beyond 2030. NIST key management guidelines further suggest that 15360-bit [asymmetric] RSA keys are equivalent in strength to 256-bit symmetric keys."

Create config snippet to point SSL key and cert (replace example.com)

`nano /etc/nginx/snippets/ssl-example.com.conf`

Add the following lines and save the file

`ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;::nginxconfig::`

Create config snippet with encryption params

`nano /etc/nginx/snippets/ssl-params.conf`

`ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Preloading HSTS is disabled. Uncomment to includes the
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/ssl/certs/dhparam.pem;::nginxconfig::`

Take backup of config (replace example.com)

`cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak`

Edit server blocks to force default ssl-port 443

`nano /etc/nginx/sites-available/example.com`

`server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com/site;
    location ~ /.well-known/acme-challenge/ {
        allow all;
    }
    location / {
        return 301 https://$server_name$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;
    server_name example.com www.example.com;
    root /var/www/example.com/site; index index.php index.html;
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
    }
    location ~ /\.ht {
        deny all;
    }
}::nginxconfig::`

Check Nginx for errors & restart

`nginx -t`

`systemctl restart nginx`

Check ssl-rating for A+

In your browser:

`https://www.ssllabs.com/ssltest/analyze.html?d=example.com`

Install Cron for automated updates

`apt install cron`

Restrict all user access. Allow access for example_user (if necessary)

`echo ALL /etc/cron.deny`

`echo example_user /etc/cron.allow`

Add following two lines to crontab for letsencrypt ssl auto renewal

`crontab -e`

`30 1 * * * /usr/bin/certbot renew >> /var/log/le-renew.log
35 1 * * * /bin/systemctl restart nginx::bash::`


IV. Installing PHP 7.3 and MariaDB

A. Installing PHP

PHP7.3 is available with Debian 10 repository

`apt install php7.3-cli php7.3-cgi php7.3-fpm php7.3-mysql php7.3-json php7.3-curl php7.3-mbstring php7.3-zip`

Link to other usable php extensions : https://www.php.net/manual/en/extensions.alphabetical.php

Securing PHP : Edit php.ini

`nano /etc/php/7.3/fpm/php.ini`
  • Set 0 for cgi.fix_pathinfo
  • Set 1 for session.referer_check
  • Set 1 for session.cookie_httponly
  • Set 128 for session.sid_length
  • Set 6 for session.sid_bits_per_character
  • Set 1 for session.use_strict_mode
  • Set 1 for session.cookie_secure
For more session security settings visit the links below

http://php.net/manual/en/session.security.ini.php

https://medium.com/the-white-hat-elephpant/a-look-into-sessions-and-security-b6939a8f6e92

Set as default and restart fpm to conclude

`update-alternatives --set php /usr/bin/php7.3`

`systemctl restart php7.3-fpm`

Create a php page to test (optional) (replace example.com)

`nano /var/www/example.com/site/index.php`

`<?php echo "Welcome to example.com"; die(); ?>::php::`


B. Installing MariaDB

`apt -y install mariadb-server mariadb-client`

Secure MariaDB (set Y for all and set root pass)

`mysql_secure_installation`

Check status of MariaDB

`systemctl status mariadb`

Creating MariaDB database for Limited user

Login in to MariaDB (with root pass)

`mariadb -u root -p`

Create a database for limited user (replace db_name, db_user, db_pass)

`::sql::CREATE DATABASE db_name;`

`::sql::CREATE USER 'db_user'@'127.0.0.1' IDENTIFIED BY 'db_pass';`

`::sql::GRANT ALL PRIVILEGES ON db_name.* TO 'db_user'@'127.0.0.1';`

Check mysql.user table for limited user and flush.

`::sql::SELECT User, Host, plugin FROM mysql.user;`

`::sql::FLUSH PRIVILEGES;`

`\q`

Restart MariaDB and PHP to save changes

`service mariadb restart`

`systemctl restart php7.3-fpm`


C. Installing phpmyadmin

Get the latest pure binary file (.tar.gz) from https://www.phpmyadmin.net/downloads/

`wget https://files.phpmyadmin.net/phpMyAdmin/4.9.2/phpMyAdmin-4.9.2-all-languages.tar.gz`

`tar xvf phpMyAdmin-4.9.2-all-languages.tar.gz`

Move and rename the directory (replace 'nothingtosee')

`mv phpMyAdmin-4.9.2-all-languages /usr/share/`

`mv -T /usr/share/phpMyAdmin-4.9.2-all-languages /usr/share/nothingtosee`

`ls -al /usr/share/`

`rm phpMyAdmin-4.9.2-all-languages.tar.gz`

Create Symbolic link to phpmyadmin (replace 'nothingtosee' & 'example.com')

`ln -s /usr/share/nothingtosee /var/www/example.com/site`

To setup phpmyadmin config.inc file, rename the sample file (replace 'nothingtosee')

`mv /usr/share/nothingtosee/config.sample.inc.php /usr/share/nothingtosee/config.inc.php`

Set blowfish_secret and change host to '127.0.0.1' in config file and save (replace 'bf_secret' with 32 chars)

`nano /usr/share/nothingtosee/config.inc.php`

$cfg['blowfish_secret'] = 'bf_secret';

$cfg['Servers'][$i]['host'] = '127.0.0.1';


Create password to the secure phpmyadmin page (using openssl)

`openssl passwd`

Enter a password and copy the resulting encrypted pass. And remember the entered pass for authentication

Create a file to store the username and password for phpmyadmin

`nano /etc/nginx/pma_pass`

Replace 'example_user' with a username & 'encrypted_password' with the encrypted one copied from previous step

`example_user:encrypted_password`

Add the phpmyadmin within the working server block of the /example.com config (replace 'nothingtosee' & 'example.com')

`nano /etc/nginx/sites-available/example.com`

`location ~ ^/nothingtosee/(.+\.php)$ {
    auth_basic "Restricted Content";
    auth_basic_user_file /etc/nginx/pma_pass;
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
    #allow 127.0.0.1;
    #deny all;
}
location ~ ^/nothingtosee/ {
    auth_basic "Restricted Content";
    auth_basic_user_file /etc/nginx/pma_pass;
}`

Restart Nginx to conclude

`service nginx restart`

Visit http://example.com/nothingtosee/ to see phpmyadmin page with authentication.

If there are session based errors, provide full access to sessions folder

`chmod 777 /var/lib/php/sessions`


V. Setting up SFTP file access

Adding a limited access SFTP user to access site files (WIP). This step is split between servers with no root access and without password authentication (A) and second one for servers that have open root password auth (B). Some distros allow password auth by default, however, using key-based authentication is a safer way to access servers.


A. For AWS only (or those with key-only limited user auth)

Create a new group (replace example_group)

`groupadd example_group`

Add Limited User Account (replace example_user and enter password for user)

`adduser example_user`

Add limited_user to the group 

`adduser example_user example_group`

For AWS EC2, creating ssh keys is the only way now. Follow instructions given below to create ssh keys.

Restrict and give limited user r(4)/w(2)/x(1) permissions

`chown -R root:root /var/www/example.com`

`chown -R example_user:example_group /var/www/example.com/site`

`chmod -R 755 /var/www/example.com`

`chmod -R 775 /var/www/example.com/site`

Edit sshd_config

`nano /etc/ssh/sshd_config`

Comment out following line in sshd_config (add a preceding pound)

#Subsystem sftp /usr/lib/openssh/sftp-server

Add following lines to sshd_config (replace example.com & example_group)

`Subsystem sftp internal-sftp

Match Group example_group
ChrootDirectory /var/www/example.com
X11Forwarding no
AllowTcpForwarding no

ForceCommand internal-sftp::bash::`

Note: ChrootDirectory is the one that is writable only by root (usually, one less than user directory)

Adding Public Key Auth for SSH

Public keys for ssh access should be stored in files /home/example_user/.ssh/authorized_keys and /root/.ssh/authorized_keys for the limited user and root user respectively. Replace "~" sign with appropriate home folders if not logged in using the user account for whom the ssh key access is being set.

Create keypair using terminal (for Linux only)

`ssh-keygen -t rsa -b 4096 -f ~/.ssh/example_user-example.com-ssh.key`

Install and open putty-keygen.exe (for Windows only)

Set bits to 4096 and option as SSH-2 RSA and generate

Enter passphrase, save public key, save private key, keep window open

Open putty application, add private key to 'putty connection > ssh > auth', save the session and open session with the desired user (limited/root) and enter password

Create new directory for auth key

`mkdir /home/example_user/.ssh; touch /home/example_user/.ssh/authorized_keys; chmod 700 /home/example_user/.ssh; chmod 600 /home/example_user/.ssh/authorized_keys; chown -R example_user:example_user /home/example_user/.ssh`

Edit the file and copy public key directly from keygen (or .pub file, if created from terminal) and paste here

`nano /home/example_user/.ssh/authorized_keys`

Exit and rerun putty, load saved session of desired user. (for windows)

Make sure private key matches the saved public key for that user.

Restart ssh to confirm changes

`service ssh restart`

`systemctl restart nginx`

Try authenticating using Filezilla/Winscp to confirm access. On Aws root login and password authentication is disabled by default. This can be verified by checking the sshd_config file.

 

B. For non-aws VPS providers (DO, Linode, Vultr, etc..)

Create a new group (replace example_group)

`groupadd example_group`

Add Limited User Account (replace example_user and enter password for user)

`adduser example_user`

Add limited_user to the group 

`adduser example_user example_group`

Mod limited_user to the group, and set the home folder

`usermod -g example_group -d /home/example_user -s /sbin/nologin example_user`

Restrict and give limited user r(4)/w(2)/x(1) permissions

`chown -R root:root /var/www/example.com`

`chown -R example_user:example_group /var/www/example.com/site`

`chmod -R 755 /var/www/example.com`

`chmod -R 775 /var/www/example.com/site`

Edit sshd_config

`nano /etc/ssh/sshd_config`

Comment out following line in sshd_config (add a preceding pound)

#Subsystem sftp /usr/lib/openssh/sftp-server

Add following lines to sshd_config (replace example.com & example_group)

`Subsystem sftp internal-sftp

Match Group example_group
ChrootDirectory /var/www/example.com
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp::bash::`

Note: ChrootDirectory is the one that is writable only by root (usually, one less than user directory)

Adding Public Key Auth for SSH 

Public keys for ssh access should be stored in files /home/example_user/.ssh/authorized_keys and /root/.ssh/authorized_keys for the limited user and root user respectively. Replace "~" sign with appropriate home folders if not logged in using the user account for whom the ssh key access is being set.

Create keypair using terminal (for Linux only)

`ssh-keygen -t rsa -b 4096 -f ~/.ssh/example_user-example.com-ssh.key`

Install and open putty-keygen.exe (for Windows only)

Set bits to 4096 and option as SSH-2 RSA and generate

Enter passphrase, save public key, save private key, keep window open

Open putty application, add private key to 'putty connection > ssh > auth', save the session and open session with the desired user (limited/root) and enter password

Create new directory for auth key

`mkdir ~/.ssh; touch ~/.ssh/authorized_keys; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys`

Edit the file and copy public key directly from keygen (or .pub file, if created from terminal) and paste here

`nano ~/.ssh/authorized_keys`

Note: when loggedin as root, 'chown' back the .ssh directory for example_user:example_group.

Exit and rerun putty, load saved session of desired user. (for windows)

Make sure private key matches the saved public key for that user.

To disable password authentication completely, edit sshd_config file

`nano /etc/ssh/sshd_config`

And include/update following values

`PasswordAuthentication no

ChallengeResponseAuthentication no

UsePAM no`

Restart ssh to confirm changes

`service ssh restart`

`systemctl restart nginx`

Try authenticating using Filezilla/Winscp and confirm access
 

VI. Optional commands

Turn off apache2 and reload nginx

`service apache2 stop && service nginx reload`

To Delete Limited User Account

`deluser --remove-home example_user`

To Delete Limited Group

`groupdel example_group`

To Delete any folders (created by mistake)

`rm -r -f /path/`

To make a directory group writable

`chmod -R g+w /var/www/example.com/site`

Restart PHP & Nginx

`systemctl restart php7.3-fpm nginx`

To revoke and remove ssl cert using certbot

`certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem`

`rm -rf /etc/certbot/archive/example.com/`

`rm -rf /etc/certbot/live/example.com/`

`rm -rf /etc/certbot/renewal/example.com.conf`


To add default permission to files created using acl

`apt install acl`

`setfacl -R -m d:u::rwx,d:g::rwx,d:o::rwx /var/www/example.com/site/777/`

To change mysql root pass (login to mysql first)

`mysql -u root -p`

`::sql::ALTER USER 'root'@'localhost' IDENTIFIED WITH 'mysql_native_password' BY 'password';`

To check Nginx status and error log

`systemctl status nginx.service`

or

`tail -n 20 /var/log/nginx/error.log`

or

`nano /var/log/nginx/error.log`

Configuration for gZip compression

`nano /etc/nginx/nginx.conf`

`::nginxconfig::gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
gzip_buffers 16 8k;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";`

Configure browser caching (replace example.com)

`nano /etc/nginx/sites-available/example.com`

Add the following in server block

`::nginxconfig::location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 365d;
}`

To install Memcached

`apt -y install memcached netcat`

Check and update configuration and enable memcached

`nano /etc/memcached.conf`

`systemctl restart memcached`

`systemctl enable memcached`

Check if working

`echo "stats settings" | nc localhost 11211`

Install php extension for memcached

`apt install -y php7.3-memcached`

Add memcached extension to php.ini & restart

`nano /etc/php/7.3/fpm/php.ini`

`extension=memcached`

`systemctl restart php7.3-fpm`

No comments