Merge remote-tracking branch 'upstream/master' into mgmt-auth

Conflicts:
	management/daemon.py
This commit is contained in:
Michael Kropat 2014-06-21 21:29:25 -04:00
commit 554a28479f
9 changed files with 97 additions and 35 deletions

View File

@ -19,8 +19,8 @@ Mail-in-a-Box turns a fresh Ubuntu 14.04 LTS 64-bit machine into a working mail
* [Spam filtering](https://spamassassin.apache.org/) that puts spam into a spam folder and [greylisting](http://postgrey.schweikert.ch/) to stop spam as it arrives. * [Spam filtering](https://spamassassin.apache.org/) that puts spam into a spam folder and [greylisting](http://postgrey.schweikert.ch/) to stop spam as it arrives.
* [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework), [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail), and [DMARC](https://en.wikipedia.org/wiki/DMARC) to prove to recipients that your email was from you --- the machine acts as its own DNS nameserver to automatically set this up. * [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework), [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail), and [DMARC](https://en.wikipedia.org/wiki/DMARC) to prove to recipients that your email was from you --- the machine acts as its own DNS nameserver to automatically set this up.
* [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC) and [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) to force cryptographically-secure communications in certain cases, especially between Mail-in-a-Boxes, if you add "DS" records to your domain registration. * [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC) and [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) to force cryptographically-secure communications in certain cases, especially between Mail-in-a-Boxes, if you add "DS" records to your domain registration.
* Configuration of mailboxes and mail aliases is done using a command-line tool or an HTTP-based API (accessible from within the server only). * A command-line tool and an HTTP-based API for administering mailboxes and mail aliases.
* Basic system services like a firewall, intrusion protection, and setting the system clock are automatically configured. * Basic system services like a firewall, intrusion protection, and setting the system clock.
This setup is what has been powering my own personal email since September 2013. This setup is what has been powering my own personal email since September 2013.

View File

@ -109,6 +109,9 @@ def do_updates():
if __name__ == '__main__': if __name__ == '__main__':
if "DEBUG" in os.environ: app.debug = True if "DEBUG" in os.environ: app.debug = True
if not app.debug:
app.logger.addHandler(utils.create_syslog_handler())
# For testing on the command line, you can use `curl` like so: # For testing on the command line, you can use `curl` like so:
# curl --user $(</var/lib/mailinabox/api.key): http://localhost:10222/mail/users # curl --user $(</var/lib/mailinabox/api.key): http://localhost:10222/mail/users
auth_service.write_key() auth_service.write_key()
@ -117,6 +120,5 @@ if __name__ == '__main__':
# debug console and enter that as the username # debug console and enter that as the username
app.logger.info('API key: ' + auth_service.key) app.logger.info('API key: ' + auth_service.key)
app.run(port=10222) app.run(port=10222)

View File

@ -132,7 +132,7 @@ def build_zone(domain, zonefile, env, with_ns=True):
records.append(("ns1", "A", env["PUBLIC_IP"])) records.append(("ns1", "A", env["PUBLIC_IP"]))
records.append(("ns2", "A", env["PUBLIC_IP"])) records.append(("ns2", "A", env["PUBLIC_IP"]))
# Add a TLSA record for SMTP. # Add a DANE TLSA record for SMTP.
records.append(("_25._tcp", "TLSA", build_tlsa_record(env))) records.append(("_25._tcp", "TLSA", build_tlsa_record(env)))
def has_rec(qname, rtype): def has_rec(qname, rtype):
@ -179,9 +179,8 @@ def build_zone(domain, zonefile, env, with_ns=True):
######################################################################## ########################################################################
def build_tlsa_record(env): def build_tlsa_record(env):
# A TLSA record in DNS specifies that connections on a port, e.g. # A DANE TLSA record in DNS specifies that connections on a port
# the SMTP port, must use TLS and the certificate must match a # must use TLS and the certificate must match a particular certificate.
# particular certificate.
# #
# Thanks to http://blog.huque.com/2012/10/dnssec-and-certificates.html # Thanks to http://blog.huque.com/2012/10/dnssec-and-certificates.html
# for explaining all of this! # for explaining all of this!

View File

@ -95,3 +95,9 @@ def shell(method, cmd_args, env={}, capture_stderr=False, return_bytes=False):
ret = getattr(subprocess, method)(cmd_args, env=env, stderr=stderr) ret = getattr(subprocess, method)(cmd_args, env=env, stderr=stderr)
if not return_bytes and isinstance(ret, bytes): ret = ret.decode("utf8") if not return_bytes and isinstance(ret, bytes): ret = ret.decode("utf8")
return ret return ret
def create_syslog_handler():
import logging.handlers
handler = logging.handlers.SysLogHandler(address='/dev/log')
handler.setLevel(logging.WARNING)
return handler

View File

@ -18,8 +18,7 @@ source /etc/mailinabox.conf # load global vars
apt_install \ apt_install \
postfix postgrey postfix-pcre \ postfix postgrey postfix-pcre \
dovecot-core dovecot-imapd dovecot-lmtpd dovecot-sqlite sqlite3 \ dovecot-core dovecot-imapd dovecot-lmtpd dovecot-sqlite sqlite3
openssl
mkdir -p $STORAGE_ROOT/mail mkdir -p $STORAGE_ROOT/mail
@ -244,29 +243,6 @@ tools/editconf.py /etc/dovecot/conf.d/10-ssl.conf \
"ssl_cert=<$STORAGE_ROOT/ssl/ssl_certificate.pem" \ "ssl_cert=<$STORAGE_ROOT/ssl/ssl_certificate.pem" \
"ssl_key=<$STORAGE_ROOT/ssl/ssl_private_key.pem" \ "ssl_key=<$STORAGE_ROOT/ssl/ssl_private_key.pem" \
# SSL CERTIFICATE
mkdir -p $STORAGE_ROOT/ssl
if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then
# Generate a new private key if one doesn't already exist.
# Set the umask so the key file is not world-readable.
(umask 077; openssl genrsa -out $STORAGE_ROOT/ssl/ssl_private_key.pem 2048)
fi
if [ ! -f $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr ]; then
# Generate a certificate signing request if one doesn't already exist.
openssl req -new -key $STORAGE_ROOT/ssl/ssl_private_key.pem -out $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr \
-subj "/C=$CSR_COUNTRY/ST=/L=/O=/CN=$PUBLIC_HOSTNAME"
fi
if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then
# Generate a SSL certificate by self-signing if a SSL certificate doesn't yet exist.
openssl x509 -req -days 365 \
-in $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr -signkey $STORAGE_ROOT/ssl/ssl_private_key.pem -out $STORAGE_ROOT/ssl/ssl_certificate.pem
fi
echo
echo "Your SSL certificate's fingerpint is:"
openssl x509 -in /home/user-data/ssl/ssl_certificate.pem -noout -fingerprint
echo
# PERMISSIONS / RESTART SERVICES # PERMISSIONS / RESTART SERVICES
# Ensure configuration files are owned by dovecot and not world readable. # Ensure configuration files are owned by dovecot and not world readable.

43
setup/ssl.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
#
# SSL Certificate
#
# Create a self-signed SSL certificate if one has not yet been created.
#
# The certificate is for PUBLIC_HOSTNAME specifically and is used for:
#
# * IMAP
# * SMTP submission (port 587) and opportunistic TLS (when on the receiving end)
# * the DNSSEC DANE TLSA record for SMTP
# * HTTPS (for PUBLIC_HOSTNAME only)
#
# When other domains besides PUBLIC_HOSTNAME are served over HTTPS,
# we generate a domain-specific self-signed certificate in the management
# daemon (web_update.py) as needed.
source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars
apt_install openssl
mkdir -p $STORAGE_ROOT/ssl
if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then
# Generate a new private key if one doesn't already exist.
# Set the umask so the key file is not world-readable.
(umask 077; openssl genrsa -out $STORAGE_ROOT/ssl/ssl_private_key.pem 2048)
fi
if [ ! -f $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr ]; then
# Generate a certificate signing request if one doesn't already exist.
openssl req -new -key $STORAGE_ROOT/ssl/ssl_private_key.pem -out $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr \
-subj "/C=$CSR_COUNTRY/ST=/L=/O=/CN=$PUBLIC_HOSTNAME"
fi
if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then
# Generate a SSL certificate by self-signing if a SSL certificate doesn't yet exist.
openssl x509 -req -days 365 \
-in $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr -signkey $STORAGE_ROOT/ssl/ssl_private_key.pem -out $STORAGE_ROOT/ssl/ssl_certificate.pem
fi
echo
echo "Your SSL certificate's fingerpint is:"
openssl x509 -in /home/user-data/ssl/ssl_certificate.pem -noout -fingerprint
echo

View File

@ -123,6 +123,7 @@ EOF
# Start service configuration. # Start service configuration.
. setup/system.sh . setup/system.sh
. setup/ssl.sh
. setup/dns.sh . setup/dns.sh
. setup/mail.sh . setup/mail.sh
. setup/dkim.sh . setup/dkim.sh

View File

@ -34,6 +34,8 @@ update-rc.d php-fastcgi defaults
# Put our webfinger server script into a well-known location. # Put our webfinger server script into a well-known location.
cp tools/webfinger.php /usr/local/bin/mailinabox-webfinger.php cp tools/webfinger.php /usr/local/bin/mailinabox-webfinger.php
chown www-data.www-data /usr/local/bin/mailinabox-webfinger.php chown www-data.www-data /usr/local/bin/mailinabox-webfinger.php
mkdir -p $STORAGE_ROOT/webfinger/acct;
chown -R $STORAGE_USER $STORAGE_ROOT/webfinger
# Start services. # Start services.
service nginx restart service nginx restart

View File

@ -1,9 +1,42 @@
<?php <?php
$resource = $_GET['resource']; $resource = $_GET['resource'];
// Parse our configuration file to get the STORAGE_ROOT.
$STORAGE_ROOT = NULL;
foreach (file("/etc/mailinabox.conf") as $line) {
$line = explode("=", rtrim($line), 2);
if ($line[0] == "STORAGE_ROOT") {
$STORAGE_ROOT = $line[1];
}
}
if ($STORAGE_ROOT == NULL) exit("no STORAGE_ROOT");
// Turn the resource into a file path. First URL-encode the resource
// so that it is filepath-safe.
$fn = urlencode($resource);
// Replace the first colon (it's URL-encoded) with a slash since we'll
// break off the files into scheme subdirectories.
$fn = preg_replace("/%3A/", "/", $fn, 1);
// Since this is often for email addresses, un-escape @-signs so they
// are not odd-looking. It's filename-safe anyway.
$fn = preg_replace("/%40/", "@", $fn);
// Combine with root path.
$fn = $STORAGE_ROOT . "/webfinger/" . $fn . ".json";
// See if the file exists.
if (!file_exists($fn)) {
header("HTTP/1.0 404 Not Found");
exit;
}
header("Content-type: application/json"); header("Content-type: application/json");
echo json_encode(array( echo file_get_contents($fn);
subject => $resource,
), JSON_PRETTY_PRINT); //json_encode(array(
// subject => $resource,
//), JSON_PRETTY_PRINT);
?> ?>