how to deploy python/django application in debian/apache?

235 views
Skip to first unread message

John Travolta

unread,
Feb 10, 2016, 3:23:05 AM2/10/16
to Django users
Hi,

I succeeded to run development python application: python3 ./manage.py runserver 0.0.0.0:8080

but now I need to make deployment, honestly I didn't succed to do it from august. I hope here I will get help.

first of all, settings.py had to be corrected from debug = true to DEBUG = False & ALLOWED_HOSTS = ['*',] should be with domain name, let's say: mijaw.comALLOWED_HOSTS = ['https://mijaw.com',]

I use debian + apache with iRedMail (I think iredmail already installed wsgi module for apache), all my sites at my VPS are redirected from port 80 to 443, visitors are forced to use httpS, it means when I tried 443 and 80, port was alrwady in use and I had to choose 8080. but I want that my python application is deployed with ssl, 443 port. 
if it is too much complicated let's try first with 8080, it doesn't matter so much,

the biggest problem for me is connecting python application (placed in root folder) with domain/apache configuration file (virtual host): /etc/apache2/sites-available/mijaw-ssl.conf
mijaw-ssl.conf is apache configuration file for domain with port 443. as I said, it is forced from 80 to 443, so, it is httpS://mijaw.com

first I will tell you my python paths:
my settings.py is located at: /root/mercury/mercury
but there is also urls.py and wsgi.py in this folder: /root/mercury/mercury
admins.py, urls.py, templates folder are located at: /root/mercury/messages
my virtualenv folder is: /root/env/bin/activate
if static folder is important, there are css,fonts,javascript and other folders: /root/mercury/static
html files are here: /root/mercury/messages/templates

my problem is: all of this should be connected with /etc/apache2/sites-available/mijaw-ssl.conf

here it is automatically created by iRedMail, I suppose it should be added similar for my python app:

<IfModule mod_ssl.c>
<VirtualHost mijaw.com:443>
 ServerAdmin webmaster@localhost
 DocumentRoot /var/www/html

SSLEngine on
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256.............................blah-blah

SSLHonorCipherOrder on

SSLCertificateFile /etc/ssl/certs/iRedMail.crt
SSLCertificateKeyFile /etc/ssl/private/iRedMail.key
...............

<FilesMatch "\.(cgi|shtml|phtml|php)$">
                                SSLOptions +StdEnvVars
                </FilesMatch>
                <Directory /usr/lib/cgi-bin>
                                SSLOptions +StdEnvVars
                </Directory>

Alias /iredadmin/static "/opt/www/iredadmin/static/"
WSGIScriptAlias /iredadmin "/opt/www/iredadmin/iredadmin.py/"
Alias /mail "/opt/www/roundcubemail/"
Alias /awstats/icon "/usr/share/awstats/icon/"
Alias /awstatsicon "/usr/share/awstats/icon/"
ScriptAlias /awstats "/usr/lib/cgi-bin/"
 
</VirtualHost>
</IfModule>

I checked of module wsgi is running and it is running: 
apache2ctl -t -D DUMP_MODULES
result: wsgi_module (shared)

should I add somewhere also: AddHandler wsgi-script .wsgi

so, all in all, I should connected python app placed in root folder with apache configuration and domain running with 443 port/https, so I would like secure connection for my python application.
wsgi_module (shared)

John Travolta

unread,
Feb 10, 2016, 3:30:17 AM2/10/16
to Django users
I don't know how to edit message, if it is important, here is path from yesterday, location of Django:
Requirement already satisfied (use --upgrade to upgrade): Django==1.8.3 in /root/env/lib/python3.4/site-packages (from -r requirements.txt (line 1))

James Schneider

unread,
Feb 10, 2016, 4:37:36 AM2/10/16
to django...@googlegroups.com


