Commit 5faa1cae authored by Joshua Tauberer's avatar Joshua Tauberer

manage the nginx conf in the management daemon too so we can have nginx...

manage the nginx conf in the management daemon too so we can have nginx operate on all domains that we serve mail for
parent a1a80b29
# Redirect all HTTP to HTTPS. # Redirect all HTTP to HTTPS.
server { server {
listen 80; listen 80;
listen [::]:80 default_server ipv6only=on; listen [::]:80;
server_name $PUBLIC_HOSTNAME; server_name $HOSTNAME;
root /tmp/invalid-path-nothing-here; root /tmp/invalid-path-nothing-here;
rewrite ^/(.*)$ https://$PUBLIC_HOSTNAME/$1 permanent; rewrite ^/(.*)$ https://$HOSTNAME/$1 permanent;
} }
# The secure HTTPS server. # The secure HTTPS server.
...@@ -13,14 +13,14 @@ server { ...@@ -13,14 +13,14 @@ server {
server { server {
listen 443 ssl; listen 443 ssl;
server_name $PUBLIC_HOSTNAME; server_name $HOSTNAME;
ssl_certificate $STORAGE_ROOT/ssl/ssl_certificate.pem; ssl_certificate $SSL_CERTIFICATE;
ssl_certificate_key $STORAGE_ROOT/ssl/ssl_private_key.pem; ssl_certificate_key $SSL_KEY;
include /etc/nginx/nginx-ssl.conf; include /etc/nginx/nginx-ssl.conf;
# Expose this directory as static files. # Expose this directory as static files.
root $STORAGE_ROOT/www/static; root $ROOT;
index index.html index.htm; index index.html index.htm;
# Roundcube Webmail configuration. # Roundcube Webmail configuration.
......
...@@ -66,6 +66,13 @@ def dns_get_ds_records(): ...@@ -66,6 +66,13 @@ def dns_get_ds_records():
except Exception as e: except Exception as e:
return (str(e), 500) return (str(e), 500)
# WEB
@app.route('/web/update', methods=['POST'])
def web_update():
from web_update import do_web_update
return do_web_update(env)
# System # System
@app.route('/system/updates') @app.route('/system/updates')
......
...@@ -6,7 +6,7 @@ import os, os.path, urllib.parse, datetime, re, hashlib ...@@ -6,7 +6,7 @@ import os, os.path, urllib.parse, datetime, re, hashlib
import rtyaml import rtyaml
from mailconfig import get_mail_domains from mailconfig import get_mail_domains
from utils import shell, load_env_vars_from_file from utils import shell, load_env_vars_from_file, safe_domain_name
def get_dns_domains(env): def get_dns_domains(env):
# What domains should we serve DNS for? # What domains should we serve DNS for?
...@@ -30,7 +30,7 @@ def get_dns_domains(env): ...@@ -30,7 +30,7 @@ def get_dns_domains(env):
# Make a nice and safe filename for each domain. # Make a nice and safe filename for each domain.
zonefiles = [] zonefiles = []
for domain in domains: for domain in domains:
zonefiles.append([domain, urllib.parse.quote(domain, safe='') + ".txt"]) zonefiles.append([domain, safe_domain_name(domain) + ".txt"])
return zonefiles return zonefiles
......
...@@ -11,6 +11,11 @@ def load_env_vars_from_file(fn): ...@@ -11,6 +11,11 @@ def load_env_vars_from_file(fn):
for line in open(fn): env.setdefault(*line.strip().split("=", 1)) for line in open(fn): env.setdefault(*line.strip().split("=", 1))
return env return env
def safe_domain_name(name):
# Sanitize a domain name so it is safe to use as a file name on disk.
import urllib.parse
return urllib.parse.quote(name, safe='')
def exclusive_process(name): def exclusive_process(name):
# Ensure that a process named `name` does not execute multiple # Ensure that a process named `name` does not execute multiple
# times concurrently. # times concurrently.
......
# Creates an nginx configuration file so we serve HTTP/HTTPS on all
# domains for which a mail account has been set up.
########################################################################
import os, os.path
from mailconfig import get_mail_domains
from utils import shell, safe_domain_name
def get_web_domains(env):
# What domains should we serve HTTP/HTTPS for?
domains = set()
# Add all domain names in use by email users and mail aliases.
domains |= get_mail_domains(env)
# Ensure the PUBLIC_HOSTNAME is in the list.
domains.add(env['PUBLIC_HOSTNAME'])
# Sort the list. Put PUBLIC_HOSTNAME first so it becomes the
# default server (nginx's default_server).
domains = sorted(domains, key = lambda domain : (domain != env["PUBLIC_HOSTNAME"], list(reversed(domain.split(".")))) )
return domains
def do_web_update(env):
# Build an nginx configuration file.
nginx_conf = ""
template = open(os.path.join(os.path.dirname(__file__), "../conf/nginx.conf")).read()
for domain in get_web_domains(env):
nginx_conf += make_domain_config(domain, template, env)
# Save the file.
with open("/etc/nginx/conf.d/local.conf", "w") as f:
f.write(nginx_conf)
# Nick nginx.
shell('check_call', ["/usr/sbin/service", "nginx", "restart"])
return "OK"
def make_domain_config(domain, template, env):
# How will we configure this domain.
# Where will its root directory be for static files? Try STORAGE_ROOT/web/domain_name
# if it exists, but fall back to STORAGE_ROOT/web/default.
for test_domain in (domain, 'default'):
root = os.path.join(env["STORAGE_ROOT"], "www", safe_domain_name(test_domain))
if os.path.exists(root): break
# What SSL private key will we use? Allow the user to override this, but
# in many cases using the same private key for all domains would be fine.
# Don't allow the user to override the key for PUBLIC_HOSTNAME because
# that's what's in the main file.
ssl_key = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_private_key.pem')
alt_key = os.path.join(env["STORAGE_ROOT"], 'ssl/domains/%s_private_key.pem' % safe_domain_name(domain))
if domain != env['PUBLIC_HOSTNAME'] and os.path.exists(alt_key):
ssl_key = alt_key
# What SSL certificate will we use? This has to be differnet for each
# domain name. The certificate is already generated for PUBLIC_HOSTNAME.
# For other domains, generate a self-signed certificate if one doesn't
# already exist. See setup/mail.sh for documentation.
if domain == env['PUBLIC_HOSTNAME']:
ssl_certificate = os.path.join(env["STORAGE_ROOT"], 'ssl/ssl_certificate.pem')
else:
ssl_certificate = os.path.join(env["STORAGE_ROOT"], 'ssl/domains/%s_certifiate.pem' % safe_domain_name(domain))
os.makedirs(os.path.dirname(ssl_certificate), exist_ok=True)
if not os.path.exists(ssl_certificate):
# Generate a new self-signed certificate using the same private key that we already have.
# Start with a CSR.
csr = os.path.join(env["STORAGE_ROOT"], 'ssl/domains/%s_cert_sign_req.csr' % safe_domain_name(domain))
shell("check_call", [
"openssl", "req", "-new",
"-key", ssl_key,
"-out", csr,
"-subj", "/C=%s/ST=/L=/O=/CN=%s" % (env["CSR_COUNTRY"], domain)])
# And then make the certificate.
shell("check_call", [
"openssl", "x509", "-req",
"-days", "365",
"-in", csr,
"-signkey", ssl_key,
"-out", ssl_certificate])
# Replace substitution strings in the template & return.
nginx_conf = template
nginx_conf = nginx_conf.replace("$HOSTNAME", domain)
nginx_conf = nginx_conf.replace("$ROOT", root)
nginx_conf = nginx_conf.replace("$SSL_KEY", ssl_key)
nginx_conf = nginx_conf.replace("$SSL_CERTIFICATE", ssl_certificate)
return nginx_conf
...@@ -112,6 +112,7 @@ fi ...@@ -112,6 +112,7 @@ fi
# Save the global options in /etc/mailinabox.conf so that standalone # Save the global options in /etc/mailinabox.conf so that standalone
# tools know where to look for data. # tools know where to look for data.
cat > /etc/mailinabox.conf << EOF; cat > /etc/mailinabox.conf << EOF;
STORAGE_USER=$STORAGE_USER
STORAGE_ROOT=$STORAGE_ROOT STORAGE_ROOT=$STORAGE_ROOT
PUBLIC_HOSTNAME=$PUBLIC_HOSTNAME PUBLIC_HOSTNAME=$PUBLIC_HOSTNAME
PUBLIC_IP=$PUBLIC_IP PUBLIC_IP=$PUBLIC_IP
...@@ -129,9 +130,10 @@ EOF ...@@ -129,9 +130,10 @@ EOF
. setup/webmail.sh . setup/webmail.sh
. setup/management.sh . setup/management.sh
# Write the DNS configuration files. # Write the DNS and nginx configuration files.
sleep 5 # wait for the daemon to start sleep 5 # wait for the daemon to start
curl -s -d POSTDATA http://127.0.0.1:10222/dns/update curl -s -d POSTDATA http://127.0.0.1:10222/dns/update
curl -s -d POSTDATA http://127.0.0.1:10222/web/update
# If there aren't any mail users yet, create one. # If there aren't any mail users yet, create one.
if [ -z "`tools/mail.py user`" ]; then if [ -z "`tools/mail.py user`" ]; then
......
#!/bin/bash
# HTTP: Turn on a web server serving static files # HTTP: Turn on a web server serving static files
################################################# #################################################
source setup/functions.sh # load our functions source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars
apt_install nginx php5-cgi apt_install nginx php5-cgi
rm -f /etc/nginx/sites-enabled/default rm -f /etc/nginx/sites-enabled/default
STORAGE_ROOT_ESC=$(echo $STORAGE_ROOT|sed 's/[\\\/&]/\\&/g') # copy in a nginx configuration file for common and best-practices
PUBLIC_HOSTNAME_ESC=$(echo $PUBLIC_HOSTNAME|sed 's/[\\\/&]/\\&/g') # SSL settings from @konklone
# copy in the nginx configuration file and substitute some
# variables
cat conf/nginx.conf \
| sed "s/\$STORAGE_ROOT/$STORAGE_ROOT_ESC/g" \
| sed "s/\$PUBLIC_HOSTNAME/$PUBLIC_HOSTNAME_ESC/g" \
> /etc/nginx/conf.d/local.conf
cp conf/nginx-ssl.conf /etc/nginx/nginx-ssl.conf cp conf/nginx-ssl.conf /etc/nginx/nginx-ssl.conf
# Other nginx settings will be configured by the management service
# since it depends on what domains we're serving, which we don't know
# until mail accounts have been created.
# make a default homepage # make a default homepage
mkdir -p $STORAGE_ROOT/www/static if [ -d $STORAGE_ROOT/www/static ]; then mv $STORAGE_ROOT/www/static $STORAGE_ROOT/www/default; fi # migration
cp conf/www_default.html $STORAGE_ROOT/www/static/index.html mkdir -p $STORAGE_ROOT/www/default
chown -R $STORAGE_USER $STORAGE_ROOT/www/static/index.html if [ ! -f STORAGE_ROOT/www/default/index.html ]; then
cp conf/www_default.html $STORAGE_ROOT/www/default/index.html
chown -R $STORAGE_USER $STORAGE_ROOT/www/default/index.html
fi
# Create an init script to start the PHP FastCGI daemon and keep it # Create an init script to start the PHP FastCGI daemon and keep it
# running after a reboot. Allows us to serve Roundcube for webmail. # running after a reboot. Allows us to serve Roundcube for webmail.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment