Multiple Domains, Subdomains, and Applications with SSL. Single web2py Instance on Apache

1,204 views
Skip to first unread message

Ross Peoples

unread,
Jul 1, 2011, 3:06:11 PM7/1/11
to web...@googlegroups.com
This post is meant to be informative, as I have been trying to get this working on my server for about a week now and I've finally got what I wanted. Sorry in advance if this turns out to be really long, but I'm hoping that my hard work and research will help someone else in the same situation.

Let's start with a problem statement: How do I run multiple web2py applications all using different domains/subdomains AND make those sites usable with SSL on Apache? Oh, and while we're at it, how do we keep existing PHP sites working? I am using Ubuntu 10.04 as a LAMP server in production and want to start hosting multiple web2py sites/applications on it as well.

The best recommendation I can make as far as getting web2py installed is to follow the documentation:

You will want to read the Apache Setup section and the first part of the mod_wsgi section where web2py is downloaded, unziped and owned by www-data. After that, the Apache config files mentioned are great for simple server setups, but we want something more complex, so we will make our own.

First let's go over what we have and what we need:

We have an existing PHP app running on oldapp.com. We have two web2py applications that we want to run and we have two domains for them: exampleapp.com and anotherexample.com. If you want to run any of these sites on SSL, then you need to get certificates for them. If they are simple apps, then you can get free 1-year certificates from StartSSL.com. I won't go into detail about how to get these certificates or decrypting the private keys, as that is a fairly broad topic and the documentation from your chosen certificate authority can probably explain it better than I can.

So now we assume you have your SSL certificates, now it's time to configure Apache and web2py. First, let's take care of the PHP application. Hopefully you will have each PHP site in it's own VirtualHost file, and not called 000-default. If it is, then you will need to run the following commands to disable the site, rename it, and re-enable it:

sudo a2dissite 000-default
sudo mv /etc/apache2/sites-available/000-default /etc/apache2/sites-available/php-oldapp
sudo a2ensite php-oldapp
sudo /etc/init.d/apache2 restart

Check to make sure your PHP site is still working, as we're done with that. Next, we need to create a default web2py configuration. So run your preferred text editor to create a new default file (I'll use nano here for simplicity):

sudo nano /etc/apache2/sites-available/000-default

Now, put the following in your new configuration file:

# This is the default VirtualHost and should be the first VirtualHost 

# This is required in order to run SSL web2py sites 
NameVirtualHost *:443 

# Make this process global so that other VirtualHosts can access it (i.e. SSL sites) 
WSGIDaemonProcess web2py user=www-data group=www-data display-name=%{GROUP} 

<VirtualHost *:80> 
  WSGIProcessGroup web2py 
  WSGIScriptAlias / /var/repositories/web2py/wsgihandler.py 

  <Directory /var/repositories/web2py/> 
    AllowOverride None 
    Order Allow,Deny 
    Deny from all 
    <Files wsgihandler.py> 
      Allow from all 
    </Files> 
  </Directory> 

  AliasMatch ^/([^/]+)/static/(.*) \ 
           /var/repositories/web2py/applications/$1/static/$2 
  <Directory /var/repositories/web2py/applications/*/static/> 
    Order Allow,Deny 
    Allow from all 
  </Directory> 

  <Location /admin> 
  Deny from all 
  </Location> 

  <LocationMatch ^/([^/]+)/appadmin> 
  Deny from all 
  </LocationMatch> 

  CustomLog /var/log/apache2/access.log common 
  ErrorLog //var/log/apache2/error.log 
</VirtualHost>


This configuration file will run all of your non-SSL web2py sites. You can map domain names to applications with web2py's routes.py file (which we will see later) without ever having to touch this Apache configuration file again. To test this, enable the site and restart Apache:

sudo a2ensite 000-default
sudo /etc/init.d/apache2 restart

web2py should be working now, but it may not work the way we want yet, as we have not configured its routers.py file yet. Now we need to set up SSL for our sites. In order to do this, we need a Apache configuration (VirtualHost) file for each domain/application. This is because each domain needs its own certificate and it's best to let Apache handle this for us. We need to make another file for our SSL-enabled exampleapp:

sudo nano /etc/apache2/sites-available/web2py-exampleapp-ssl

Put the following in the new configuration file:

<VirtualHost *:443> 
  ServerName exampleapp.com 
  ServerAlias *.exampleapp.com 

  SSLEngine on 
  SSLCertificateFile /var/repositories/web2py/exampleapp.com.crt 
  SSLCertificateKeyFile /var/repositories/web2py/exampleapp.com.key 

  WSGIProcessGroup web2py 
  WSGIScriptAlias / /var/repositories/web2py/wsgihandler.py 

  <Directory /var/repositories/web2py/> 
    AllowOverride None 
    Order Allow,Deny 
    Deny from all 
    <Files wsgihandler.py> 
      Allow from all 
    </Files> 
  </Directory> 

  AliasMatch ^/([^/]+)/static/(.*) \ 
           /var/repositories/web2py/applications/$1/static/$2 
  <Directory /var/repositories/web2py/applications/*/static/> 
    Order Allow,Deny 
    Allow from all 
  </Directory> 

  <Location /admin> 
  Deny from all 
  </Location> 

  <LocationMatch ^/([^/]+)/appadmin> 
  Deny from all 
  </LocationMatch> 

  CustomLog /var/log/apache2/access.log common 
  ErrorLog //var/log/apache2/error.log 
