Monthly Archives: Apr 2018

Install and configure Nginx web sever, MySQL and PHP support with Let’s Encrypt and DDNS on Debian 9

Without going in too much details, I have been asked to build a server with certain requirements witch let me to that project and I thought it might be a good idea to describe the process as a reference as well as something to help others.

The assumption here is that you know how to install Debian, beware there are some changes if this is your first try on Debian 9. Make sure you know your network interfaces names, use the ip command or dmesg to find them out. Also as this setup is with DDNS make sure before you sign up for a DDNS service to check if the domain you want is included in the DNS Public Suffix List, otherwise you will not be able to use Let’s Encrypt services. You can check it here. I have used one of noip.com listed once.

Before everything else I installed the NO-IP’s DUC software. There is a good bash script to help you with that, which you can find here.

It did not quite work for me, I was missing the killall command, which can be installed with apt install psmisc and also on restart the service was not active, so what I did is to add a symbolic link in my rc.3 folder pointing to the script in /etc/init.d folder. You can also follow the guide on NO-IP’s web site and see how it goes.

Then comes Nginx, to install it run

apt install nginx,

in /etc/nginx/nginx.conf I changed the keep alive parameter down to 25.

Once the installation is finished next comes MySQL. MySQL is not Debian’s default database any more, so to install it you have to have the sources in your apt repository list. Navigate to https://dev.mysql.com/downloads/repo/apt/ to check on the latest version and correct download link, at the time of writing 0.8.10-1. Go to your download folder or create one and run

wget https://dev.mysql.com/get/mysql-apt-config_0.8.10-1_all.deb

and install it using dpkg:

dpkg -i mysql-apt-config_0.8.10-1_all.deb

Now having the right information in sources.list file we can install MySQL:

apt update

apt install mysql-community-client mysql-community-server

To have PHP installed run:

apt install php7.0-fpm php7.0-gd php-pear php7.0-mysqlnd php7.0-curl php7.0-intl php-imagick php7.0-imap php7.0-mcrypt php-memcache php7.0-intl php7.0-pspell php7.0-recode php7.0-tidy php7.0-xmlrpc php7.0-xsl

Have a look at the file /etc/php/7.0/fpm/php.ini if you need to change something, probably at least date.timezone to match your location.

Reload PHP: systemctl reload php7.0-fpm.service

Not much will be discussed here on MySQL and PHP. I wanted them installed for future development on multi level user login and file management, but in this instance I have only used the built in basic authentication in Nginx. (P.S. After finishing reading you can now check the next post where this is discussed.)

Before that though we will generate Let’s Encrypt certificates and this will be done using the default Nginx configuration. With your preferred editor open the file /etc/nginx/sites-available/default and set your domain name in the server_name line as well as the web root folder if different from default. The line should look like this after editing:

server_name yourhost.provider.com;

Replace yourhost.provider.com with your own domain name and then install Certbot:

apt install certbot python-certbot-nginx

Now we request an SSL certificate from Let’s Encrypt:

certbot certonly –webroot -d yourhost.provider.com

Read carefully the questions and give the appropriate answers. The newly generated SSL certificate is in a subfolder of /etc/letsencrypt/live/ folder. The exact path is shown in the Certbot output. Now it is time to create your own Nginx web server file. In mine I have the HTTP traffic redirected to the HTTPS, the web root denied for everyone so no one sees its content, two folders for two users and autoindex option on so users can see the file content in the folders. This is the configuration file:

server {

listen [::]:80;

listen 80;

server_name yourhost.provider.com;

# redirect http to https

return 301 https://yourhost.provider.com$request_uri;

}

