diff --git a/bench/__init__.py b/bench/__init__.py index 6b4bb207..ad92bc1d 100644 --- a/bench/__init__.py +++ b/bench/__init__.py @@ -2,7 +2,7 @@ from jinja2 import Environment, PackageLoader __version__ = "4.0.0-beta" -env = Environment(loader=PackageLoader('bench.config'), trim_blocks=True) +env = Environment(loader=PackageLoader('bench.config')) FRAPPE_VERSION = None diff --git a/bench/config/nginx.py b/bench/config/nginx.py index 2444d062..6570597b 100644 --- a/bench/config/nginx.py +++ b/bench/config/nginx.py @@ -1,4 +1,4 @@ -import os, json, click +import os, json, click, random, string from bench.utils import get_sites, get_bench_name def make_nginx_conf(bench_path, force=False): @@ -18,7 +18,10 @@ def make_nginx_conf(bench_path, force=False): "sites": sites, "webserver_port": config.get('webserver_port'), "socketio_port": config.get('socketio_port'), - "bench_name": get_bench_name(bench_path) + "bench_name": get_bench_name(bench_path), + + # for nginx map variable + "random_string": "".join(random.choice(string.ascii_lowercase) for i in xrange(7)) }) conf_path = os.path.join(bench_path, "config", "nginx.conf") @@ -31,22 +34,39 @@ def make_nginx_conf(bench_path, force=False): def prepare_sites(config, bench_path): sites = { + "that_use_port": [], "that_use_dns": [], "that_use_ssl": [], - "that_use_port": [] + "that_use_wildcard_ssl": [] } + + domain_map = {} ports_in_use = {} + dns_multitenant = config.get('dns_multitenant') for site in get_sites_with_config(bench_path=bench_path): if dns_multitenant: - # assumes site's folder name is same as the domain name + domain = site.get('domain') - if site.get("ssl_certificate") and site.get("ssl_certificate_key"): + if domain: + # when site's folder name is different than domain name + domain_map[domain] = site['name'] + + site_name = domain or site['name'] + + if site.get('wildcard'): + sites["that_use_wildcard_ssl"].append(site_name) + + if not sites.get('wildcard_ssl_certificate'): + sites["wildcard_ssl_certificate"] = site['ssl_certificate'] + sites["wildcard_ssl_certificate_key"] = site['ssl_certificate_key'] + + elif site.get("ssl_certificate") and site.get("ssl_certificate_key"): sites["that_use_ssl"].append(site) else: - sites["that_use_dns"].append(site["name"]) + sites["that_use_dns"].append(site_name) else: if not site.get("port"): @@ -58,10 +78,16 @@ def prepare_sites(config, bench_path): ports_in_use[site["port"]] = site["name"] sites["that_use_port"].append(site) + sites['domain_map'] = domain_map + return sites def get_sites_with_config(bench_path): + from bench.config.common_site_config import get_config + sites = get_sites(bench_path=bench_path) + dns_multitenant = get_config(bench_path).get('dns_multitenant') + ret = [] for site in sites: site_config = get_site_config(site, bench_path=bench_path) @@ -71,8 +97,55 @@ def get_sites_with_config(bench_path): "ssl_certificate": site_config.get('ssl_certificate'), "ssl_certificate_key": site_config.get('ssl_certificate_key') }) + + if dns_multitenant and site_config.get('domains'): + for domain in site_config.get('domains'): + # domain can be a string or a dict with 'domain', 'ssl_certificate', 'ssl_certificate_key' + if isinstance(domain, basestring): + domain = { 'domain': domain } + + domain['name'] = site + ret.append(domain) + + use_wildcard_certificate(bench_path, ret) + return ret +def use_wildcard_certificate(bench_path, ret): + ''' + stored in common_site_config.json as: + "wildcard": { + "domain": "*.erpnext.com", + "ssl_certificate": "/path/to/erpnext.com.cert", + "ssl_certificate_key": "/path/to/erpnext.com.key" + } + ''' + from bench.config.common_site_config import get_config + config = get_config(bench_path=bench_path) + wildcard = config.get('wildcard') + + if not wildcard: + return + + domain = wildcard['domain'] + ssl_certificate = wildcard['ssl_certificate'] + ssl_certificate_key = wildcard['ssl_certificate_key'] + + if domain.startswith('*.'): + domain = domain[1:] + else: + domain = '.' + domain + + for site in ret: + if site.get('ssl_certificate'): + continue + + if (site.get('domain') or site['name']).endswith(domain): + # example: ends with .erpnext.com + site['ssl_certificate'] = ssl_certificate + site['ssl_certificate_key'] = ssl_certificate_key + site['wildcard'] = 1 + def get_site_config(site, bench_path='.'): with open(os.path.join(bench_path, 'sites', site, 'site_config.json')) as f: return json.load(f) diff --git a/bench/config/templates/nginx.conf b/bench/config/templates/nginx.conf index f1a2659a..0eb9050a 100644 --- a/bench/config/templates/nginx.conf +++ b/bench/config/templates/nginx.conf @@ -1,7 +1,16 @@ -{% macro server_block(bench_name, port, server_names, sites_path, ssl_certificate, ssl_certificate_key) %} +{%- macro nginx_map(from_variable, to_variable, values, default) %} +map {{ from_variable }} {{ to_variable }} { + {% for (from, to) in values.items() -%} + {{ from }} {{ to }}; + {% endfor %} -{%- set site_name = server_names[0] if (server_names|length)==1 else "$host" -%} + {%- if default -%} + default {{ default }}; + {% endif %} +} +{%- endmacro %} +{%- macro server_block(bench_name, port, server_names, site_name, sites_path, ssl_certificate, ssl_certificate_key) %} server { listen {{ port }}; server_name @@ -94,18 +103,22 @@ server { # text/html is always compressed by HttpGzipModule } -{% if ssl_certificate and ssl_certificate_key %} - # http to https redirect for {{ server_names[0] }} +{% if ssl_certificate and ssl_certificate_key -%} + # http to https redirect server { listen 80; - server_name {{ server_names[0] }}; + server_name + {% for name in server_names -%} + {{ name }} + {% endfor -%} + ; + return 301 https://$host$request_uri?$query_string; } {% endif %} -{# keep the empty line above for a pleasant rendering #} -{% endmacro %} +{%- endmacro -%} upstream {{ bench_name }}-frappe { server 127.0.0.1:{{ webserver_port or 8000 }} fail_timeout=0; @@ -115,16 +128,36 @@ upstream {{ bench_name}}-socketio-server { server 127.0.0.1:{{ socketio_port or 3000 }} fail_timeout=0; } +# setup maps +{%- set site_name_variable="$host" %} +{% if sites.domain_map -%} + {# we append these variables with a random string as there could be multiple benches #} + {%- set site_name_variable="$site_name_{0}".format(random_string) -%} + {{ nginx_map(from_variable="$host", to_variable=site_name_variable, values=sites.domain_map, default="$host") }} +{%- endif %} + +# server blocks {% if sites.that_use_dns -%} - {{ server_block(bench_name, 80, sites.that_use_dns, sites_path) }} + {{ server_block(bench_name, port=80, server_names=sites.that_use_dns, site_name=site_name_variable, sites_path=sites_path) }} + +{%- endif %} + +{% if sites.that_use_wildcard_ssl -%} + + {{ server_block(bench_name, port=443, server_names=sites.that_use_wildcard_ssl, + site_name=site_name_variable, sites_path=sites_path, + ssl_certificate=sites.wildcard_ssl_certificate, + ssl_certificate_key=sites.wildcard_ssl_certificate_key) }} {%- endif %} {%- if sites.that_use_ssl -%} {% for site in sites.that_use_ssl -%} - {{ server_block(bench_name, 443, [site.name], sites_path, site.ssl_certificate, site.ssl_certificate_key) }} + {{ server_block(bench_name, port=443, server_names=[site.domain or site.name], + site_name=site_name_variable, sites_path=sites_path, + ssl_certificate=site.ssl_certificate, ssl_certificate_key=site.ssl_certificate_key) }} {% endfor %} {%- endif %} @@ -132,7 +165,7 @@ upstream {{ bench_name}}-socketio-server { {% if sites.that_use_port -%} {%- for site in sites.that_use_port -%} - {{ server_block(bench_name, site.port, [site.name], sites_path) }} + {{ server_block(bench_name, port=site.port, server_names=[site.name], site_name=site.name, sites_path=sites_path) }} {%- endfor %} {% endif %}