manage the nginx conf in the management daemon too so we can have nginx operate on all domains that we serve mail for
This commit is contained in:
parent
a1a80b295e
commit
5faa1cae71
|
@ -1,11 +1,11 @@
|
||||||
# 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 {
|
||||||
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():
|
||||||
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
|
||||||
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):
|
||||||
# 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):
|
||||||
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.
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
# 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
|
||||||
# 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
|
||||||
. 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
|
||||||
|
|
26
setup/web.sh
26
setup/web.sh
|
@ -1,27 +1,29 @@
|
||||||
|
#!/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.
|
||||||
|
|
Loading…
Reference in New Issue