## Deploying Web Applications ### Reproducibility & Isolation ---- John Kristensen

Reproducibility

ensure the consistency of application environments at deploy time

## Deploying a web application * retrieve application * install application dependencies * configure application * initialise application * start the application

Retrieve application


$ tar -zxvf taslug-webapp-1.2.3.tar.gz
          

$ sudo apt-get install taslug-webapp-1.2.3.deb
          

$ git clone --branch=v1.2.3 http://code.repository.com/taslug.git

$ git fetch origin && git merge --ff-only origin v1.2.3
          

Install application dependencies


$ sudo apt-get install python-flask gunicorn
          

$ pip install flask gunicorn
          

Install application dependencies

requirements.txt


Flask==0.10.1
gunicorn==19.3.0

          

install dependencies:


$ pip install -r requirements.txt
          

Configure application

Web interface configuration:

http://webapp.taslug.org.au/install.php

Edit config file:


$ cp config.php-dist config.php
$ vi config.php
          

Configure application


$ export DB_HOSTNAME=taslugdb1
$ export DB_PASSWORD=sekrit!
          

config.py


from os import environ

def get_env_setting(setting):
    try:
        return environ[setting]
    except KeyError:
        error_msg = "Set the %s env variable" % setting
        raise ImproperlyConfigured(error_msg)
          

...


DB_HOSTNAME = get_env_setting('DB_HOSTNAME')
DB_USERNAME = 'taslug_webapp'
DB_PASSWORD = get_env_var('DB_PASSWORD')
DB_PORT = 80
          

Initialise application

Web interface configuration:

http://webapp.taslug.org.au/install.php

Login to update:

http://webapp.taslug.org.au/admin.php

## Run Application apache + mod_php/mod_python

Run Application

apache + mod_proxy + application server

## Deploying a web application * retrieve application * install application dependencies * configure application * initialise application * start the application
# Immutability ensure no changes to the application's environment occur during runtime

Resource Isolation

## The Twelve-Factor App http://12factor.net/

virtualenv


$ pip freeze
BeautifulSoup==3.2.1
CherryPy==3.5.0
Jinja2==2.8
Markdown==2.6.5
MarkupSafe==0.23
Pillow==3.1.1
...
supervisor==3.2.0
tornado==4.2.1
uTidylib==0.2
vboxapi==1.0
virtualenv==1.11.6
wsgiref==0.1.2
          

virtualenv

requirements.txt


Flask==0.10.1
gunicorn==19.4.5
          

Install dependencies in virtualenv:


$ cd taslug
$ virtualenv venv
$ source venv/bin/activate

(venv)$ pip install -r requirements.txt
(venv)$ pip freeze
Flask==0.10.1
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.11.4
argparse==1.2.1
gunicorn==19.4.5
itsdangerous==0.24
wsgiref==0.1.2
          

Run application as root


root@stark:/var/www/taslug# gunicorn -b 0.0.0.0:80 webapp:app
          

code:


import yaml
data = yaml.load(request.data)
          

yaml request:


first_name: John
last_name: Kristensen
exploit:
  !!python/object/apply:subprocess.check_output [['rm', '-rf', '/']]
          


Tom Eastman - Serialization formats are not toys (PyConAU 2014)

Run application as taslug user


root@stark:/var/www/taslug# adduser taslug
root@stark:/var/www/taslug# su - taslug

taslug@stark:/var/www/taslug$ gunicorn -b 0.0.0.0:80 webapp:app
          

yaml request:


cat_passwd:
  !!python/object/apply:subprocess.check_output [['cat', '/etc/passwd']]
list_packages:
  !!python/object/apply:subprocess.check_output [['dpkg-query', '-l']]
processes:
  !!python/object/apply:subprocess.check_output [['ps', 'aux']]
          

Run application in a chroot

Run application in a chroot

Run application in a chroot

Create chroot directory structure:

<insert lots of work to create a directory structure>

Enter chroot environment:


root@stark:~# chroot --userspec=taslug /home/taslug/chroot/ /bin/bash

taslug@stark:/var/www/taslug$ gunicorn -b 0.0.0.0:80 webapp:app
          

One virtual machine per application

or
LXC — rkt — systemd-spawn

## Linux containers * chroot * control groups (cgroups) * CPU, memory, network, disk I/O * namespace isolation * PIDs, network, UTS, users, mounts, IPC
### _Demo: pull/run docker container_

Example Application

webapp.py


import socket
import config
from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def main():
    return """\
Hello TasLUG!

From: {}
Listening on port: {}
The secret is: {}
""".format(socket.gethostname(), request.environ['SERVER_PORT'],
           config.SECRET)
          

Example Application

config.py


from os import environ

def get_env_settings(setting):
    try:
        return environ[setting]
    except KeyError:
        error_msg = "Set the %s env variable" % setting
        raise EnvironmentError(error_msg)

SECRET = get_env_settings('WEBAPP_SECRET')
          

requirements.txt


Flask==0.10.1
gunicorn==19.3.0
          
### _Demo: Local Webapp_

Building a docker container

Dockerfile


FROM debian:jessie

RUN apt-get update && apt-get install -y python python-virtualenv

RUN mkdir -p /var/www/taslug
ADD webapp.py config.py requirements.txt /var/www/taslug/

WORKDIR /var/www/taslug
RUN virtualenv venv
RUN venv/bin/pip install -r requirements.txt

CMD /var/www/taslug/venv/bin/gunicorn -b 0.0.0.0:80 webapp:app
          
### _Demo: Docker Webapp_

Questions?

  • Email: john@jerrykan.com
  • Twitter: @jerrykan
  • GNUSocial: jerrykan@taslug.org.au

Support the Software Freedom Conservancy
https://sfconservancy.org/supporter/