On Feb 10, 2016 12:23 AM, "John Travolta" <travo...@gmail.com> wrote:
>
> Hi,
>
> I succeeded to run development python application: python3 ./manage.py runserver 0.0.0.0:8080
>
> but now I need to make deployment, honestly I didn't succed to do it from august. I hope here I will get help.
>
> first of all, settings.py had to be corrected from debug = true to DEBUG = False & ALLOWED_HOSTS = ['*',] should be with domain name, let's say: mijaw.com, ALLOWED_HOSTS = ['https://mijaw.com',]
>
> I use debian + apache with iRedMail (I think iredmail already installed wsgi module for apache), all my sites at my VPS are redirected from port 80 to 443, visitors are forced to use httpS, it means when I tried 443 and 80, port was alrwady in use and I had to choose 8080. but I want that my python application is deployed with ssl, 443 port. 
> if it is too much complicated let's try first with 8080, it doesn't matter so much,
>
> the biggest problem for me is connecting python application (placed in root folder) with domain/apache configuration file (virtual host): /etc/apache2/sites-available/mijaw-ssl.conf
> mijaw-ssl.conf is apache configuration file for domain with port 443. as I said, it is forced from 80 to 443, so, it is httpS://mijaw.com
>

Don't place your project in the root folder, meaning both /root on the box, or wherever Apache has configured as the root folder (probably /var/www/html or similar). WSGI can access your project outside of the Apache root, and if left there, a bad configuration in Apache can expose your source code, including your settings file and consequently, your database password, among other things. Pick a folder somewhere you can remember, maybe /srv/django or /var/lib/django. Wherever you put it, make sure that the Apache system user has read access to that directory and all subdirectories, and no write access (special consideration may be needed for an SQLite DB file).

> first I will tell you my python paths:
> my settings.py is located at: /root/mercury/mercury
> but there is also urls.py and wsgi.py in this folder: /root/mercury/mercury
> admins.py, urls.py, templates folder are located at: /root/mercury/messages
> my virtualenv folder is: /root/env/bin/activate
> if static folder is important, there are css,fonts,javascript and other folders: /root/mercury/static
> html files are here: /root/mercury/messages/templates
>

Same goes for your virtualenv. Move it out of your /root directory. You probably should just recreate the virtualenv at the new location, rather than trying to move the existing one. The process is easy enough to duplicate your existing one with a freeze output from pip.

> my problem is: all of this should be connected with /etc/apache2/sites-available/mijaw-ssl.conf
>
> here it is automatically created by iRedMail, I suppose it should be added similar for my python app:
>

So you already have an instance of iRedMail running using HTTPS on the server? That heavily complicates your deployment. What site do you expect to show up when you go to the domain?

Trying not to get into a technical discussion about how HTTPS works, but basically you can only have a single site running on a single IP on the server, unless your server is capable of handling something called the Server Name Indicator (SNI).

You have 4 choices:

A. Completely remove the instance of iRedMail from Apache and update the configuration to only serve your Django site via WSGI. See https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/modwsgi/ for the Apache config.

B. Acquire a second public IP address for the server and attach your Django site to it with its own TLS certificate. All requests for Django would go to the new IP, while your original IP would still serve iRedMail, each with their own TLS certificate.

C. Configure an SNI domain and certificate within Apache, assuming your version of Apache and OpenSSL support it. Most recent distros should, but it's still a new enough feature that you should check.

D. Deploy your site on a separate VPS without a conflicting HTTPS configuration.

HTTPS is not fun to deal with, especially when dealing with publicly signed certificates, intermediate CA chains (which are quite common), DNS, and SNI. Include on top of that several SSL/TLS versions and cipher types that are being deprecated by browsers on an ongoing basis, and keeping up with the various OpenSSL vulnerabilities that are now more frequent since the POODLE attack was made famous and brought attention to the fragmented and complex code base that OpenSSL uses (which they're fixing, but it's a slow process). Your system updates will take care of most of that for you, but a few of the things I mentioned would require configuration file changes down the road.

There's a bit of work involved in keeping the green padlock icon in the browser (visit the login page for your bank website and look for it, usually near the address bar or done message saying that this site is secure), and avoiding the "this site can't be trusted, continue anyway?" errors in the browser.

