Lasaña setup with lighttpd - ntrrgc/lasana GitHub Wiki

Lasaña setup with lighttpd

Deploying Lasaña is a process fairly complex, specially if you are not familiar with Django and FastCGI, so we will do in a step-check fashion. In every stage we will configure something and test it works before going to a more complicated stage.

If you get stuck, open a ticket.

Stage 0: Prerequisites

Install the following software:

  • Python 2.6+ (Python 3.x not supported)
  • virtualenv
  • git
  • gettext (in order to compile translations)
  • lighttpd
  • cron

On Debian this can be done running this as root:

aptitude install python python-virtualenv git gettext lighttpd cron

Stage 1: Install Lasaña

User creation

To increase security, we will create a new user for running the service. Run the following as root.

useradd -r -m -d /srv/lasana -s /bin/bash lasana

Now we can switch to this user. Run as root:

su - lasana

Warning for that kind of people that love running everything as root

If not explicitly said, all the commands in this guide should be run as lasana user. Running them as root may mess your setup, and probably won't work.


Virtualenv and Django installation

We will use virtualenv to manage our own Python setup. This will allow us to use recent Django versions independently of the version provided by the system vendor.

Then, we will proceed to create a new Python setup with an existent interpreter. Lasaña supports both Python 2.6 and Python 2.7. Python 3.x is not supported yet.

virtualenv -p /usr/bin/python2.7 ~/env
~/env/bin/pip install Django==1.5.1 flup

Note 1: The above command assumes you have a Python 2.7 interpreter in /usr/bin/python2.7. That may not be true on old systems, i.e. Debian 6. In such, just change it to use Python 2.6, since it will work anyway.

Of course, you could also compile your own Python interpreter, but yours would be a pretty old system if it would not provide at least Python 2.6.


Creating a Django project and merging Lasaña

Next, we will create a new Django project in which will install Lasaña.

~/env/bin/django-admin.py startproject lasana_proj
cd ~/lasana_proj
git clone https://github.com/ntrrgc/lasana.git

Compile Lasaña translations...

cd ~/lasana_proj/lasana
~/env/bin/python ../manage.py compilemessages

Don't worry about the red output. They did not choose a good color, I know.

Adjusting project settings

And then we have to adjust project settings. Open ~/lasana_proj/lasana_proj/settings.py with your favourite editor and replace all its contents with this:

# Django settings for lasana_proj project.
import os.path
ROOT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))

DEBUG = True
TEMPLATE_DEBUG = DEBUG

ADMINS = (
    # ('Your Name', '[email protected]'),
)

MANAGERS = ADMINS

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.expanduser('~/lasanadb'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}

ALLOWED_HOSTS = ['*']

INSTALLED_APPS = (
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'lasana',
)

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'django.core.context_processors.static',
    'django.core.context_processors.tz',
    'django.contrib.messages.context_processors.messages',
    'lasana.context_processors.common',
)

LANGUAGE_CODE = 'en-us'

USE_I18N = True
USE_L10N = True
USE_TZ = True

STATIC_ROOT = os.path.expanduser('~/static')

STATICFILES_DIRS = (
    os.path.join(ROOT_PATH, 'static'),
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.locale.LocaleMiddleware',
)

ROOT_URLCONF = 'lasana_proj.urls'

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'lasana_proj.wsgi.application'

SECRET_FILE = os.path.join(os.path.dirname(__file__), 'secret.key')

try:
    SECRET_KEY = open(SECRET_FILE).read().strip()
except IOError:
    import os, stat
    import django.utils.crypto
    chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
    with open(SECRET_FILE, 'w') as f:
        SECRET_KEY = django.utils.crypto.get_random_string(50, chars)
        os.fchmod(f.fileno(), stat.S_IRUSR)
        f.write(SECRET_KEY)
    del chars

TEMPLATE_DIRS = (
    os.path.join(ROOT_PATH, 'templates'),
)

FORCE_SCRIPT_NAME = ''

STATIC_URL = '/static/'

LASANA_UPLOAD_ROOT = os.path.expanduser('~/uploads')
MEDIA_ROOT = LASANA_UPLOAD_ROOT

if DEBUG:
    LASANA_USE_X_SENDFILE = False
else:
    LASANA_USE_X_SENDFILE = 'lighttpd'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}

If you are new to Django, sure, that was scary! Don't worry. It just a bunch of paths, instructions to tell Django which modules use and some other tricks.

Then we will set the URL mapping router. This is simpler. Just replace the contents of your ~/lasana_proj/lasana_proj/urls.py with this:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
    url('', include('lasana.urls')),
)

