1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2024-11-22 02:17:26 +00:00

provide redirects from www subdomains of zones to their parent domain

* Split the nginx templates again so we have just the part needed to make a domain do a redirect separate from the rest.
* Add server blocks to the nginx config for these domains.
* List these domains in the SSL certificate install admin panel.
* Generate default 'www' records just for domains we provide default redirects for.

Fixes #321.
This commit is contained in:
Joshua Tauberer 2015-06-04 12:06:02 +00:00
parent 1d09e2406b
commit 95173bb327
6 changed files with 121 additions and 91 deletions

View File

@ -0,0 +1,79 @@
# Expose this directory as static files.
root $ROOT;
index index.html index.htm;
location = /robots.txt {
log_not_found off;
access_log off;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /mailinabox.mobileconfig {
alias /var/lib/mailinabox/mobileconfig.xml;
}
location = /.well-known/autoconfig/mail/config-v1.1.xml {
alias /var/lib/mailinabox/mozilla-autoconfig.xml;
}
# Roundcube Webmail configuration.
rewrite ^/mail$ /mail/ redirect;
rewrite ^/mail/$ /mail/index.php;
location /mail/ {
index index.php;
alias /usr/local/lib/roundcubemail/;
}
location ~ /mail/config/.* {
# A ~-style location is needed to give this precedence over the next block.
return 403;
}
location ~ /mail/.*\.php {
# note: ~ has precendence over a regular location block
include fastcgi_params;
fastcgi_split_path_info ^/mail(/.*)()$;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /usr/local/lib/roundcubemail/$fastcgi_script_name;
fastcgi_pass php-fpm;
# Outgoing mail also goes through this endpoint, so increase the maximum
# file upload limit to match the corresponding Postfix limit.
client_max_body_size 128M;
}
# Z-Push (Microsoft Exchange ActiveSync)
location /Microsoft-Server-ActiveSync {
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/index.php;
fastcgi_param PHP_VALUE "include_path=.:/usr/share/php:/usr/share/pear:/usr/share/awl/inc";
fastcgi_read_timeout 630;
fastcgi_pass php-fpm;
# Outgoing mail also goes through this endpoint, so increase the maximum
# file upload limit to match the corresponding Postfix limit.
client_max_body_size 128M;
}
location /autodiscover/autodiscover.xml {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/autodiscover/autodiscover.php;
fastcgi_param PHP_VALUE "include_path=.:/usr/share/php:/usr/share/pear:/usr/share/awl/inc";
fastcgi_pass php-fpm;
}
# ADDITIONAL DIRECTIVES HERE
# Disable viewing dotfiles (.htaccess, .svn, .git, etc.)
# This block is placed at the end. Nginx's precedence rules means this block
# takes precedence over all non-regex matches and only regex matches that
# come after it (i.e. none of those, since this is the last one.) That means
# we're blocking dotfiles in the static hosted sites but not the FastCGI-
# handled locations for ownCloud (which serves user-uploaded files that might
# have this pattern, see #414) or some of the other services.
location ~ /\.(ht|svn|git|hg|bzr) {
log_not_found off;
access_log off;
deny all;
}

View File

@ -58,3 +58,4 @@
rewrite ^/.well-known/carddav /cloud/remote.php/carddav/ redirect; rewrite ^/.well-known/carddav /cloud/remote.php/carddav/ redirect;
rewrite ^/.well-known/caldav /cloud/remote.php/caldav/ redirect; rewrite ^/.well-known/caldav /cloud/remote.php/caldav/ redirect;
# ADDITIONAL DIRECTIVES HERE

View File

@ -33,84 +33,5 @@ server {
ssl_certificate_key $SSL_KEY; ssl_certificate_key $SSL_KEY;
include /etc/nginx/nginx-ssl.conf; include /etc/nginx/nginx-ssl.conf;
# Expose this directory as static files.
root $ROOT;
index index.html index.htm;
location = /robots.txt {
log_not_found off;
access_log off;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /mailinabox.mobileconfig {
alias /var/lib/mailinabox/mobileconfig.xml;
}
location = /.well-known/autoconfig/mail/config-v1.1.xml {
alias /var/lib/mailinabox/mozilla-autoconfig.xml;
}
# Roundcube Webmail configuration.
rewrite ^/mail$ /mail/ redirect;
rewrite ^/mail/$ /mail/index.php;
location /mail/ {
index index.php;
alias /usr/local/lib/roundcubemail/;
}
location ~ /mail/config/.* {
# A ~-style location is needed to give this precedence over the next block.
return 403;
}
location ~ /mail/.*\.php {
# note: ~ has precendence over a regular location block
include fastcgi_params;
fastcgi_split_path_info ^/mail(/.*)()$;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /usr/local/lib/roundcubemail/$fastcgi_script_name;
fastcgi_pass php-fpm;
# Outgoing mail also goes through this endpoint, so increase the maximum
# file upload limit to match the corresponding Postfix limit.
client_max_body_size 128M;
}
# Z-Push (Microsoft Exchange ActiveSync)
location /Microsoft-Server-ActiveSync {
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/index.php;
fastcgi_param PHP_VALUE "include_path=.:/usr/share/php:/usr/share/pear:/usr/share/awl/inc";
fastcgi_read_timeout 630;
fastcgi_pass php-fpm;
# Outgoing mail also goes through this endpoint, so increase the maximum
# file upload limit to match the corresponding Postfix limit.
client_max_body_size 128M;
}
location /autodiscover/autodiscover.xml {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/autodiscover/autodiscover.php;
fastcgi_param PHP_VALUE "include_path=.:/usr/share/php:/usr/share/pear:/usr/share/awl/inc";
fastcgi_pass php-fpm;
}
# ADDITIONAL DIRECTIVES HERE # ADDITIONAL DIRECTIVES HERE
# Disable viewing dotfiles (.htaccess, .svn, .git, etc.)
# This block is placed at the end. Nginx's precedence rules means this block
# takes precedence over all non-regex matches and only regex matches that
# come after it (i.e. none of those, since this is the last one.) That means
# we're blocking dotfiles in the static hosted sites but not the FastCGI-
# handled locations for ownCloud (which serves user-uploaded files that might
# have this pattern, see #414) or some of the other services.
location ~ /\.(ht|svn|git|hg|bzr) {
log_not_found off;
access_log off;
deny all;
}
} }

View File

@ -57,13 +57,15 @@ def do_dns_update(env, force=False):
# Custom records to add to zones. # Custom records to add to zones.
additional_records = list(get_custom_dns_config(env)) additional_records = list(get_custom_dns_config(env))
from web_update import get_default_www_redirects
www_redirect_domains = get_default_www_redirects(env)
# Write zone files. # Write zone files.
os.makedirs('/etc/nsd/zones', exist_ok=True) os.makedirs('/etc/nsd/zones', exist_ok=True)
updated_domains = [] updated_domains = []
for i, (domain, zonefile) in enumerate(zonefiles): for i, (domain, zonefile) in enumerate(zonefiles):
# Build the records to put in the zone. # Build the records to put in the zone.
records = build_zone(domain, domains, additional_records, env) records = build_zone(domain, domains, additional_records, www_redirect_domains, env)
# See if the zone has changed, and if so update the serial number # See if the zone has changed, and if so update the serial number
# and write the zone file. # and write the zone file.
@ -126,7 +128,7 @@ def do_dns_update(env, force=False):
######################################################################## ########################################################################
def build_zone(domain, all_domains, additional_records, env, is_zone=True): def build_zone(domain, all_domains, additional_records, www_redirect_domains, env, is_zone=True):
records = [] records = []
# For top-level zones, define the authoritative name servers. # For top-level zones, define the authoritative name servers.
@ -177,7 +179,7 @@ def build_zone(domain, all_domains, additional_records, env, is_zone=True):
subdomains = [d for d in all_domains if d.endswith("." + domain)] subdomains = [d for d in all_domains if d.endswith("." + domain)]
for subdomain in subdomains: for subdomain in subdomains:
subdomain_qname = subdomain[0:-len("." + domain)] subdomain_qname = subdomain[0:-len("." + domain)]
subzone = build_zone(subdomain, [], additional_records, env, is_zone=False) subzone = build_zone(subdomain, [], additional_records, www_redirect_domains, env, is_zone=False)
for child_qname, child_rtype, child_value, child_explanation in subzone: for child_qname, child_rtype, child_value, child_explanation in subzone:
if child_qname == None: if child_qname == None:
child_qname = subdomain_qname child_qname = subdomain_qname
@ -215,9 +217,12 @@ def build_zone(domain, all_domains, additional_records, env, is_zone=True):
has_rec_base = records has_rec_base = records
defaults = [ defaults = [
(None, "A", env["PUBLIC_IP"], "Required. May have a different value. Sets the IP address that %s resolves to for web hosting and other services besides mail. The A record must be present but its value does not affect mail delivery." % domain), (None, "A", env["PUBLIC_IP"], "Required. May have a different value. Sets the IP address that %s resolves to for web hosting and other services besides mail. The A record must be present but its value does not affect mail delivery." % domain),
("www", "A", env["PUBLIC_IP"], "Optional. Sets the IP address that www.%s resolves to, e.g. for web hosting." % domain),
(None, "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that %s resolves to, e.g. for web hosting. (It is not necessary for receiving mail on this domain.)" % domain), (None, "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that %s resolves to, e.g. for web hosting. (It is not necessary for receiving mail on this domain.)" % domain),
("www", "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that www.%s resolves to, e.g. for web hosting." % domain), ]
if "www." + domain in www_redirect_domains:
defaults += [
("www", "A", env["PUBLIC_IP"], "Optional. Sets the IP address that www.%s resolves to so that the box can provide a redirect to the parent domain." % domain),
("www", "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that www.%s resolves to so that the box can provide a redirect to the parent domain." % domain),
] ]
for qname, rtype, value, explanation in defaults: for qname, rtype, value, explanation in defaults:
if value is None or value.strip() == "": continue # skip IPV6 if not set if value is None or value.strip() == "": continue # skip IPV6 if not set
@ -847,8 +852,10 @@ def build_recommended_dns(env):
domains = get_dns_domains(env) domains = get_dns_domains(env)
zonefiles = get_dns_zones(env) zonefiles = get_dns_zones(env)
additional_records = list(get_custom_dns_config(env)) additional_records = list(get_custom_dns_config(env))
from web_update import get_default_www_redirects
www_redirect_domains = get_default_www_redirects(env)
for domain, zonefile in zonefiles: for domain, zonefile in zonefiles:
records = build_zone(domain, domains, additional_records, env) records = build_zone(domain, domains, additional_records, www_redirect_domains, env)
# remove records that we don't dislay # remove records that we don't dislay
records = [r for r in records if r[3] is not False] records = [r for r in records if r[3] is not False]

View File

@ -12,7 +12,7 @@ import dns.reversename, dns.resolver
import dateutil.parser, dateutil.tz import dateutil.parser, dateutil.tz
from dns_update import get_dns_zones, build_tlsa_record, get_custom_dns_config, get_secondary_dns from dns_update import get_dns_zones, build_tlsa_record, get_custom_dns_config, get_secondary_dns
from web_update import get_web_domains, get_domain_ssl_files from web_update import get_web_domains, get_default_www_redirects, get_domain_ssl_files
from mailconfig import get_mail_domains, get_mail_aliases from mailconfig import get_mail_domains, get_mail_aliases
from utils import shell, sort_domains, load_env_vars_from_file from utils import shell, sort_domains, load_env_vars_from_file
@ -227,7 +227,7 @@ def run_domain_checks(rounded_time, env, output, pool):
dns_domains = set(dns_zonefiles) dns_domains = set(dns_zonefiles)
# Get the list of domains we serve HTTPS for. # Get the list of domains we serve HTTPS for.
web_domains = set(get_web_domains(env)) web_domains = set(get_web_domains(env) + get_default_www_redirects(env))
domains_to_check = mail_domains | dns_domains | web_domains domains_to_check = mail_domains | dns_domains | web_domains

View File

@ -5,7 +5,7 @@
import os, os.path, shutil, re, tempfile, rtyaml import os, os.path, shutil, re, tempfile, rtyaml
from mailconfig import get_mail_domains from mailconfig import get_mail_domains
from dns_update import get_custom_dns_config, do_dns_update from dns_update import get_custom_dns_config, do_dns_update, get_dns_zones
from utils import shell, safe_domain_name, sort_domains from utils import shell, safe_domain_name, sort_domains
def get_web_domains(env): def get_web_domains(env):
@ -36,21 +36,35 @@ def get_domains_with_a_records(env):
domains.add(domain) domains.add(domain)
return domains return domains
def get_default_www_redirects(env):
# Returns a list of www subdomains that we want to provide default redirects
# for, i.e. any www's that aren't domains the user has actually configured
# to serve for real. Which would be unusual.
web_domains = set(get_web_domains(env))
www_domains = set('www.' + zone for zone, zonefile in get_dns_zones(env))
return sort_domains(www_domains - web_domains - get_domains_with_a_records(env), env)
def do_web_update(env): def do_web_update(env):
# Build an nginx configuration file. # Build an nginx configuration file.
nginx_conf = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-top.conf")).read() nginx_conf = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-top.conf")).read()
# Load the templates. # Load the templates.
template1 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx.conf")).read() template0 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx.conf")).read()
template1 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-alldomains.conf")).read()
template2 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-primaryonly.conf")).read() template2 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-primaryonly.conf")).read()
template3 = "\trewrite / https://$REDIRECT_DOMAIN permanent;\n"
# Add the PRIMARY_HOST configuration first so it becomes nginx's default server. # Add the PRIMARY_HOST configuration first so it becomes nginx's default server.
nginx_conf += make_domain_config(env['PRIMARY_HOSTNAME'], [template1, template2], env) nginx_conf += make_domain_config(env['PRIMARY_HOSTNAME'], [template0, template1, template2], env)
# Add configuration all other web domains. # Add configuration all other web domains.
for domain in get_web_domains(env): for domain in get_web_domains(env):
if domain == env['PRIMARY_HOSTNAME']: continue # handled above if domain == env['PRIMARY_HOSTNAME']: continue # handled above
nginx_conf += make_domain_config(domain, [template1], env) nginx_conf += make_domain_config(domain, [template0, template1], env)
# Add default www redirects.
for domain in get_default_www_redirects(env):
nginx_conf += make_domain_config(domain, [template0, template3], env)
# Did the file change? If not, don't bother writing & restarting nginx. # Did the file change? If not, don't bother writing & restarting nginx.
nginx_conf_fn = "/etc/nginx/conf.d/local.conf" nginx_conf_fn = "/etc/nginx/conf.d/local.conf"
@ -300,4 +314,12 @@ def get_web_domains_info(env):
"static_enabled": not has_root_proxy_or_redirect(domain), "static_enabled": not has_root_proxy_or_redirect(domain),
} }
for domain in get_web_domains(env) for domain in get_web_domains(env)
] + \
[
{
"domain": domain,
"ssl_certificate": check_cert(domain),
"static_enabled": False,
}
for domain in get_default_www_redirects(env)
] ]