I guess what I'm trying to say is, what you're trying to do is possible, but not easy if you aren't familiar with how Apache (or insert name of web server process) handles HTTPS. All of them will have the same trouble, and you can't run more than one on the same port.

You can run Apache on a different port using HTTPS, though, as you mentioned. But that obviously changes your URL to include the non-standard port (https://mydomain.com:8080). Don't expect that to climb very high in the search rankings, if the crawlers find it at all (if you care about that sort of thing).

Once you get past all that, though, and assuming you generate relative URL's within Django (which is the default using reverse() or {% url %} in your templates), then your site should run fine assuming you make the settings tweaks recommended by the Django team for HTTPS connections: https://docs.djangoproject.com/en/1.9/topics/security/#ssl-https

I apologize if this seems a bit cynical. Having done other HTTPS deployments with Apache (not with Django specifically, but the process is the same, Django itself is the easy part), I've found that you'll almost never get the HTTPS settings right on the first, second, or 15th try. Even with experience, it's difficult to get everything lined up correctly due to the number of components that require configuration for a successful connection. I'm not trying to dissuade you from attempting to implement HTTPS, just trying to warn and prepare you for the many esoteric issues that may creep up. It'll be a good learning experience.

-James

John Travolta

unread,
Feb 10, 2016, 6:53:33 AM2/10/16
to Django users
Hi James, thank you for so detailed answer in general about this problematic.

1) I am agreed with you that "root" folder is not the best place for my application. I thought if I try to chose another location for python app, paths in app will be different and it will not work. but I will try it later when I use my computer, as you said (new env and new location) and I will see what will happen. I will see also how to make Apache just to read and not to write in that folder.

2) iRedMail had redirection in apache from mijaw.com to mijaw.com/mail but I deleted that redirection and I have now clear apache default welcome page when I visit mijaw.com it means I must type mijaw.com/mail to visit iRedMail/roundcube. although https://mijaw.com and https://mijaw.com/mail are using the same ssl certificate from iRedMail. so, mijaw.com is clear apache welcome page & that's the place for python application, when visitors type https://mijaw.com they should get/visit my python app.

3) about single site: possibly you speak about single python site? but beside python can I have other sites as subdomains?
in this moment, I have VPS with one IP address and 2 domains on the same IP.
mijaw.com has one subdomain, html/css website, but it is subdomain like library.mijaw.com, mijaw.com is free for python application, another domain has several subdomains (flash, wordpress and 2 html/css websites).
so, I think there should be no problem to connect python app with this domain mijaw.com because it is default apache welcome page, I should be able to connect it with my python app. am I wrong?

4) this is exactly what I can't do, the paths are nightmare for me, I cannot connect python with apache: https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/modwsgi/
look this, I will never succeed to implement this general path into my particular case:
WSGIPythonPath /path/to/mysite.com:/path/to/your/venv/lib/python3.X/site-packages
I suppose I should chose that apache/wsgi option because I use virtualenv.

5) well, in the case it will not work in apache because of iredmail and other sites, can I install parallel nginx with apache and connect mijaw.com with nginx? problem is that I never used nginx than only apache, but I could try it, if it is necessary. than apache and iredmail would use mijaw.com/mail and nginx would use mijaw.com i.e. python app.

but in general, I would like to try first in apache, so, I would need help to implement this below in my particular case, I wrote in my first post my application paths:
WSGIPythonPath /path/to/mysite.com:/path/to/your/venv/lib/python3.X/site-packages

if somebody solve me this problem, I will move app from root folder in SRV or similar and change it in this path.

this is also killing me, I suppose it should be written in apache conf file "mijaw-ssl.conf":
WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py
WSGIPythonPath /path/to/mysite.com
Alias /robots.txt /path/to/mysite.com/static/robots.txt
Alias /favicon.ico /path/to/mysite.com/static/favicon.ico
Alias /media/ /path/to/mysite.com/media/
Alias /static/ /path/to/mysite.com/static/
<Directory /path/to/mysite.com/mysite>