server {

listen [::]:443 ssl http2;

listen 443 ssl http2;

server_name yourhost.provider.com;

root /var/www/html;

index index.php index.html index.htm;

ssl on;

ssl_certificate_key /etc/letsencrypt/live/yourhost.provider.com/privkey.pem;

ssl_certificate /etc/letsencrypt/live/yourhost.provider.com/fullchain.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers ‘ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS’;

ssl_prefer_server_ciphers on;

# Rest of the config

location = / {

deny all;

}

location ^~ /test1/ {

auth_basic “Restricted Access”;

auth_basic_user_file /etc/nginx/users/.test1;

try_files $uri $uri/ =404;

autoindex on;

}

location ^~ /test2/ {

auth_basic “Restricted Access”;

auth_basic_user_file /etc/nginx/users/.test2;

try_files $uri $uri/ =404;

autoindex on;

}

# pass PHP scripts to FastCGI server

location ~ \.php$ {

include snippets/fastcgi-php.conf;

# With php-fpm (or other unix sockets):

fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

# With php-cgi (or other tcp sockets):

# fastcgi_pass 127.0.0.1:9000;

}

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).

location ~ /\. {

deny all;

access_log off;

log_not_found off;

}

}

It is useful to run nginx -t before reloading the service with systemctl restart nginx.service.

To create the files with the Nginx users and passwords I used the handy python script htpasswd.py, but if you are having troubles running it you are very likely to be missing the Trac package, which you can easily install:

apt install trac

To actually get the script just do:

cd /usr/local/bin

wget https://trac.edgewall.org/export/16537/trunk/contrib/htpasswd.py

chmod 755 /usr/local/bin/htpasswd.py

though check the Trac website for the latest download link.

To create a file /in my case/ do:

htpasswd.py -c -b /etc/nginx/users/.test1 user password

Do not use -c option if you want to add more users to the same file.

Finally it is not a bad idea to add firewall rules, these are my set as a bash script, but you can always change it the way it suits your needs. I use iptables-persistent package to save the rules and load them at boot:

#!/bin/bash

# Flush previous rules, delete chains and reset counters

iptables -F

iptables -X

iptables -Z

iptables -t nat -F

# INPUT

# Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn’t use lo0

iptables -A INPUT -i lo -j ACCEPT

iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

# Accepts all established inbound connections

iptables -A INPUT -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT

# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)

iptables -A INPUT -p tcp –dport 80 -j ACCEPT

iptables -A INPUT -p tcp –dport 443 -j ACCEPT

# Allows SSH connections

# The –dport number is the same as in /etc/ssh/sshd_config

iptables -A INPUT -p tcp -s 192.168.0.0/16 -m conntrack –ctstate NEW,ESTABLISHED –dport 22 -j ACCEPT

# Allow Samba connections

iptables -A INPUT -p tcp -s 192.168.0.0/16 –dport 445 -j ACCEPT

iptables -A INPUT -p tcp -s 192.168.0.0/16 –dport 139 -j ACCEPT

iptables -A INPUT -p udp -s 192.168.0.0/16 –dport 137 -j ACCEPT

iptables -A INPUT -p udp -s 192.168.0.0/16 –dport 138 -j ACCEPT

# Allow ping

# note that blocking other types of icmp packets is considered a bad idea by some

# remove -m icmp –icmp-type 8 from this line to allow all kinds of icmp:

# https://security.stackexchange.com/questions/22711

iptables -A INPUT -p icmp -j ACCEPT

# log iptables denied calls (access via ‘dmesg’ command)

iptables -A INPUT -m limit –limit 5/min -j LOG –log-prefix “iptables denied: ” –log-level 7

# REST

# Drop invalid state packets

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

iptables -A OUTPUT -m conntrack –ctstate INVALID -j DROP

iptables -A FORWARD -m conntrack –ctstate INVALID -j DROP

# Reject all other inbound – default deny unless explicitly allowed policy:

iptables -A INPUT -j REJECT

iptables -A FORWARD -j REJECT

# Allows all outbound traffic

# You could modify this to only allow certain traffic

iptables -A OUTPUT -j ACCEPT

And that’s it, if I haven’t forgotten something… 🙂

Advertisement