176 lines
6.8 KiB
Python
176 lines
6.8 KiB
Python
|
import os
|
||
|
|
||
|
from django.template import Template, Context
|
||
|
from django.utils.translation import ugettext_lazy as _
|
||
|
|
||
|
from orchestra.apps.orchestration import ServiceBackend
|
||
|
|
||
|
from .. import settings
|
||
|
|
||
|
|
||
|
class Apache2Backend(ServiceBackend):
|
||
|
model = 'websites.Website'
|
||
|
related_models = (('websites.Content', 'website'),)
|
||
|
verbose_name = _("Apache 2")
|
||
|
|
||
|
def save(self, site):
|
||
|
context = self.get_context(site)
|
||
|
extra_conf = self.get_content_directives(site)
|
||
|
if site.protocol is 'https':
|
||
|
extra_conf += self.get_ssl(site)
|
||
|
extra_conf += self.get_security(site)
|
||
|
context['extra_conf'] = extra_conf
|
||
|
|
||
|
apache_conf = Template(
|
||
|
"# {{ banner }}\n"
|
||
|
"<VirtualHost *:{{ site.port }}>\n"
|
||
|
" ServerName {{ site.domains.all|first }}\n"
|
||
|
"{% if site.domains.all|slice:\"1:\" %}"
|
||
|
" ServerAlias {{ site.domains.all|slice:\"1:\"|join:' ' }}\n"
|
||
|
"{% endif %}"
|
||
|
" CustomLog {{ logs }} common\n"
|
||
|
" SuexecUserGroup {{ user }} {{ group }}\n"
|
||
|
"{% for line in extra_conf.splitlines %}"
|
||
|
" {{ line | safe }}\n"
|
||
|
"{% endfor %}"
|
||
|
"</VirtualHost>\n"
|
||
|
)
|
||
|
apache_conf = apache_conf.render(Context(context))
|
||
|
apache_conf += self.get_protections(site)
|
||
|
context['apache_conf'] = apache_conf
|
||
|
|
||
|
self.append(
|
||
|
"{ echo -e '%(apache_conf)s' | diff -N -I'^\s*#' %(sites_available)s - ; } ||"
|
||
|
" { echo -e '%(apache_conf)s' > %(sites_available)s; UPDATED=1; }" % context
|
||
|
)
|
||
|
self.enable_or_disable(site)
|
||
|
|
||
|
def delete(self, site):
|
||
|
context = self.get_context(site)
|
||
|
self.append("a2dissite %(site_unique_name)s.conf && UPDATED=1" % context)
|
||
|
self.append("rm -fr %(sites_available)s" % context)
|
||
|
|
||
|
def commit(self):
|
||
|
""" reload Apache2 if necessary """
|
||
|
self.append('[[ $UPDATED == 1 ]] && service apache2 reload')
|
||
|
|
||
|
def get_content_directives(self, site):
|
||
|
directives = ''
|
||
|
for content in site.content_set.all().order_by('-path'):
|
||
|
method, args = content.webapp.get_method()
|
||
|
method = getattr(self, 'get_%s_directives' % method)
|
||
|
directives += method(content, *args)
|
||
|
return directives
|
||
|
|
||
|
def get_alias_directives(self, content, *args):
|
||
|
context = self.get_content_context(content)
|
||
|
context['path'] = args[0] % context if args else content.webapp.get_path()
|
||
|
return "Alias %(location)s %(path)s\n" % context
|
||
|
|
||
|
def get_fpm_directives(self, content, *args):
|
||
|
context = self.get_content_context(content)
|
||
|
context['fcgi_path'] = args[0] % context
|
||
|
directive = "ProxyPassMatch ^%(location)s(.*\.php(/.*)?)$ %(fcgi_path)s$1\n"
|
||
|
return directive % context
|
||
|
|
||
|
def get_fcgid_directives(self, content, fcgid_path):
|
||
|
context = self.get_content_context(content)
|
||
|
context['fcgid_path'] = fcgid_path % context
|
||
|
fcgid = self.get_alias_directives(content)
|
||
|
fcgid += (
|
||
|
"ProxyPass %(location)s !\n"
|
||
|
"<Directory %(app_path)s>\n"
|
||
|
" Options +ExecCGI\n"
|
||
|
" AddHandler fcgid-script .php\n"
|
||
|
" FcgidWrapper %(fcgid_path)s\n"
|
||
|
) % context
|
||
|
for option in content.webapp.options.filter(name__startswith='Fcgid'):
|
||
|
fcgid += " %s %s\n" % (option.name, option.value)
|
||
|
fcgid += "</Directory>\n"
|
||
|
return fcgid
|
||
|
|
||
|
def get_ssl(self, site):
|
||
|
cert = settings.WEBSITES_DEFAULT_HTTPS_CERT
|
||
|
custom_cert = site.options.filter(name='ssl')
|
||
|
if custom_cert:
|
||
|
cert = tuple(custom_cert[0].value.split())
|
||
|
directives = (
|
||
|
"SSLEngine on\n"
|
||
|
"SSLCertificateFile %s\n"
|
||
|
"SSLCertificateKeyFile %s\n"
|
||
|
) % cert
|
||
|
return directives
|
||
|
|
||
|
def get_security(self, site):
|
||
|
directives = ''
|
||
|
for rules in site.options.filter(name='sec_rule_remove'):
|
||
|
for rule in rules.split():
|
||
|
directives += "SecRuleRemoveById %d" % rule
|
||
|
|
||
|
for modsecurity in site.options.filter(name='sec_rule_off'):
|
||
|
directives += (
|
||
|
"<LocationMatch %s>\n"
|
||
|
" SecRuleEngine Off\n"
|
||
|
"</LocationMatch>\n" % modsecurity.value
|
||
|
)
|
||
|
return directives
|
||
|
|
||
|
def get_protections(self, site):
|
||
|
protections = ""
|
||
|
__, regex = settings.WEBSITES_OPTIONS['directory_protection']
|
||
|
for protection in site.options.filter(name='directory_protection'):
|
||
|
path, name, passwd = re.match(regex, protection.value).groups()
|
||
|
path = os.path.join(context['root'], path)
|
||
|
passwd = os.path.join(self.USER_HOME % context, passwd)
|
||
|
protections += ("\n"
|
||
|
"<Directory %s>\n"
|
||
|
" AllowOverride All\n"
|
||
|
# " AuthPAM_Enabled off\n"
|
||
|
" AuthType Basic\n"
|
||
|
" AuthName %s\n"
|
||
|
" AuthUserFile %s\n"
|
||
|
" <Limit GET POST>\n"
|
||
|
" require valid-user\n"
|
||
|
" </Limit>\n"
|
||
|
"</Directory>\n" % (path, name, passwd)
|
||
|
)
|
||
|
return protections
|
||
|
|
||
|
def enable_or_disable(self, site):
|
||
|
context = self.get_context(site)
|
||
|
self.append("ls -l %(sites_enabled)s; DISABLED=$?" % context)
|
||
|
if site.is_active:
|
||
|
self.append("if [[ $DISABLED ]]; then a2ensite %(site_unique_name)s.conf;\n"
|
||
|
"else UPDATED=0; fi" % context)
|
||
|
else:
|
||
|
self.append("if [[ ! $DISABLED ]]; then a2dissite %(site_unique_name)s.conf;\n"
|
||
|
"else UPDATED=0; fi" % context)
|
||
|
|
||
|
def get_context(self, site):
|
||
|
base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF
|
||
|
sites_available = os.path.join(base_apache_conf, 'sites-available')
|
||
|
sites_enabled = os.path.join(base_apache_conf, 'sites-enabled')
|
||
|
context = {
|
||
|
'site': site,
|
||
|
'site_name': site.name,
|
||
|
'site_unique_name': site.unique_name,
|
||
|
'user': site.account.user.username,
|
||
|
'group': site.account.user.username,
|
||
|
'sites_enabled': sites_enabled,
|
||
|
'sites_available': "%s.conf" % os.path.join(sites_available, site.unique_name),
|
||
|
'logs': os.path.join(settings.WEBSITES_BASE_APACHE_LOGS, site.unique_name),
|
||
|
'banner': self.get_banner(),
|
||
|
}
|
||
|
return context
|
||
|
|
||
|
def get_content_context(self, content):
|
||
|
context = self.get_context(content.website)
|
||
|
context.update({
|
||
|
'type': content.webapp.type,
|
||
|
'location': content.path,
|
||
|
'app_name': content.webapp.name,
|
||
|
'app_path': content.webapp.get_path(),
|
||
|
'fpm_port': content.webapp.get_fpm_port(),
|
||
|
})
|
||
|
return context
|