6) of course, if I can use other port for https for django, it is good. just I don't know where are this https and port settings in my python app: https://docs.djangoproject.com/en/1.9/topics/security/#ssl-https
I will try to find later this string: SECURE_SSL_REDIRECT to True.
btw, I don't mind for search rankings :)

thank you for your answer. I will use later my computer and relocate app, try to find string and all the rest.

Fred Stluka

unread,
Feb 10, 2016, 10:16:41 AM2/10/16
to django...@googlegroups.com
On 2/10/16 4:37 AM, James Schneider wrote:
... make sure that the Apache system user has read access to that directory and all subdirectories, and no write access ...
James,

Won't that prevent Django from creating *.pyc files from the
deployed *.py files?  Or do you suggest deploying *.pyc files?

--Fred
Fred Stluka -- mailto:fr...@bristle.com -- http://bristle.com/~fred/
Bristle Software, Inc -- http://bristle.com -- Glad to be of service!
Open Source: Without walls and fences, we need no Windows or Gates.

James Schneider

unread,
Feb 10, 2016, 1:58:13 PM2/10/16
to django...@googlegroups.com
On Wed, Feb 10, 2016 at 7:15 AM, Fred Stluka <fr...@bristle.com> wrote:
On 2/10/16 4:37 AM, James Schneider wrote:
... make sure that the Apache system user has read access to that directory and all subdirectories, and no write access ...
James,

Won't that prevent Django from creating *.pyc files from the
deployed *.py files?  Or do you suggest deploying *.pyc files?

--Fred

Oh, yep, you're right. It was 1:30am and I was in PHP mode in my head (I'm not getting kicked off the list for mentioning that word, right? ;-D). I believe that means that the Apache user needs write access to the project folders (to create the .pyc files), but still only needs read access to the .py files. I believe that you can tweak the write access to the .pyc files and parent folders after they have been generated by mod_wsgi/Apache. You can also do a deployment just using just the .pyc files, if I remember correctly. 

Thanks for the double-check.

-James

Michal Petrucha

unread,
Feb 10, 2016, 3:04:49 PM2/10/16
to django...@googlegroups.com
I'm not entirely convinced this is the best idea, to be honest. Sure,
it might be possible, but I fail to see the benefit you get out of
that, compared to just granting the apache user full write access.

If the user has read-only access to *.py files, but read-write access
to *.py[co], a hypothetical attacker with the ability to write to
arbitrary files in your filesystem could just write bytecode directly
to the bytecode files instead of writing plaintext to sources, with
the same effect (which is arbitrary code execution).

The only benefit you get out of it is a much more complicated set of
permissions on your files that will make your life harder to set up
correctly, and easy to mess up, thus potentially breaking the
application in subtle ways. (Unless, of course, there's some scenario
that I'm overlooking, which is perfectly possible.)

I see several options:

* You can decide you don't need the startup speed benefits of having
pre-compiled bytecode files, just deploy read-only source files. As
long as your application server (mod_wsgi?) runs in long-lived
processes, the bytecode files only really play any role on startup,
when everything gets imported, and might make the difference of, I
don't know, a second, tops (unless you have *really* many Python
modules that get imported on startup – take this as a very rough
estimate, and try to measure the difference for yourself instead of
taking my word for it). If that delay is acceptable to you, just
ignore bytecode files.

* You could generate bytecode files beforehand as part of your
deployment infrastructure (for example when generating the
package you install on your servers), and install them read-only
along with the plaintext source files. For example, if you use pip
to install your application on your servers, it will create *.pyc
files automatically with the --compile flag [1].

* You could decide that creating this particular layer of defense
against an attacker with the ability to write to arbitrary files is
not worth it, and just go full read-write. It really depends on the
security requirements and trade-offs you're willing to accept.

In one project, we're going with the second option, where we build
DEB/RPM packages, and include all bytecode files inside those
packages, only writable by root, while the application runs as a
separate user. In other projects we just create a separate user for
every application we deploy, and run those applications from those
users' home directories (where they are checked out from their git
repositories).

Cheers,

Michal


[1]: https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption--compile
signature.asc
Reply all
Reply to author
Forward
0 new messages