Skip to content

Build apache.conf files for django based on a simple .ini file.

License

Notifications You must be signed in to change notification settings

datakortet/dkbuild-apacheconf

Repository files navigation

https://travis-ci.org/datakortet/dkbuild-apacheconf.svg?branch=master

dkbuild-apacheconf - build apache.conf from site.ini and server.ini

NOTE / WARNING

This code is currently more proof-of-concept than reliable tool.. you have been warned.

The generated file relies on a definition of SRV (which requires an Apache that supports mod_define):

Define SRV  /srv

The generated file is currently hardcoded to our setup in several ways and you'll almost certainly want to edit it by hand after generating it.

Alternatively you can modify the apache.conf.jinja template to suit your needs. If you think your changes are of general interest we're very happy to receive PRs.

Please report any bugs on github: https://github.com/datakortet/dkbuild-apacheconf/issues/new

Known issues:

  • we expect to be on a Ubuntu installation of Apache
  • sites are expected to be at ${SRV}/www/<sitename>
  • virtualenvs are expected to be found at ${SRV}/venv/<venvname>
  • Django static and media folders are expected to be linked to ${SRV}/www/<sitename>/static (and similarly for media).
  • Certain static files are expected to be under ${SRV}/www/docroot:
    • favicon.ico
    • robots.txt
    • cookies.html (cookie policy)
    • privacy-policy.html

Installation

I'm using the virtualenv-win package to create the virtualenv here, you can of course do it any way you want:

c:\srv> mkvirtualenv apb
New python executable in c:\srv\venv\apb\Scripts\python.exe
Installing setuptools, pip, wheel...
done.

(apb) c:\srv> cdvirtualenv
(apb) c:\srv\venv\apb> git clone https://github.com/datakortet/dkbuild-apacheconf.git
Cloning into 'dkbuild-apacheconf'...
...
(apb) c:\srv\venv\apb> cd dkbuild-apacheconf

(apb) c:\srv\venv\apb\dkbuild-apacheconf> pip install -r requirements.txt
...
(apb) c:\srv\venv\apb\dkbuild-apacheconf> pip install -e .
Obtaining file:///C:/srv/venv/apb/dkbuild-apacheconf
Installing collected packages: dkbuild-apacheconf
  Running setup.py develop for dkbuild-apacheconf
Successfully installed dkbuild-apacheconf

Creating a sample site.ini file:

(apb) c:\srv\venv\apb\dkbuild-apacheconf> cd ..

(apb) c:\srv\venv\apb> mkdir mysite

(apb) c:\srv\venv\apb> cd mysite

(apb) c:\srv\venv\apb\mysite> emacs site.ini

(apb) c:\srv\venv\apb\mysite> cat site.ini
[site]
sitename = example
dns = www.example.com
virtual_env = prod_142
www_prefix = require
https = yes

[ssl]
certpath = /srv/ssl/mycert/2019/42
cert_file = example.crt
key_file = example.key
chain_file = YourPreferredCA.crt

[wsgi]
processes = 2
threads = 20

Now generate the apache.conf file:

(apb) c:\srv\venv\apb\mysite> dkbuild-apacheconf --skip-server run

(apb) c:\srv\venv\apb\mysite> dir
 Volume in drive C has no label.
 Volume Serial Number is 04D6-81F0

 Directory of c:\srv\venv\apb\mysite

2019-04-18  15:24    <DIR>          .
2019-04-18  15:24    <DIR>          ..
2019-04-18  15:24             7,405 apache.conf
2019-04-18  15:22               278 site.ini
               2 File(s)          7,683 bytes
               2 Dir(s)  320,047,165,440 bytes free

(apb) c:\srv\venv\apb\mysite>