</VirtualHost>


Just replace "exampleapp" with the name of the domain and the appropriate names of the private key file and the certificate file. You can use this file as a template for other SSL sites, again, just change the domain name and SSL file names and you can have as many SSL-enabled sites as you have certificates.

Enable this site and restart Apache to make sure there were no configuration errors:

sudo a2enmod web2py-exampleapp-ssl
sudo /etc/init.d/apache2 restart

Now we need to configure web2py to map our domains to the proper applications. Open the routes.py file and configure the router:

routers = dict( 
    BASE = dict( 
        domains = { 
            'exampleapp.com': 'exampleapp', 
            'www.exampleapp.com': 'exampleapp', 
            'anotherexample.com': 'anotherexample', 
            'www.anotherexample.com': 'anotherexample', 
        } 
    ), 
)


This part is pretty simple. Just replace the domain/subdomain names on the left and the application names they belong to on the right. Finally, restart Apache again for the router changes to take effect:

sudo /etc/init.d/apache2 restart

Now test your sites to see if they work on both HTTP and HTTPS. If you have any trouble, I have attached some example files to this post that you can examine. If you're still having problems, let me know. I am no Apache expert by any stretch of the imagination, as I am still learning myself, but I'll try to help if there are any problems.
examples.zip

Ovidio Marinho

unread,
Jul 1, 2011, 5:51:00 PM7/1/11
to web...@googlegroups.com
Very Goog Tutorial. Congratulations.
      


       Ovidio Marinho Falcao Neto
             ovid...@gmail.com
                     88269088
                   Paraiba-Brasil



2011/7/1 Ross Peoples <ross.p...@gmail.com>

cjrh

unread,
Jul 2, 2011, 6:44:19 AM7/2/11
to web...@googlegroups.com
If you format this post in markmin and email it to me, I'll add it to the book as an additional subsection after Apache setup.

danto

unread,
Jul 12, 2011, 1:52:25 PM7/12/11
to web...@googlegroups.com
I don't know what should be wrong, but I cannot map 2 apps to different subdomains in the same domain, my routes.py is like the follow:

#!/usr/bin/python
#-*- coding: utf-8 -*-


routers = dict(
          BASE  = dict(
    domains = {
    'sub1.AAA.com' : 'app1',
    'sub2.AAA.com' : 'app2',
    }
    )
          )


All works when there're just 1 app mapped (sub1<->app1) but it stop working when I add the second statement (sub2 <-> app2).
I restart/stop/start apache after every change on routes.py

I'm using webfaction, if that is useful to know.

Ross Peoples

unread,
Jul 13, 2011, 7:48:04 AM7/13/11
to web...@googlegroups.com
it looks correct, and shouldn't make any difference as long as indentation is correct. Maybe try lowercasing the domain names. Also, do you have any other routing options besides this one?

danto

unread,
Jul 13, 2011, 9:45:46 AM7/13/11
to web...@googlegroups.com
2011/7/13 Ross Peoples <ross.p...@gmail.com>

it looks correct, and shouldn't make any difference as long as indentation is correct. Maybe try lowercasing the domain names. Also, do you have any other routing options besides this one?

yeah, it is very weird, but suddenly after stoping, wait for about 10 secs and then starting apache now it works. I was using the *restart* command of apache before but it didn't work, so maybe is something with the cache or alike. I remark on "suddenly" because I did the same procedure a few times before without changes. At least now is still working.

Thank you anyway, and for that you decided been informative to others in the first post.

best regards :)

Ross Peoples

unread,
Jul 13, 2011, 2:23:50 PM7/13/11
to web...@googlegroups.com
Glad you got it resolved. And I'm glad this is helping others. I should mention though that the Apache configurations mentioned in my original post disable admin and app admin access completely (even over SSL). This is a good idea unless you absolutely need it, since web2py's admin application doesn't have any protection against brute-force hacking attempts. However, I have submitted a patch that adds this protection to the admin application. If it gets accepted, it may be moderately safe to enable the admin application over SSL, but only if you need it. To enable admin over SSL, you would simply change these lines in the Apache site configuration files:

  <Location /admin> 
  Deny from all 
  </Location> 

  <LocationMatch ^/([^/]+)/appadmin> 
  Deny from all 
  </LocationMatch>


You can remove these lines, or simply put a hash mark (#) in front of the Deny from all part. Using the hash marks could make it easier to switch admin on and off if you only want it on when needed. Just keep in mind that you would need to run:

sudo /etc/init.d/apache2 reload

In order for the change to take effect. If for some reason the changes don't get applied, then doing an Apache restart instead of a reload should do the trick.
Reply all
Reply to author
Forward
0 new messages