Creating database models

Last but not least, we will create the database. In settings.py we told it to use SQLite, so we don't need extra setup here. Just run:

~/env/bin/python ~/lasana_proj/manage.py syncdb

Giving it a test run

If everything was OK, we can run now this:

~/env/bin/python ~/lasana_proj/manage.py runserver 0.0.0.0:8000

And you should have Lasaña running at http://localhost:8000/ now. Congratulations!

Screenshot of Lasaña just installed

But we are far from done yet, so keep on reading.

Stage 2: Customizing your Lasaña instance

Introduction to templates

Thanks to Django, Lasaña is easily customizable through templates. Templates are pieces of code used to build a website in an structured manner.

Did you saw the note of your new Lasaña installation on the footer? Let's edit that footer. For that we'll use templates.

Lasaña templates are located in ~/lasana_proj/lasana/templates. If you go to that folder you'll find there is again other folder named lasana inside. This is because Django applications can bundle templates for other applications too. Don't worry about it.

lasana
├── base.html
├── copyright_footer_note.html
├── introduction_note.html
├── meal_create_success.html
├── meal_form.html
├── style_collection.html
└── style.html

We could just edit those files to customize Lasaña, but we won't do that, because we would mess the git repository!

Instead, we will create a folder in our project for our own templates. Django will look first on ours, if not found it will search in the original templates.

mkdir -p ~/lasana_proj/templates/lasana
cd ~/lasana_proj/templates/lasana

You can copy now the template you want to tinker with and edit it. For example, let's change the footer.

cp ~/lasana_proj/lasana/templates/lasana/copyright_footer_note.html .

Open copyright_footer_note.html with your favorite editor and you'll see this.

{% load i18n %}
&copy; John Titor 2036<br/>
<span style="color: #737373">
  {% blocktrans with template_path='templates/lasana/copyright_footer_note.html' %}Redefine <em>'{{ template_path }}'</em> to edit this note.{% endblocktrans %}
</span>

Templates are just HTML with some additions. That {% blocktrans %} thing is useful to create translatable strings, but that's an advanced topic. The {% load i18n %} is needed for {% blocktrans %} to work. If you don't use {% blocktrans %} in your template, you don't need it.

Let's erase all the contents and place something simple. How about this?

I'm an <strong>evil</strong> hawk.

Load the page again in your browser and now you should see the result.

A screenshot of the footer

Changing the introductory note

You may not like the Lasaña description for your custom instance. For example, "keeps your files private"? You are an evil hawk! You'll gossip on every uploaded file! Let's put it clear.

In order to edit the description, repeat the previous process with lasana/introduction_note.html.

cp ~/lasana_proj/lasana/templates/lasana/introduction_note.html .

It looks like this:

{% load i18n %}
<p>{% blocktrans %}{{ lasana_name }} is a temporary file hosting service.{% endblocktrans %}</p>
<p>{% blocktrans %}{{ lasana_name }} aims at minimal user intrusion: we don't want you to need more than one click for uploading or downloading a file.{% endblocktrans %}</p>
<p>{% blocktrans %}{{ lasana_name }} keeps your files private: your files can only be downloaded by those who receive the links.{% endblocktrans %}</p>

{{ lasana_name }} is a variable. It's defined outside the template. As from the name can be guessed, it's used to contain the name of the service, Lasaña.

Let's change the new template to look like this.

<p>Welcome to my custom {{ lasana_name }} instance.</p>
<p>Now with more white sauce!</p>
<p>I'm watching you!</p>

Reload again, and you'll see it applied.

A screenshot of the introduction text

Changing the service name

What about the service name? Yes, we can change it too!

Open ~/lasana_proj/lasana_proj/settings.py with your editor and add a line like this:

LASANA_NAME = u'Hamburger'

Done.

A screenshot of the new title

You're own style

Lastly, we may want to add your own style. Styling is controlled through CSS.

CSS files are considered static files because, differently from templates, they rarely change its content. Static files work in a similar fashion to templates. You can find current static files in ~/lasana_proj/lasana/static/.

lasana
├── css
│   ├── cute.css
│   ├── dark.css
│   └── original.css
├── fonts
│   ├── AveriaGruesaLibre-Regular.ttf
│   ├── DonegalOne-Regular.ttf
│   └── MerriweatherSans-Regular.ttf
└── img
    ├── clover.jpg
    ├── flower.jpg
    └── reimu.jpg

As before, we will not change those files directly, but we will create a folder for our own static assets in the project.