The generated apache.conf file can e.g. be linked to your /etc/apache2/sites-available directory (you'll likely need to do this using sudo):

ln -s apache.conf /etc/apache2/sites-available/www-example.conf

and then enabled/disabled the usual way, e.g.:

a2ensite www-example
a2dissite www-example

The generated apache.conf from the site.ini file above:

<VirtualHost *:443>

    ServerName www.example.com
    ServerAdmin bp@example.com

    # these will be availablee from request.META
    SetEnv DK_SITE_NAME example
    SetEnv DK_SITE_DNS www.example.com
    SetEnv DK_SITE_ROOT ${SRV}/www/example
    SetEnv DK_SRV_ROOT ${SRV}
    SetEnv DKSTATICROOT ${SRV}/data/static
    SetEnv DK_VIRTUAL_ENV prod_142

    DocumentRoot ${SRV}/www/example/docroot

    <Directory "${SRV}/www/example/docroot">
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
##:41

    ErrorLog /var/log/apache2/example-error.log
    CustomLog /var/log/apache2/example-access.log combined
    ServerSignature Off

    <Location /server-status>
        SetHandler server-status
        Order allow,deny
        Allow from ${AllowFromISP}
        Allow from 127.0.0.1
    </Location>

    SetOutputFilter DEFLATE
    AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-javascript text/css application/javascript image/svg+xml
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|pdf|swf)$ no-gzip dont-vary
    Header append Vary User-Agent env=!dont-vary
    AddType image/svg+xml svg svgz
    AddEncoding gzip svgz
##:70
    # SSL Certificates
    SSLEngine on
    SSLCertificateFile /srv/ssl/mycert/2019/42/example.crt
    SSLCertificateKeyFile /srv/ssl/mycert/2019/42/example.key
    SSLCertificateChainFile /srv/ssl/mycert/2019/42/YourPreferredCA.crt

    SetEnvIf User-Agent ".*MSIE.*" ssl-unclean-shutdown

    # IE >= 9, Android >= 2.2, Java >= 6
    # SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
    # The real world is ugly..
    SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4
    SSLProtocol all -SSLv2 -SSLv3
    SSLHonorCipherOrder On
    Header always set X-Frame-Options SAMEORIGIN
    Header always set X-Content-Type-Options nosniff

##:100
    # Requires Apache >= 2.4
    #SSLCompression off
    #SSLSessionTickets Off
    #SSLUseStapling on
    #SSLStaplingCache "shmcb:logs/stapling-cache(150000)"

    # Due to CRIME and BREACH it is not safe to use compression over https,
    # howvever, turning it off generally has major performance repercussions..
    # SetEnv no-gzip 1

    # BREACH migitation (turn off gzip only for foreign requests)
    SetEnvIfNoCase Referer .* self_referer=no
    SetEnvIfNoCase Referer ^https://www\.example\.com/ self_referer=yes
    SetEnvIf self_referer ^no$ no-gzip
    Header append Vary User-Agent env=!dont-vary

    <FilesMatch "\.(ico|flv|jpg|jpeg|png|svg|svgz|gif|woff2)$">
        Header set Cache-Control "public"
    </FilesMatch>

    ExpiresActive On
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType application/pdf "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType text/css "access plus 1 year"

    FileETag MTime Size

    <IfModule mod_rewrite.c>
        RewriteEngine On

        RewriteCond %{HTTP_HOST} !^www\. [NC]
        RewriteRule ^(.*)$ https://www.example.com/ [L,R=301]

        RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK|OPTIONS|PROPFIND)
        RewriteRule .* - [F]
    </IfModule>

    WSGIDaemonProcess example \
        display-name=example \
        processes=2 threads=20 \
        maximum-requests=10000 \
        umask=0002 \
        python-path=${SRV}/www:${SRV}/src:${SRV}/venv/prod_142/lib/python2.7/site-packages \
        python-eggs=${SRV}/.python-eggs

    WSGIScriptAlias / ${SRV}/www/example/wsgi.py  \
        process-group=example      \
        application-group=%{GLOBAL}

##:204
    Alias /favicon.ico ${SRV}/www/example/docroot/favicon.ico
    Alias /robots.txt ${SRV}/www/example/docroot/robots.txt
    Alias /cookies.html ${SRV}/www/example/docroot/cookies.html
    Alias /privacy-policy.html ${SRV}/www/example/docroot/privacy-policy.html

    Alias /static/ ${SRV}/www/example/static/
    Alias /media/ ${SRV}/www/example/media/

</VirtualHost>

