Deployment with Apache and mod_wsgi

This documentation assumes you have Apache already installed on your system. If you do not, install Apache 2.X for your platform in whatever manner makes sense.

Installing Chrysalio in a production mode

We suppose we have a user super who is in the sudo group and we are using this user to install the application.

Install required packages (Installing Required Packages) and deploy a Python 3 virtual environment (Installing a Virtual Environment) using /usr/local/virtualenv3 as directory.

$ sudo python3 -m venv /usr/local/virtualenv3
$ sudo chown super:super -R /usr/local/virtualenv3
$ source /usr/local/virtualenv3/bin/activate
(virtualenv3)$ pip install -U pip setuptools wheel
(virtualenv3)$ pip install --extra-index-url=https://pypi.chrysal.io Chrysalio

Create a database (see Setting up the Database) named, for instance, Project01.

Setting an UTF-8 environment

Make sure that on your system an UTF-8 locale is generated. To do so, type:

$ sudo dpkg-reconfigure locales

Adding a User

Create a user for your instance so you can control use of resources (Cf. ulimit, quota…). In this documentation, we suppose the name of this user is mycompany.

$ sudo adduser mycompany

If you plan to deploy several instances, we suggest to create a different user for each instance to isolate them.

Creating WSGI script

Log as mycompany and within the Chrysalio/Project01 directory (e.g. /home/mycompany/Chrysalio/Project01), create a script named project01.wsgi with the following content:

from pyramid.paster import get_app, setup_logging

ini_path = '/home/mycompany/Chrysalio/Project01/myproject01.ini'
setup_logging(ini_path)
application = get_app(ini_path, 'main')

Creating Configuration File

You can retrieve an example of configuration file in the archive of example of configuration:

mycompany$ wget https://pypi.chrysal.io/conf/Chrysalio.zip

Copy the production.ini into the /home/mycompany/Chrysalio/Project01 directory and name it project01.ini. Tune its content according to your needs.

Then, populate your database:

mycompany$ cd ~/Chrysalio/Project01
mycompany$ source /usr/local/virtualenv3/bin/activate
(virtualenv3) mycompany$ ciopopulate project01.ini

Installing Redis Server

Redis is an open source, in-memory data structure store, used as a cache. It’s a good practice to use it in a production configuration for Chrysalio.

$ sudo aptitude install redis-server
$ source /usr/local/virtualenv3/bin/activate
(virtualenv3)$ pip install redis pyramid_redis_sessions

Then, in your INI file (project01.ini), add the following lines:

# ------ Includes
pyramid.includes =
    pyramid_redis_sessions

# ------ Redis session
redis.sessions.secret = sekreet1
redis.sessions.timeout = 43200
redis.sessions.cookie_name = CIO_SESSION

If you plan to use a cache manager as well, you need to use Redis via Beaker. In that case, add the following lines instead:

# ------ Includes
pyramid.includes =
    pyramid_beaker

# ------ Beaker session
beaker.session.type = ext:redis
beaker.session.url = redis://127.0.0.1:6379/0
beaker.session.lock_dir = %(here)s/Var/Sessions/Lock
beaker.session.secret = sekreet1
beaker.session.timeout = 43200
beaker.session.key = CIO_SESSION

# ------ Beaker cache
beaker.cache.type = ext:redis
beaker.cache.url = redis://127.0.0.1:6379/1
beaker.cache.lock_dir = %(here)s/Var/Cache/Lock
beaker.cache.expire = 43200
beaker.cache.regions = ciowarehouse_user, ciowarehouse_global
beaker.cache.ciowarehouse_user.expire = 3600
beaker.cache.ciowarehouse_global.expire = 3600

Installing Let’s Encrypt Certificate

It’s a good idea to use HTTPS instead HTTP to protect your password during log in.

You can create a certificate for Apache 2 using Certbot. Install certbot according to the instruction of the site.

Then, create your own certificate:

$ sudo certbot --apache certonly -d www.project01.mycompany.com

Configuring Apache

Installing Apache 2

$ sudo aptitude install apache2

Installing mod_wsgi

Once Apache is installed, we need to install mod_wsgi.

You can install this module with your package manager:

$ sudo aptitude install libapache2-mod-wsgi-py3
$ sudo a2enmod header ssl

However, we recommend installing the latest version from PyPi. To do this, we need the Apache development package:

$ sudo aptitude install apache2-dev

Then, run the following command to put mod_wsgi into your virtual environment:

(virtualenv3)$ pip install mod_wsgi

To verify that the installation was successful, run the mod_wsgi-express script with the start-server command:

(virtualenv3)$ mod_wsgi-express start-server

To activate the WSGI module, you need to create inside the /etc/apache2/mods-available directory the two following files.

wsgi.conf:

<IfModule mod_wsgi.c>
    WSGIPythonHome '/usr/local/virtualenv3'
    WSGIApplicationGroup %{GLOBAL}
    WSGIPassAuthorization On
    WSGIPythonOptimize 1
</IfModule>

wsgi.load:

LoadModule wsgi_module /usr/local/virtualenv3.5/lib/python3.5/site-packages/mod_wsgi/server/mod_wsgi-py35.cpython-35m-x86_64-linux-gnu.so

Then, enable it:

$ sudo a2enmod wsgi
$ sudo systemctl restart apache2.service

Creating the New Site

Now, in /etc/apache2/sites-available, create a www.project01.mycompany.com.conf file with the following content:

<IfModule mod_ssl.c>

<VirtualHost _default_:443>
    ServerName www.project01.mycompany.com
    CustomLog  ${APACHE_LOG_DIR}/access_project01.log combined

    SSLEngine             on
    SSLProtocol           all -SSLv3 -TLSv1 -TLSv1.1
    SSLHonorCipherOrder   off
    SSLCompression        off
    SSLSessionTickets     off
    SSLOptions            +StrictRequire
    SSLCertificateFile    /etc/letsencrypt/live/www.project01.mycompany.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/www.project01.mycompany.com/privkey.pem

    <Location />
      Allow from all
      SSLRequireSSL
    </Location>

    <IfModule mod_headers.c>
      Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
      Header always set Content-Security-Policy "\
         default-src 'self' ; \
         img-src 'self' data: blob: ; \
         style-src 'self' 'unsafe-inline' cdn.jsdelivr.net ; \
         script-src 'self' 'nonce-ICAgIHZhciBERUZBVU' cdn.jsdelivr.net ; \
         font-src 'self' cdn.jsdelivr.net ; \
         frame-src 'self' www.youtube.com www.dailymotion.com player.vimeo.com ; frame-ancestors 'self'"
      Header always set X-Frame-Options "sameorigin"
      Header always set X-Content-Type-Options: "nosniff"
    </IfModule>

    Protocols h2 http/1.1
    KeepAlive Off
    TimeOut 600

    Alias /css         /home/mycompany/Repositories/Chrysalio/chrysalio/Static/Css
    Alias /images      /home/mycompany/Repositories/Chrysalio/chrysalio/Static/Images
    Alias /fonts       /home/mycompany/Repositories/Chrysalio/chrysalio/Static/Fonts
    Alias /js          /home/mycompany/Repositories/Chrysalio/chrysalio/Static/Js
    Alias /favicon.ico /home/mycompany/Repositories/Chrysalio/chrysalio/Static/favicon.ico
    Alias /robots.txt  /home/mycompany/Repositories/Chrysalio/chrysalio/Static/robots.txt
    <Directory /home/mycompany/Repositories/Chrysalio/chrysalio/Static>
        Require all granted
    </Directory>
    Alias /favicon.ico /home/mycompany/Repositories/MyCompany/Chrysalio/Project01/project01/Static/favicon.ico
    Alias /custom      /home/mycompany/Repositories/MyCompany/Chrysalio/Project01/project01/Static/Custom
    <Directory /home/mycompany/Repositories/MyCompany/Chrysalio/Project01/project01/Static>
        Require all granted
    </Directory>

    WSGIApplicationGroup %{GLOBAL}
    WSGIDaemonProcess project01 \
       user=mycompany group=mycompany \
       python-path=/usr/local/virtualenv3/lib/python3.5/site-packages \
       python-eggs=/home/mycompany/Chrysalio/Project01/Var/Tmp \
       processes=2 \
       threads=5 \
       restart-interval=7200 \
       listen-backlog=100 \
       queue-timeout=45 \
       socket-timeout=60 \
       connect-timeout=15 \
       request-timeout=60 \
       deadlock-timeout=60 \
       graceful-timeout=15 \
       eviction-timeout=0 \
       shutdown-timeout=5 \
       send-buffer-size=0 \
       receive-buffer-size=0 \
       header-buffer-size=0 \
       response-buffer-size=0 \
       server-metrics=Off
    WSGIImportScript /home/mycompany/Chrysalio/Project01/project01.wsgi \
       process-group=project01 \
       application-group=%{GLOBAL}
    WSGIScriptAlias / /home/mycompany/Chrysalio/Project01/project01.wsgi

    <Directory /home/mycompany/Chrysalio/Project01>
        WSGIProcessGroup project01
        Require all granted
    </Directory>
</VirtualHost>

<VirtualHost _default_:80>
    ServerName   www.project01.mycompany.com
    Redirect permanent / https://www.project01.mycompany.com/
</VirtualHost>

</IfModule>

The Alias /Static block will greatly improve performance because requests for this content (images, CSS, fonts, JavaScript, robots.txt and favicon.ico) will not need to be proxied to Chrysalio application and can be served directly. Correct the path according to your environment. You cannot use this optimization if you use the theme system.

Finally, activate this configuration and reload Apache:

$ sudo a2ensite www.project01.mycompany.com.conf
$ sudo systemctl restart apache2.service

Activating a Git Access

If you want to use versioned Chrysalio warehouse as Git repository, follow theses steps:

Preparing a Non-bare Git repository

$ cd Warehouses/Local/MyWarehouse
$ git update-server-info
$ git config receive.denyCurrentBranch updateInstead

Configuring Apache

Install the following packages:

$ sudo aptitude install git libapache2-mpm-itk
$ sudo a2enmod cgi mpm_itk

In /etc/apache2/sites-available, create a git.project01.mycompany.com.conf file with the following content:

<IfModule mod_ssl.c>

<VirtualHost _default_:443>
    ServerName   git.project01.mycompany.com
    CustomLog    ${APACHE_LOG_DIR}/access_git_project01.log combined
    <IfModule mpm_itk_module>
        AssignUserID mycompany mycompany
    </IfModule>

    SSLEngine             on
    SSLProtocol           all -SSLv2 -SSLv3
    SSLHonorCipherOrder   on
    SSLCompression        off
    SSLOptions            +StrictRequire
    SSLCertificateFile    /etc/letsencrypt/live/git.project01.mycompany.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/git.project01.mycompany.com/privkey.pem

    <IfModule mod_headers.c>
      Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
    </IfModule>

    SetEnv GIT_PROJECT_ROOT /home/mycompany/Chrysalio/Project01/Warehouses/Local
    SetEnv GIT_HTTP_EXPORT_ALL
    ScriptAlias / /usr/lib/git-core/git-http-backend/

    <Location />
      SSLRequireSSL

      AuthType Basic
      AuthName "MyCompany - Git"
      AuthUserFile /home/mycompany/Chrysalio/Project01/Warehouses/users
      Require valid-user
    </Location>
</VirtualHost>

<VirtualHost _default_:80>
    ServerName git.project01.mycompany.com
    Redirect   permanent / https://git.project01.mycompany.com/
</VirtualHost>

</IfModule>

Finally, activate this configuration and reload Apache:

$ sudo a2ensite git.project01.mycompany.com.conf
$ sudo systemctl restart apache2.service