Bits of Py.

If the implementation is hard to explain, it's a bad idea

Mon 11 June 2018

Running a Flask App on Ubuntu using an Apache

Posted by marodrig in blog   

Running a Flask Web Application on Ubuntu using Apache as our web server

I used Amazon Lightsail to host my own web server, and I wanted to share my setup to deploy a Python web application written using Flask.
We are going to use a PostgreSQL database for storing data.

Requirements

  • Ubuntu Linux.
  • Python.
  • Flask, as our web development framework.
  • pip, to manage our python packages.
  • venv, to create virtual environments for Python.
  • apache.
  • PostgreSQL as our relational database.
  • WSGI.
  • GIT, to manage our source code.
  • sqlAlchemy for Object Relational Mapping of our database.
  • AWS Lightsail.

Set up Ubuntu

Once we have created our Ubuntu Lightsail instance we can start by updating our packages.

We do so by typing the following command:

python sudo apt-get update

This will update the sources for our software packages:

sudo apt-get update && time sudo apt-get dist-upgrade

Using dist-upgrade will remove or install packages as needed for the upgrade, while upgrade won't change what is currently installed in the system.
Using the ampersand allows us to run the update command, and if successful to run the dist-upgrade. You could write a small script to do this for you.

Setting up our git repository

First start by initiating your git repository:

git init

This will create a .git directory and a repository in your local machine.
If you have an existing project you can simply clone the repository using git clone {your-repository} and get your project code.

Installing venv for virtual environments for Python

I'll be using Flaks and Python for this project. Therefore I will go over the setup for Python, pip and virtual environments. Your ubuntu instance should have Python installed and pip. If it happens that is not the case, we need to install pip before moving forward and using virtual environments.

Check that pip is install pip --version. You should see this output on your prompt:

  pip xx.x.x from /{path_to_your_python_executable} (python x.x)

If this is not the case, you need to install pip:

  sudo apt-get install python-pip python-dev build-essential

  sudo pip install --upgrade pip

After installing pip we can go ahead and use pip to install virtualenv:

sudo pip install --upgrade virtualenv

We can test we have successfully installed virtualenv by typing:

virtualenv --version

In order to create our virtualenv that will allow us to have our packages separate from our main python installation we create a new virtual environment using the following command:

virtualenv venv

We can activate the virtual environment by the following command source venv/bin/activate, and then we can use pip to install our packages as needed. To deactivate the virtual environment we can simply type deactivate.

As a best practice always create a requirements.txt file using pip. This file will contain the packages dependencies for the current project. Create the text file by typing pip freeze > requirements.txt. If you already have a requirements.txt file, you can install the packages by pip install -r requirements.txt.

PostgreSQL setup

Use apt-get to install postgresql:

  sudo apt-get install postgresql

We need to run psql as user postgres:

sudo su - postgres
psql

We are now in the psql prompt and we can create our user:

  create user catalog_user;

And out database:

  CREATE DATABASE catalog;

Then assign a password to our user:

  ALTER ROLE catalog_user WITH PASSWORD 'not_the_actual_password_because_security';

And now let's grant the user permissions to our catalog database:

  GRANT ALL PRIVILEGES ON DATABASE catalog TO catalog_user;

And we created a www-data user in order to play nice with the Apache2 default configuration:

  create user "www-data" with login;

No remote connections are allowed as per the file located at /etc/postgresql/9.5/main/pg_hba.conf

We need to restart the service:

  sudo service postgresql restart

Setup apache

First install Apache using apt-get:

    sudo apt-get install apache2 apache2-doc

We need to configure our site, Apache has a default page currently displayed, but we want to create our own. Use your text editor of choice to create a new configuration file for Apache:

    vi /etc/apache2/sites-available/FlaskApp.conf

And we want the file to look like this:

<VirtualHost *:80>
    ServerName {your_server_ip}
    ServerAlias {your_server_alias}
    ServerAdmin {your_user_name}@{your_server_ip}
    WSGIDaemonProcess {app_name} threads=5
  python-path=/var/www/{location_of_your_app}:/var/www/{your_python_location}
    WSGIProcessGroup {app_name}
    WSGIScriptAlias / /var/www/FlaskApp/myapp.wsgi
    <Directory /var/www/FlaskApp/catalog_app/>
        Order allow,deny
        Allow from all
    </Directory>
    Alias /static /var/www/FlaskApp/catalog_app/static
    <Directory /var/www/FlaskApp/static/>
        Order allow,deny
        Allow from all
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/error.log
    LogLevel warn
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Apache will now be ready to serve our WSGI but we need to do some configuration before we are ready.

Setup WSGI

Use apt-get to install apache WSGI module:

    sudo apt-get install libapache2-mod-wsgi

As I configure Apache to look for my WSGI in the following location: /var/www/FlaskApp/myapp.wsgi I will use vi to once again create myapp.wsgi.

    sudo vi /var/www/FlaskApp/myapp.wsgi

And edit the file with the following content:

        import sys
        import os
        import logging
        import hashlib
        logging.basicConfig(stream=sys.stderr)
        sys.path.append("/var/www/FlaskApp/catalog_app/")

        from views import app as application
        from views import generate_csrf_token
        application.jinja_env.globals['csrf_token'] = generate_csrf_token
        application.secret_key = hashlib.sha256(os.urandom(1024)).hexdigest()

I moved the portion of my views.py file that was in the main area. This is the file that will be run by python. First enable our new site:

  sudo a2ensite FlaskApp

Remove Apache's default site:

  sudo a2dissite 000-default

Restart apache2 with our configuration:

  sudo service apache2 reload

Secure your git repo

In order to prevent access to our .git directory we need to properly configure the following file: /etc/apache2/conf-enabled/security.conf

<DirectoryMatch "/\.git">
  Require all denied
</DirectoryMatch>

References

For a more details of why to do this and how please follow this link:

https://davidegan.me/hide-git-repos-on-public-sites/