<VirtualHost *:80>

    ServerName www.example.com
    ServerAdmin bp@norsktest.no

    # these will be availablee from request.META
    SetEnv DK_SITE_NAME example
    SetEnv DK_SITE_DNS www.example.com
    SetEnv DK_SITE_ROOT ${SRV}/www/example
    SetEnv DK_SRV_ROOT ${SRV}
    SetEnv DKSTATICROOT ${SRV}/data/static
    SetEnv DK_VIRTUAL_ENV prod_142

    DocumentRoot ${SRV}/www/example/docroot

    <Directory "${SRV}/www/example/docroot">
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>
##:41

    ErrorLog /var/log/apache2/example-error.log
    CustomLog /var/log/apache2/example-access.log combined
    ServerSignature Off

    <Location /server-status>
        SetHandler server-status
        Order allow,deny
        Allow from ${AllowFromISP}
        Allow from 127.0.0.1
    </Location>

    SetOutputFilter DEFLATE
    AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-javascript text/css application/javascript image/svg+xml
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|pdf|swf)$ no-gzip dont-vary
    Header append Vary User-Agent env=!dont-vary
    AddType image/svg+xml svg svgz
    AddEncoding gzip svgz
##:70

    <FilesMatch "\.(ico|flv|jpg|jpeg|png|svg|svgz|gif|woff2)$">
        Header set Cache-Control "public"
    </FilesMatch>

    ExpiresActive On
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType application/pdf "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType text/css "access plus 1 year"

    FileETag MTime Size

    <IfModule mod_rewrite.c>
        RewriteEngine On

        RewriteCond %{HTTP_HOST} !^www\. [NC]
        RewriteRule ^(.*)$ http://www.example.com/ [L,R=301]

        RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK|OPTIONS|PROPFIND)
        RewriteRule .* - [F]
    </IfModule>

    WSGIProcessGroup example
    WSGIScriptAlias / ${SRV}/www/example/wsgi.py

##:204
    Alias /favicon.ico ${SRV}/www/example/docroot/favicon.ico
    Alias /robots.txt ${SRV}/www/example/docroot/robots.txt
    Alias /cookies.html ${SRV}/www/example/docroot/cookies.html
    Alias /privacy-policy.html ${SRV}/www/example/docroot/privacy-policy.html

    Alias /static/ ${SRV}/www/example/static/
    Alias /media/ ${SRV}/www/example/media/

</VirtualHost>

The corresponding wsgi.py file (it has not been tested with py3 yet):

"""
WSGI config for www_datakortet project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/
"""
from ConfigParser import ConfigParser

import sys, os

# server variables
DK_SITE_ROOT = os.path.split(__file__)[0]
SITE_ROOT = DK_SITE_ROOT
DK_WWW = os.path.split(DK_SITE_ROOT)[0]
WWWDIR = DK_WWW
DK_SRV = os.path.split(DK_WWW)[0]
SRV = DK_SRV

config = ConfigParser()
config.read([os.path.join(DK_SITE_ROOT, 'site.ini'),
             os.path.join(DK_SRV, 'server.ini')])

# site properties
SHORT_NAME = config.get('site', 'shortname')
SITE_NAME = config.get('site', 'sitename')
DNS = config.get('site', 'dns')


# virtualenv variables
VENV_NAME = config.get('site', 'venv')

# derived settings
DJANGO_SETTINGS_MODULE = "%s.settings" % SITE_NAME
VIRTUAL_ENV = "%s/venv/%s" % (SRV, VENV_NAME)

# activate virtualenv
# (warning: https://code.google.com/p/modwsgi/wiki/CheckingYourInstallation)
_activate = "%s/%s/activate_this.py" % (
    VIRTUAL_ENV,
    'Scripts' if sys.platform == 'win32' else 'bin'
)
if sys.version_info >= (3, 0):
    exec(compile(open(_activate, 'rb').read(), _activate, 'exec'))
else:
    execfile(_activate, dict(__file__=_activate))

# make site directory importable
sys.path.insert(0, WWWDIR)

# set all GLOBAL vars as environment variables
for _varname in [k for k in globals().keys() if k == k.upper()]:
    _val = globals()[_varname]
    if type(_val) == str:
        os.environ[_varname] = _val

# This application object is used by the development server
# as well as any WSGI server configured to use this file.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

About

Build apache.conf files for django based on a simple .ini file.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages