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" "\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 %}" "\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" "\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 += "\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 += ( "\n" " SecRuleEngine Off\n" "\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" "\n" " AllowOverride All\n" # " AuthPAM_Enabled off\n" " AuthType Basic\n" " AuthName %s\n" " AuthUserFile %s\n" " \n" " require valid-user\n" " \n" "\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