mkdir ~/lasana_proj/static

Let's create a folder for CSS files inside.

mkdir -p ~/lasana_proj/static/lasana/css

We will create an evil style! Place this code in ~/lasana_proj/static/lasana/css/evil.css:

body {
  background: #181818; }

body { 
  color: #D5C3C3;
  font-family: "Trebuchet MS";
}

p {
  line-height: 1.5em; }

input, select {
  font-size: 15px; }

h1 {
  text-align: center; 
  color: #FF3737;
}

#form_container {
  border: 1px solid #B94343;
  width: 600px;
  margin-left: auto;
  margin-right: auto;
  padding: 30px; 
  border-radius: 5px;
}
  
#introduction {
  border: none;
  width: 600px;
  margin-left: auto;
  margin-right: auto;
  padding: 30px; 
  margin-bottom: -50px;
}

form .fields {
  width: 100%;
  display: table;
  border-collapse: collapse; }

form .fields > .field {
  display: table-row; }

form .fields > .field > .cell {
  display: table-cell;
  padding: 10px 10px;
  text-align: left; 
  font-weight: bold;
}

form input[type="submit"] {
  margin-top: 5px;
  margin-left: 5px; }

input[type="text"] {
  cursor: text; }

#share_title {
  font-size: 22px;
  margin-top: 5px;
  margin-bottom: 5px; }

#lasagna_url_field {
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  width: 100%;
  border: 1px solid #ff912f;
  padding: 3px;
  font-size: 36px; }

#store_more {
  margin-top: 45px; }

footer {
  width: 600px;
  margin: 0 auto;
  margin-top: 50px;
  border: 0;
  border-top: 2px solid #F74E4E; }

#copyright_footer_note {
  font-size: 70%;
  text-align: center; }

#set_your_style {
    margin-left: 0;
    margin-right: 0;
    margin-bottom: 20px;
    text-align: center;
    color: #DD8A8A;
}

#set_your_style li {
    display: inline-block;
    font-style: italic;
    margin: 0 1px;
}
#set_your_style ul {
    display: inline-block;
    margin: 0;
    padding: 0;
}

#set_your_style li, #set_your_style li a, a {
    color: #E94B4B;
}

Now we will add to the themes chooser. Copy the template lasana/style_collection.html and edit it to add a new entry to our style.

{% load i18n %}
<section id="set_your_style" aria-hidden="true">
  <span>{% trans "Set your favorite style!" %}</span>
  <ul>
    {% include "lasana/style.html" with name="Original" %}
    {% include "lasana/style.html" with name="Cute" %}
    {% include "lasana/style.html" with name="Dark" %}
    {% include "lasana/style.html" with name="Evil" %}
  </ul>
</section>

The name of the CSS file is generated slugifying the name of the style. To slugify a text means converting it all to lowercase and removing special characters. That's why it works with name Evil even when the CSS file is named evil in lowercase.

Also, this template has an example of an {% include %} tag. As the name suggest, they are replaced with the template specified, optionally setting additional variables in the context of the included template (in this case, the name variable).

F5 on your browser and there is.

Screenshot of new header

But wait! We want it to be the default style!

We can set that in ~/lasana_proj/lasana_proj/settings.py. Just add this line:

LASANA_DEFAULT_STYLE = 'evil'

If you reload the page now and you didn't clicked on any style previously, you will be shown the new style. If you did, delete last hour cookies in your browser or open a new incognito or private window and you'll see the setting working as new users would see.

Disabling theme changer

But wait again! Cute theme, original warm theme...? No! This is my evil hamburger. There is nothing apart from pure evilness in it! No place for heart-warming themes! — If you thought that, you may want to disable the theme changer. Just go to settings.py again and add this line:

LASANA_ALLOW_CHANGE_STYLE = False

Finally, this is the result.

Screenshot of final appearance

Welcome to the town

This section has given you an introduction on how to customize Lasaña and has introduced some Django concepts like templates and configurable applications.

There is much more of it. Lasaña is not built on magic, but short, mostly easy to understand code. If you are curious, check it! Also, if you want to learn more about Django, the framework which powers it, check https://www.djangoproject.com/.

Stage 3: Integrating Lasaña with lighttpd

