Now that we have our basic server setup installed and ready to go we need to get the basic web stack ready to go. This for us will include setting up Nginx, PHP, MySql, Lets Encrypt and configure them to work with our service. This will not be a extensive in-depth guide but rather will show you how to get everything setup and give you a reference to look back on in the future.
After this tutorial we will have our basic web stack up and running with a ssl certificate provided by lets encrypt. We will also be ready to get our python environment setup and running and once our project is ready, make a small edit for our python service to be proxied through nginx with ssl.
Contents:
(Part 1) Initial Ubuntu 16.04 Server Setup
- Getting Started
- SSH-Key Authentication Setup
- Finishing Up
(Part 2 (You Are Here)) Web Stack Install and Configuration
- Nginx
- MySQL
- PHP
- Lets Encrypt
- Finishing Up
(Part 3) Python Environment Setup
- Installing Software
- Setting Up the VirtEnv
- Finishing Up
(Part 4) Sanic Project Setup
- Getting Files Setup
- Setting Up Project Defaults
- Running Our Project
- Where to go from here?
Nginx
Nginx is a great software package and is much more then just a simple web server. Besides being a http web server, it also includes the ability to act as a email proxy, reverse proxy as well as a load balancer. For our uses we will be using the http web server to serve our static content and the reverse proxy to serve our python content from the server.
To get started we will need to ensure our prerequisites are installed and ready for us to use. To start we will issue this command to grab everything all at once and get it installed. This will be mostly compiling tools and extensions:
sudo apt-get install build-essential git zlib1g-dev libpcre3 libpcre3-dev libbz2-dev libssl-dev tar unzip curl
Accept the install and we’ll be ready to start installing nginx on our server.
Next we’re going to grab the sources for nginx from the main download site (Current). At the time of writing we will be using the current stable version 1.12.2, but one could use the mainline version instead of this version with no modification for install.
In order get started we must download the sources to our local server, we’ll go ahead and use our tmp directory to work in to ensure a easy cleanup when we’re finished.
cd /tmp
wget http://nginx.org/download/nginx-1.12.2.tar.gz
tar zxf nginx-1.12.2.tar.gz
cd nginx-1.12.2
If you would like to have a more customized server headers for your project you can make a couple edits before we continue with the configure script. I will show the automated way of making these edits below, but note you can do the edits manually via the src/http/ngx_http_header_filter_module.c
file.
sed -i 's/static char ngx_http_server_string\[] = \"Server: nginx\"/static char ngx_http_server_string[] = \"Server: WhiteDog Technology\"/' src/http/ngx_http_header_filter_module.c
sed -i 's/static char ngx_http_server_full_string\[] = \"Server: \" NGINX_VER/static char ngx_http_server_full_string[] = \"Server: WhiteDog Tech: DogServer v1\"/' src/http/ngx_http_header_filter_module.c
Now that we have the sources for nginx all un-packaged and ready to go we can go ahead and setup our configuration script. We will use a more generic approach here but you are free to edit this to what you need. What you may need to add or remove here is entirely dependent on what you will be running in the end on this web stack.
./configure --user=www-data \
--group=www-data \
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/run/nginx.pid \
--lock-path=/run/subsys/nginx \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_gzip_static_module \
--with-http_secure_link_module \
--with-file-aio \
--with-threads \
--with-http_v2_module \
--with-stream \
--with-stream_ssl_module \
--with-debug
Now that we have the build configured we can go ahead and get this baby ready for some action. Start by firing off the make
command and let this finish. Finally if this goes well we’ll go ahead and install nginx finally by using sudo make install
. If all goes well we can setup our nginx service script and start it up by running the following and visiting our site in the browser.
wget https://raw.githubusercontent.com/whitedogtech/nginx-init-script/master/init -O ~/nginx-init -q &> /dev/null
sudo mv ~/nginx-init /etc/init.d/nginx
sudo chmod +x /etc/init.d/nginx
sudo /usr/sbin/update-rc.d -f nginx defaults
Lets make our dhparam key:
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
sudo nginx -t && sudo service nginx start
Great, we now have nginx install and ready to go, from here we’ll continue on to getting mysql setup and running.
MySQL
For the MySQL install we will be using the package repository install. This will make this section simple and sweet, all we need to do is simply fire off the install command:
sudo apt-get install mysql-server
Once this finishes it will ask for a root password which you should make secure and memorable. This will finish up rather quickly, but we have just a bit of cleanup to do to ensure we don’t have random objects laying about in our database. Start by issuing the command
sudo mysql_secure_installation
This will ask us a few question starting with the password we setup previously. For these question we will want to answer like so:
VALIDATE PASSWORD PLUGIN can be used to test passwords and improve security. It checks the strength of password and allows the users to set only those passwords which are secure enough. Would you like to setup VALIDATE PASSWORD plugin? This would be if you want to enforce password restriction on the database server, we will go with yes and the option for medium, 1. Change password for root? No Remove Anonymous users? Yes Disallow root login remotely? Yes Remove test database and access to it? Yes Reload privilege tables now? Yes
That will finish up our setup of the database sever, now we just need to ensure we have a new user to use with our projects in the future. We will go through this pretty quick as its all pretty self explanatory, that way we can continue on with the rest of the tutorial. Do note here, we will not be using a software such as phpmyadmin, rather we will do this via the command line. If you require a gui to work with your database i recommend HeidiSql.
mysql -u root -p Enter Password: mysql>
Now that we are logged in we’ll go ahead and add our new user to the server
CREATE DATABASE TutorialProjects; CREATE USER 'bryson'@'localhost' IDENTIFIED BY 'White Dog1!'; GRANT ALL PRIVILEGES ON TutorialProjects.* TO 'bryson'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES;
This will allow us to now have a user to only access the databases we choose and only locally. This is just a safety measure as we shouldn’t have any scripts or applications running from our root database user nor do we want unauthorized remote database access. This will finish up the basic MySQL install and setup, we are now ready to use our database in our future projects.
PHP
For the PHP side of the server we will be opting for php-fpm
, as nginx doesn’t contain native PHP processing like other web servers. We will use the “fastCGI process manager” to pass our requests from nginx for processing. This will be another quick setup but we will need to make some changes to our nginx file which we will do in the next section of this tutorial.
For the install we start again with our package manage and issue the install command:
sudo apt-get install php-fpm php-mysql
Once this is finished we will need to make a small change to the php.ini
file. Open the file sudo nano /etc/php/7.0/fpm/php.ini
and we’re looking for the line that is commented out:
;cgi.fix_pathinfo=1
We want to uncomment this line and change the value to 0 like so:
cgi.fix_pathinfo=0
Now we simply need to restart our PHP processor by sending this command to our server:
sudo systemctl restart php7.0-fpm
We will also need to setup the fastcgi-php.conf
file, go ahead and copy in this snippet into the file: /etc/nginx/fastcgi-php.conf
# regex to split $uri to $fastcgi_script_name and $fastcgi_path fastcgi_split_path_info ^(.+\.php)(/.+)$; # Check that the PHP script exists before passing it try_files $fastcgi_script_name =404; # Bypass the fact that try_files resets $fastcgi_path_info # see: http://trac.nginx.org/nginx/ticket/321 set $path_info $fastcgi_path_info; fastcgi_param PATH_INFO $path_info; fastcgi_index index.php; include fastcgi.conf;
Stack Configuration
Awesome we made it this far and we now have a working stack… to a degree. We need to do some minor configuration before everything will jive together as it should. We well get our directories ready with a more generic setup as most people will be used to, sites-available
sites-enabled
.
Let’s start by opening /etc/nginx/nginx.conf
. We’re going to be making be replacing the contents of this file with the following code. This will give us a basic setup for our nginx to ensure we keep our configuration files separate for easier changes. This is more commonly known as virtual hosts and is my preferred way of managing my hosts.
/etc/nginx/nginx.conf
user www-data; worker_processes 5; error_log /srv/nginx/logs/error.log; # pid /srv/nginx/nginx.pid; worker_rlimit_nofile 8192; events { worker_connections 4096; } http { include mime.types; include /etc/nginx/sites-enabled/*; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /srv/nginx/logs/access.log main; sendfile on; client_max_body_size 100M; tcp_nopush on; server_names_hash_bucket_size 128; keepalive_timeout 65; gzip on; server { listen 80; listen [::]:80 ipv6only=on; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
Now that we have those edits done we need to make our directories which we’ll go ahead and do that quick:
sudo mkdir /etc/nginx/sites-available sudo mkdir /etc/nginx/sites-enabled
Next we’ll setup a our configuration for our domain, in order to do this we will need to have our A records setup at our registrar or dns management service. This usual entails a @ host to the ip as well as a www host to the ip as well. This differs when we have a sub domain directed to the server, where we don’t the www host.
Let’s setup our directories quick for where we will host our files. I personally like to keep my files in /srv/domain.com/public_html/
so this is where we will make the directory.
sudo mkdir -p /srv/example.com/public_html/ sudo nano /srv/example.com/public_html/index.php
Add to file:<?php phpinfo(); ?>
Most of the following configuration is ready to go out of the box with the exception of editing the domain names. There are 3 lines at the bottom commented out currently and should remain so until the ssl certificate is obtained in the next section.
/etc/nginx/sites-available/example.com.conf
# redirect all http traffic to https server { listen 80; listen [::]:80; server_name whitedog.tech www.whitedog.tech; return 301 https://$host$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com www.example.com; root /srv/example.com/public_html; index index.php index.html index.htm; 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; } location / { try_files $uri $uri/ =404; # wordpress # try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { include fastcgi-php.conf; fastcgi_pass unix:/run/php/php7.0-fpm.sock; } location ~ /\.ht { deny all; } # ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # managed by Certbot # ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem; # managed by Certbot ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_prefer_server_ciphers on; 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_stapling on; ssl_stapling_verify on; # ssl_trusted_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; resolver 8.8.8.8 8.8.4.4; add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"; }
This configuration will have us setup to use nginx alongside php, and we will be ready to get our ssl certificate through LetsEncrypt. We can test this config by activating it by running and running the nginx reset command and finally visiting our domain in a browser. If all goes well we will be greeted by our main index page.
sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/ sudo service nginx restart
Lets Encrypt
Well we finally are ready to secure our site and need to just install and setup letsencrypt to do just that. We will be using certbot to do this with the nginx extension to get the certificates. This is pretty straightforward and most errors caused at this stage relate to the domain settings else where.
Start by installing certbot and its dependencies, we will be adding a external repository to get and install this application but it is safe to do so.
sudo add-apt-repository ppa:certbot/certbot sudo apt-get update sudo apt-get install python-certbot-nginx
Quick and easy, what did i tell ya. From here we just need to request our domain ssl certificates and ensure nginx is reading them and we will be set to go. To get started we just need to issue the following command and fill in the requested information.
sudo certbot --nginx -d example.com -d www.example.comsudo certbot --authenticator standalone --installer nginx -d example.com --pre-hook "service nginx stop" --post-hook "service nginx start"
(Edit for fix)
If this is the first time you are running this on the server you will be asked if you agree to the terms of use, which is required to continue on. You will then be asked for a email to communicate with for renewal notices, you may also be asked if you would like to join the eff newletter.
If all goes well you will be presented with a Congratulations, and possibly asked if you would like to redirect traffic to https, we can safely hit c to cancel and continue on here. We will go back and double check that our http is directing to https and our certificates are uncommented from our previous edit.
Open /ect/nginx/example.com.conf
and ensure the file is as we copied it above but now has the ssl lines uncommented and ready to use. If lets encrypt added any extra lines you can safely remove those and compare to the previous code block we had for it.
Finishing Up
This concludes the second part of the tutorial. We now have the web stack up and running and ready to go. We also added a ssl certificate for some privacy and encryption on the server side. We can always test this out by going to SSL Labs and running a test here on each of our domains and the results will show any issues. With the setup above i usually get a A+ and is not hard to maintain on a general basis. In the next part we will finally get python setup and ready to go to actually get our hand dirty with python and Sanic.