Django development server is intended only to help at development and is not (and the developers say it won't become) a full web server intended for production.

Instead, Django is intended to be plugged into a real web server, like Apache, nginx or lighttpd. For this it provides a WSGI interface and a FastCGI interface. WSGI is often told to be the best way to deploy Python web applications, but lighttpd only supports FastCGI, so we'll use that.

Also, static files are meant not to be served from Django, but directly by the web server, which is faster, knows how to cache files, etc.

Uploaded files can be served also from lighttpd thanks to the X-Sendfile extension. Basically, Django tells lighttpd "serve this file" and lighttpd opens that file and serves it while Django process can continue doing other things. This allows for a big number of concurrent downloads due to the asynchronous nature of lighttpd.

FastCGI is tricky, and errors are hard to debug, so we'll go very slowly.

Stage 3.1: Preparing Django for production

Release mode

By default Django is in debug mode. In this mode, changes to files are reflected automatically to the user, and errors show a bunch of information in the browser about what was wrong and other fancy things to help the developers do their job.

But that also makes debug mode inefficient and shows the world more things about the system that one would expect for a web application. For deploying applications on the Internet exists the release mode, where the focus is in getting the things working right and fast.

Every Django project deployed should be on release mode. In order to set this mode, edit ~/lasana_proj/lasana_proj/settings.py, seek the following line:

DEBUG = True

And change it to:

DEBUG = False

Note: Please note in the settings.py provided in this guide there are some lines which read:

if DEBUG:
    LASANA_USE_X_SENDFILE = False
else:
    LASANA_USE_X_SENDFILE = 'lighttpd'

So putting Django into release mode will trigger Lasaña to use X-Sendfile extension. If you try to download a file with the development server, it will now fail, because Django development server does not understand X-Sendfile extension. lighttpd does.


Collect static files

We want static files to be served by the web server, but at the moment we have a problem because they are spread between project static assets (~/lasana_proj/static) and Lasaña static assets (~/lasana_proj/lasana/static).

For this purpose Django has a static file collector utility. Run the following:

~/env/bin/python ~/lasana_proj/manage.py collectstatic --noinput

And Django will collect all of them to a single directory set in settings.py. In the one provided with this guide that directory is ~/static.


Note: If it gives you an error about ~/lasana_proj/static directory not existing, create it. That's what you get for skipping the stage 2.


Stage 3.2: lighttpd with only Lasaña, both on the same user

Let's start with a simple goal. We will set up a web server on http://localhost:8000/, just like before, but this time with lighttpd and release mode.


Note: If you are running Django development server (that runserver thing), you should stop it now. We are done with it.


Run Lasaña over a FastCGI socket

FastCGI is a protocol to communicate web applications (like Lasaña) with web servers in an efficient way.

This communication can be a local communication between processes through UNIX domain sockets or remote communication through TCP sockets. UNIX domain sockets serve better for our purpose because them allow for increased security.

For exposing an application through FastCGI, Django comes with the runfcgi utility. Let's expose FastCGI through a UNIX socket on ~/lasana.sock:

~/env/bin/python ~/lasana_proj/manage.py runfcgi daemonize=false socket=/srv/lasana/lasana.sock

It will provide no output, it's fine. Let it running and head to the next section.

Teach lighttpd to talk to the socket

The following is a minimal lighttpd configuration.

server.modules += (
    "mod_fastcgi",
    "mod_alias",
    "mod_rewrite"
)

server.document-root = "/tmp"
server.port = 8000

mimetype.assign = (
    ".html" => "text/html",
    ".txt" => "text/plain",
    ".jpg" => "image/jpeg",
    ".png" => "image/png",
    ".js"  => "application/javascript",
    ".css" => "text/css",
    ".ogg" => "audio/ogg",
    ".woff" => "application/font-woff",
    ".ttf" => "application/x-font-ttf",
)

fastcgi.server = (
    "/lasana.fcgi" => (
        "lasana" => (
            "socket" => "/srv/lasana/lasana.sock",
            "check-local" => "disable",
            "allow-x-send-file" => "enable",
        )
    ),
)

alias.url = (
    "/static" => "/srv/lasana/static",
)

url.rewrite-once = (
    "^(/static.*)$" => "$1",
    "^/favicon\.ico$" => "/static/favicon.ico",
    "^(/.*)$" => "/lasana.fcgi/$1",
)

Save it as ~/lighttpd.conf and run it with the following command:

lighttpd -D -f ~/lighttpd.conf

Note: In Debian, lighttpd is located in /usr/sbin/lighttpd. If the previous command does not work for you, type this instead.

/usr/sbin/lighttpd -D -f ~/lighttpd.conf

Now you should be able to access Lasaña at http://localhost:8000/ just like before. You'll probably won't notice a performance difference at this point, but believe me, it's better this way.


Understanding the magic

It's easy to see this setup working, but it's not so easy to understand why it works.

This is the explained version of the above configuration file:

server.modules += (
    "mod_fastcgi",
    "mod_alias",
    "mod_rewrite"
)

# lighttpd would serve files on this directory, altough it won't
# because of the following rules.
server.document-root = "/tmp"

server.port = 8000

# MIME types to send in the HTTP response headers.
# Browsers complain if they are not correctly set.
mimetype.assign = (
    ".html" => "text/html",
    ".txt" => "text/plain",
    ".jpg" => "image/jpeg",
    ".png" => "image/png",
    ".js"  => "application/javascript",
    ".css" => "text/css",
    ".ogg" => "audio/ogg",
    ".woff" => "application/font-woff",
    ".ttf" => "application/x-font-ttf",
)

fastcgi.server = (
    # FastCGI prefix (because it starts with '/').
    #
    # URLs starting with this prefix will not be served as files but be 
    # managed by the FastCGI server.
    "/lasana.fcgi" => ( 
        "lasana" => ( # Name of the server, just for the logs sake.
            # The socket Lasaña is listening onto
            "socket" => "/srv/lasana/lasana.sock",
            # Tells lighttpd not to raise a 404 if it does not exist a file
            # with the given URL.
            "check-local" => "disable", 
            # Required to activate X-Sendfile extension
            "allow-x-send-file" => "enable",
        )
    ),
)

# Aliases are URL prefixes behind which the web server will serve files
# from a different directory than the default `server.document-root`.
alias.url = (
    # URLs starting with '/static' will be served with files from 
    # '/srv/lasana/static'
    "/static" => "/srv/lasana/static",
)

# Each URL is matched against the left patterns until it matches one, and 
# rewritten to the expression on the right.
#
# These rewrites happen on the server but are invisible to the user.
# 
# Altough they are matched from first to last, please read the comments
# from last to first. They are better understanded that way.
url.rewrite-once = (
    #  ^  Or it is another static file. This rewrite rule tells to a 
    #  |  substitute string starting with '/static' for exactly the same 
    #  |  string, effectively not doing rewrite but stopping the rewriting
    #  |  process and getting it attended by the rule in `alias.url`.
    "^(/static.*)$" => "$1",

    #  ^  Except it is the favicon, in that case rewrite it to 
    #  |  '/static/favicon.ico'.
    "^/favicon\.ico$" => "/static/favicon.ico",

    #  ^  Rewrite every URL so it begins with '/lasana.fcgi/' in order for 
    #  |  it to necesarily pass through the FastCGI socket.
    "^(/.*)$" => "/lasana.fcgi/$1",
)

Also, there is another issue.

Django gets their URLs with the /lasana.fcgi part stripped off, but the web server sets another FastCGI header telling it's running under the /lasana.fcgi prefix.

By default, Django notices this advice and makes all the links in the site to point to URLs starting with /lasana.fcgi. This is undesirable in our case and is avoided by setting the following line in settings.py:

FORCE_SCRIPT_NAME = ''

That line was already in the settings.py file provided with this guide, so you don't need to worry this time.


Stage 3.3: lighttpd with only Lasaña, both on the same user on port 80

Now you have a Lasaña installation, but it's running on port 8000, and that's not cool.

In order for it to run on port 80 we need to run lighttpd as root, but we don't want our web server being a root process!

This is really easy to fix, just modify the previous lighttpd.conf to set these variables:

server.port = 80

server.username = "lasana"
server.groupname = "lasana"

Now type the following in a root console to start the server:

lighttpd -D -f /srv/lasana/lighttpd.conf

Note: If you get a "80 Address already in use" you probably have another web server running on that port. This may be true in the case you just installed lighttpd in Debian, in which it starts with the system. Do the following to stop it:

service lighttpd stop

The server is started as root, binds the socket and then drops privileges to lasana user. You can check this is true with ps.

ps aux |grep lighttpd

This is the expected result:

lasana    5774  0.0  0.1  49964  2556 pts/1    S+   21:25   0:00 lighttpd -D -f /srv/lasana/lighttpd.conf

Stage 3.4: lighttpd with only Lasaña, on different users

Often you won't run your web server with the same user as the web applications for the sake of security.

It's a good measure to have a separate system user for each web application you have in your server so in case one of them is compromised it could not propagate the attack to others. Also, probably your distribution will already have a user for the web server (like http, lighttpd or www-data) and you may want to use it.

Sadly, at the moment there is no way to communicate Django over UNIX socket and FastCGI with processes being run as another user, because runfcgi lacks an option to set the socket permission mask. It has something that looks like it, but that's not it. For the discussion, read here.

I wrote a patch to add this functionality, which we will use in the meantime until it (or other similar patch) is accepted in core and reaches a stable version of Django.

To apply it, type the following commands:

cd ~/env/lib/python2.*/site-packages
patch -p1

After entering the patch command, the terminal will be waiting for input. Paste the following in the terminal.

--- a/django/core/servers/fastcgi.py
+++ b/django/core/servers/fastcgi.py
@@ -34,10 +34,11 @@
     'maxrequests': 0,
     'debug': None,
     'outlog': None,
     'errlog': None,
     'umask': None,
+    'socketumask': None,
 }
 
 FASTCGI_HELP = r"""
   Run this project as a fastcgi (or some other protocol supported
   by flup) application. To do this, the flup package from
@@ -61,10 +62,12 @@
   workdir=DIRECTORY    change to this directory when daemonizing (default %(workdir)s).
   debug=BOOL           set to true to enable flup tracebacks.
   outlog=FILE          write stdout to this file.
   errlog=FILE          write stderr to this file.
   umask=UMASK          umask to use when daemonizing, in octal notation (default 022).
+  socketumask=UMASK    umask to use when creating the socket, in octal notation
+                       (default 022).
 
 Examples:
   Run a "standard" fastcgi process on a file-descriptor
   (for Web servers which spawn your processes for you)
     $ manage.py runfcgi method=threaded
@@ -161,18 +164,24 @@
             daemonize = False
         else:
             return fastcgi_help("ERROR: Invalid option for daemonize "
                                 "parameter.")
 
+    if options['socketumask'] and not options['socket']:
+        return fastcgi_help("ERROR: socketumask requires socket parameter")
+
     daemon_kwargs = {}
     if options['outlog']:
         daemon_kwargs['out_log'] = options['outlog']
     if options['errlog']:
         daemon_kwargs['err_log'] = options['errlog']
     if options['umask']:
         daemon_kwargs['umask'] = int(options['umask'], 8)
 
+    if options['socketumask']:
+        wsgi_opts['umask'] = int(options['socketumask'], 8)
+
     if daemonize:
         from django.utils.daemonize import become_daemon
         become_daemon(our_home_dir=options["workdir"], **daemon_kwargs)
 
     if options["pidfile"]:

Then press Return and then Ctrl+D twice to tell there is no more input. This should appear on the terminal:

patching file django/core/servers/fastcgi.py

Patch successful!

Also, sockets are usually placed under /var/run/{app name}. Let's follow that convention running the following commands as root:

mkdir -m 755 /var/run/lasana
chown lasana: /var/run/lasana

Note: /var/run is wiped at boot.


Now bind the socket with the following command. The umask 0007 sets all permissions to user and group and none to others.

~/env/bin/python ~/lasana_proj/manage.py runfcgi daemonize=false socket=/var/run/lasana/lasana.sock socketumask=0007

You can check the permissions with ls -l:

ls -l /var/run/lasana/lasana.sock

Next, edit lighttpd.conf to run as other user. Probably your system already has a user meant to be used by the web server itself, i.e. www-data (Debian), lighttpd (Gentoo) or http (Arch Linux).

server.username = "www-data"
server.groupname = "www-data"

And change the socket path in the lighttpd configuration file.

"socket" => "/var/run/lasana/lasana.sock",

Probably this is also a good moment to move your lighttpd.conf to /etc/lighttpd/lighttpd.conf in order to take advantage of your distribution utilities.


Are you afraid of losing the default lighttpd.conf file of your distro?

Then type this before (as root):

mv /etc/lighttpd/lighttpd.conf{,.example}

Add the server user to lasana group, running the following command as root (changing www-data for the correct user if you are not in Debian):

usermod -a -G lasana www-data

Stop lighttpd if you were running it, start it again and it should work. If you decided to move the configuration file to /etc/lighttpd/lighttpd.conf, you can run it now typing the following (as root):

service lighttpd start

Stage 3.5: lighttpd with other applications, with Lasaña behind a subdomain

The following is an example lighttpd configuration file you can use to serve Lasaña among other things. In the example case, the other things are PHP scripts and static files. Also, directory listing is enabled.

The only change to make Lasaña be served only in its subdomain is the $HTTP["host"] == ... { block.

server.modules += (
    "mod_fastcgi",
    "mod_alias",
    "mod_rewrite"
)

server.document-root = "/srv/http/blank"
server.port = 80

server.username = "www-data"
server.groupname = "www-data"

dir-listing.activate = "enable"
dir-listing.hide-dotfiles = "enable"
dir-listing.encoding = "utf-8"
dir-listing.set-footer = "Evil Corporation"
dir-listing.exclude = (".*~", ".*.swp$")

index-file.names = ("index.html", "index.php", "index.htm")

mimetype.assign = (
    ".html" => "text/html",
    ".txt" => "text/plain",
    ".jpg" => "image/jpeg",
    ".png" => "image/png",
    ".js"  => "application/javascript",
    ".css" => "text/css",
    ".ogg" => "audio/ogg",
    ".woff" => "application/font-woff",
    ".ttf" => "application/x-font-ttf",
)

# If requested Lasaña
$HTTP["host"] == "lasana.rufian.eu" {

# Hint!
# Want to serve several (sub)domains? Use regex! i.e...
# $HTTP["host"] =~ "^(lasana|xn--lasaa-rta)\.rufian\.eu$" {

    fastcgi.server = (
        "/lasana.fcgi" => (
            "lasana" => (
                "socket" => "/var/run/lasana/lasana.sock",
                "check-local" => "disable",
                "allow-x-send-file" => "enable",
            )
        ),
    )

    alias.url = (
        "/static" => "/srv/lasana/static",
    )

    url.rewrite-once = (
        "^(/static.*)$" => "$1",
        "^/favicon\.ico$" => "/static/favicon.ico",
        "^(/.*)$" => "/lasana.fcgi/$1",
    )

# If requested PHP + static (typical LAMP stuff)
} else $HTTP["host"] == "xn--lea-8ma.rufian.eu" {
    server.document-root = "/srv/http/lena"

    fastcgi.server = (
        ".php" => (
            "php" => (
                "socket" => "/var/run/lighttpd/lighttpd-fastcgi-php-" + PID + ".socket",
                "bin-path" => "/usr/bin/php-cgi",
            )
        )
    )

    static-file.exclude-extensions = (".php", ".inc")
}

# In other case /srv/http/blank is served

Stage 3.5b: lighttpd with other applications, with Lasaña behind a directory

This is an alternative configuration file for the case you prefer serving Lasaña behind a URL prefix (i.e. http://rufian.eu/lasana/).

server.modules += (
    "mod_fastcgi",
    "mod_alias",
    "mod_rewrite"
)

server.document-root = "/srv/http/blank"
server.port = 80

server.username = "www-data"
server.groupname = "www-data"

dir-listing.activate = "enable"
dir-listing.hide-dotfiles = "enable"
dir-listing.encoding = "utf-8"
dir-listing.set-footer = "Evil Corporation"
dir-listing.exclude = (".*~", ".*.swp$")

index-file.names = ("index.html", "index.php", "index.htm")

mimetype.assign = (
    ".html" => "text/html",
    ".txt" => "text/plain",
    ".jpg" => "image/jpeg",
    ".png" => "image/png",
    ".js"  => "application/javascript",
    ".css" => "text/css",
    ".ogg" => "audio/ogg",
    ".woff" => "application/font-woff",
    ".ttf" => "application/x-font-ttf",
)

fastcgi.server = (
    "/lasana.fcgi" => (
        "lasana" => (
            "socket" => "/var/run/lasana/lasana.sock",
            "check-local" => "disable",
            "allow-x-send-file" => "enable",
        )
    ),
)

alias.url = (
    "/lasana/static" => "/srv/lasana/static",
    "/lena" => "/srv/http/lena",
)

url.rewrite-once = (
    "^/lasana(/static.*)$" => "/lasana/$1",
    "^/lasana(/?.*)$" => "/lasana.fcgi/$1",
)

# Activate PHP only for some URLs
$HTTP["url"] =~ "^/lena(/.*)?$" {
    fastcgi.server = (
        ".php" => (
            "php" => (
                "socket" => "/var/run/lighttpd/lighttpd-fastcgi-php-" + PID + ".socket",
                "bin-path" => "/usr/bin/php-cgi",
            )
        )
    )

    static-file.exclude-extensions = (".php", ".inc")
}

# /srv/http/blank is served (without PHP) if URL does not start with /lasana nor /lena.

In this case, extra configuration is needed in settings.py. Seek and change these variables:

FORCE_SCRIPT_NAME = '/lasana'

STATIC_URL = '/lasana/static/'

##Stage 3.6: Make Lasaña start up with the system

In order to make lighttpd start with the system, your system already has a way to do it (and in the case of Debian, it does by default). Just make sure to place your lighttpd.conf in /etc/lighttpd/lighttpd.conf.

In order to make Lasaña application (the one that listens through FastCGI) start with the system, there are two approaches:

  • Use something your system has to start Lasaña with the system and optionally stop and restart it. This is the best approach, specially with many web applications, because a restart of the web server or the web application does not require restarting the other.

  • Make lighttpd start Lasaña just like it does with PHP. This has the advantage of that the procedure is the same on different platforms but it is not supported at the moment.

###Stage 3.6a: Make Lasaña start up with the system using systemd

systemd makes this task really straighforward..., if your system does use systemd.

The need to create the /var/run/lasana directory adds a bit of a handicap, though.

First, place the following in `/etc/systemd/system/lasana_socket.service:

[Unit]
Description=Creates the directory /var/run/lasana

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'mkdir -p -m 755 /var/run/lasana && chown lasana: /var/run/lasana'

[Install]
WantedBy=multi-user.target

Then, create a file named /etc/systemd/system/lasana.service and write this in it:

[Unit]
Description=Lasana with FastCGI
After=lasana_socket.service

[Service]
User=lasana
ExecStart=/srv/lasana/env/bin/python /srv/lasana/lasana_proj/manage.py runfcgi daemonize=false socket=/var/run/lasana/lasana.sock socketumask=0007

[Install]
WantedBy=multi-user.target

Then enable and start them, running the following as root.

systemctl enable lasana_socket.service lasana.service
systemctl start lasana_socket.service lasana.service

###Stage 3.6b: Make Lasaña start up with the system using Debian initscripts

initscripts suck, but they are the status quo.

As root, create the following script in /etc/init.d/lasana.

### BEGIN INIT INFO
# Provides:             lasana
# Required-Start:       $syslog
# Required-Stop:        $syslog
# Default-Start:        2 3 4 5
# Default-Stop:         0 1 6
# Short-Description:    Lasana with FastCGI.
### END INIT INFO

NAME=lasana
LONG_NAME="Lasana with FastCGI"
RUN_USER=lasana
RUN_BIN=/srv/lasana/env/bin/python
RUN_OPTIONS="/srv/lasana/lasana_proj/manage.py runfcgi daemonize=false socket=/var/run/lasana/lasana.sock socketumask=0007"
RUN_PIDFILE="/var/run/$NAME.pid"

test -x $RUN_BIN || exit 1

# Define LSB log_* functions.
. /lib/lsb/init-functions


d_start() {
    mkdir -p -m 755 /var/run/lasana
    chown lasana: /var/run/lasana
    start-stop-daemon --background --make-pidfile --start --quiet --pidfile $RUN_PIDFILE \
                --chuid $RUN_USER --exec $RUN_BIN -- $RUN_OPTIONS
}


d_stop() {
    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $RUN_PIDFILE \
                --user $RUN_USER --exec $RUN_BIN
}

case "$1" in
  start)
        log_daemon_msg "Starting $LONG_NAME" $NAME
        d_start
        log_end_msg $?
        ;;
  stop)
        log_daemon_msg "Stopping $LONG_NAME" $NAME
        d_stop
        log_end_msg $?
        ;;

  restart)
        log_daemon_msg "Restarting $LONG_NAME" $NAME
        d_stop
        d_start
        echo "."
        ;;
  status)
        status_of_proc -p $RUN_PIDFILE $RUN_BIN $NAME || exit $?
        ;;

  *)
        log_action_msg "Usage: $NAME {start|stop|status|restart}" >&2
        exit 2
        ;;
esac

exit 0

Those 62 lines do no more than the systemd configuration file of the previous section. Cool.

Give it execution permission and start it with the following command (as root):

chmod +x /etc/init.d/lasana
/etc/init.d/lasana start

Add it to the runlevels running the following commands (as root again):

update-rc.d lasana defaults

Reboot to be sure it works, and done.

Stage 4: Set the washer to remove expired meals

As lasana user, run the following:

crontab -e

The lasana user crontab will show. Add this line.

*/5 *  *   *   *     /srv/lasana/env/bin/python /srv/lasana/lasana_proj/manage.py wash > /dev/null

This will check for expired meals (uploaded files) every five minutes and delete them.

Please note that if a meal is expired, Lasaña won't serve it even if it hasn't been removed, so the checking interval does not affect validity of expiration times.

And with this you are finally done. Have fun with your Lasaña.

⚠️ **GitHub.com Fallback** ⚠️