mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-05 15:57:23 +01:00
rename the scripts directory to setup
This commit is contained in:
59
setup/dkim.sh
Normal file
59
setup/dkim.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
# OpenDKIM: Sign outgoing mail with DKIM
|
||||
########################################
|
||||
|
||||
# After this, you'll still need to run dns_update.sh to get the DKIM
|
||||
# signature in the DNS zones.
|
||||
|
||||
source setup/functions.sh # load our functions
|
||||
|
||||
# Install DKIM
|
||||
apt_install opendkim opendkim-tools
|
||||
|
||||
# Make sure configuration directories exist.
|
||||
mkdir -p /etc/opendkim;
|
||||
mkdir -p $STORAGE_ROOT/mail/dkim
|
||||
|
||||
# Used in InternalHosts and ExternalIgnoreList configuration directives.
|
||||
# Not quite sure why.
|
||||
echo "127.0.0.1" > /etc/opendkim/TrustedHosts
|
||||
|
||||
if grep -q "ExternalIgnoreList" /etc/opendkim.conf; then
|
||||
true; # already done
|
||||
else
|
||||
# Add various configuration options to the end.
|
||||
cat >> /etc/opendkim.conf << EOF;
|
||||
MinimumKeyBits 1024
|
||||
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
|
||||
InternalHosts refile:/etc/opendkim/TrustedHosts
|
||||
KeyTable refile:/etc/opendkim/KeyTable
|
||||
SigningTable refile:/etc/opendkim/SigningTable
|
||||
Socket inet:8891@localhost
|
||||
RequireSafeKeys false
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Create a new DKIM key if we don't have one already. This creates
|
||||
# mail.private and mail.txt in $STORAGE_ROOT/mail/dkim. The former
|
||||
# is the actual private key and the latter is the suggested DNS TXT
|
||||
# entry which we'll want to include in our DNS setup.
|
||||
if [ ! -z "$STORAGE_ROOT/mail/dkim/mail.private" ]; then
|
||||
# Should we specify -h rsa-sha256?
|
||||
opendkim-genkey -r -s mail -D $STORAGE_ROOT/mail/dkim
|
||||
fi
|
||||
|
||||
# Ensure files are owned by the opendkim user and are private otherwise.
|
||||
chown -R opendkim:opendkim $STORAGE_ROOT/mail/dkim
|
||||
chmod go-rwx $STORAGE_ROOT/mail/dkim
|
||||
|
||||
# Add OpenDKIM as a milter to postfix, which is how it intercepts outgoing
|
||||
# mail to perform the signing (by adding a mail header).
|
||||
# Be careful. If we add other milters later, it needs to be concatenated on the smtpd_milters line.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtpd_milters=inet:127.0.0.1:8891 \
|
||||
non_smtpd_milters=\$smtpd_milters \
|
||||
milter_default_action=accept
|
||||
|
||||
# Restart services.
|
||||
service opendkim restart
|
||||
service postfix restart
|
||||
|
||||
48
setup/dns.sh
Normal file
48
setup/dns.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
# DNS: Configure a DNS server using nsd
|
||||
#######################################
|
||||
|
||||
# After running this script, you also must run setup/dns_update.sh,
|
||||
# and any time a zone file is added/changed/removed, and any time a
|
||||
# new domain name becomes in use by a mail user.
|
||||
#
|
||||
# This script will turn on DNS for $PUBLIC_HOSTNAME.
|
||||
|
||||
source setup/functions.sh # load our functions
|
||||
|
||||
# Install nsd, our DNS server software.
|
||||
|
||||
# ...but first, we have to create the user because the
|
||||
# current Ubuntu forgets to do so in the .deb
|
||||
# see issue #25 and https://bugs.launchpad.net/ubuntu/+source/nsd/+bug/1311886
|
||||
if id nsd > /dev/null 2>&1; then
|
||||
true; #echo "nsd user exists... good";
|
||||
else
|
||||
useradd nsd;
|
||||
fi
|
||||
|
||||
# Okay now install the package.
|
||||
# bc is needed by dns_update.
|
||||
|
||||
apt_install nsd bc
|
||||
|
||||
# Prepare nsd's configuration.
|
||||
|
||||
sudo mkdir -p /var/run/nsd
|
||||
mkdir -p "$STORAGE_ROOT/dns";
|
||||
|
||||
# Create the default zone if it doesn't exist.
|
||||
|
||||
if [ ! -f "$STORAGE_ROOT/dns/$PUBLIC_HOSTNAME.txt" ]; then
|
||||
# can be an empty file, defaults are applied elsewhere
|
||||
cat > "$STORAGE_ROOT/dns/$PUBLIC_HOSTNAME.txt" << EOF;
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Let the storage user own all DNS configuration files.
|
||||
|
||||
chown -R $STORAGE_USER.$STORAGE_USER $STORAGE_ROOT/dns
|
||||
|
||||
# Permit DNS queries on TCP/UDP in the firewall.
|
||||
|
||||
ufw_allow domain
|
||||
|
||||
135
setup/dns_update.sh
Executable file
135
setup/dns_update.sh
Executable file
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
# DNS: Creates DNS zone files
|
||||
#############################
|
||||
|
||||
# Create nsd.conf and zone files, and updates the OpenDKIM signing tables.
|
||||
|
||||
# We set the administrative email address for every domain to domain_contact@[domain.com].
|
||||
# You should probably create an alias to your email address.
|
||||
|
||||
# This script is safe to run on its own.
|
||||
|
||||
source /etc/mailinabox.conf # load global vars
|
||||
|
||||
# Ensure a zone file exists for every domain name in use by a mail user.
|
||||
for mail_user in `tools/mail.py user`; do
|
||||
domain=`echo $mail_user | sed s/.*@//`
|
||||
if [ ! -f $STORAGE_ROOT/dns/$domain.txt ]; then
|
||||
echo "" > $STORAGE_ROOT/dns/$domain.txt;
|
||||
fi
|
||||
done
|
||||
|
||||
# Create the top of nsd.conf.
|
||||
|
||||
cat > /etc/nsd/nsd.conf << EOF;
|
||||
server:
|
||||
hide-version: yes
|
||||
|
||||
# identify the server (CH TXT ID.SERVER entry).
|
||||
identity: ""
|
||||
|
||||
# The directory for zonefile: files.
|
||||
zonesdir: "/etc/nsd/zones"
|
||||
|
||||
# ZONES
|
||||
EOF
|
||||
|
||||
# For every zone file in our dns directory, build a proper zone
|
||||
# file and mention it in nsd.conf. And add information to the
|
||||
# OpenDKIM signing tables.
|
||||
|
||||
mkdir -p /etc/nsd/zones;
|
||||
|
||||
truncate --size 0 /etc/opendkim/KeyTable
|
||||
truncate --size 0 /etc/opendkim/SigningTable
|
||||
|
||||
for fn in $STORAGE_ROOT/dns/*.txt; do
|
||||
# $fn is the zone configuration file, which is just a placeholder now.
|
||||
# For every file like mydomain.com.txt we'll create zone information
|
||||
# for that domain. We don't actually read the file.
|
||||
# $fn2 is the file without the directory.
|
||||
# $zone is the domain name (just mydomain.com).
|
||||
fn2=`basename $fn`
|
||||
zone=`echo $fn2 | sed "s/.txt\$//"`
|
||||
|
||||
# If the zone file exists, get the existing zone serial number so we can increment it.
|
||||
# TODO: This needs to be done better so that the existing serial number is persisted in the storage area.
|
||||
serial=`date +"%Y%m%d00"`
|
||||
if [ -f /etc/nsd/zones/$fn2 ]; then
|
||||
existing_serial=`grep "serial number" /etc/nsd/zones/$fn2 | sed "s/; serial number//"`
|
||||
if [ ! -z "$existing_serial" ]; then
|
||||
serial=`echo $existing_serial + 1 | bc`
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create the zone file.
|
||||
cat > /etc/nsd/zones/$fn2 << EOF;
|
||||
\$ORIGIN $zone. ; default zone domain
|
||||
\$TTL 86400 ; default time to live
|
||||
|
||||
@ IN SOA ns1.$PUBLIC_HOSTNAME. hostmaster.$PUBLIC_HOSTNAME. (
|
||||
$serial ; serial number
|
||||
28800 ; Refresh
|
||||
7200 ; Retry
|
||||
864000 ; Expire
|
||||
86400 ; Min TTL
|
||||
)
|
||||
|
||||
NS ns1.$PUBLIC_HOSTNAME.
|
||||
NS ns2.$PUBLIC_HOSTNAME.
|
||||
IN A $PUBLIC_IP
|
||||
MX 10 $PUBLIC_HOSTNAME.
|
||||
|
||||
300 TXT "v=spf1 mx -all"
|
||||
|
||||
www IN A $PUBLIC_IP
|
||||
EOF
|
||||
|
||||
# In PUBLIC_HOSTNAME, also define ns1 and ns2.
|
||||
if [ "$zone" = $PUBLIC_HOSTNAME ]; then
|
||||
cat >> /etc/nsd/zones/$fn2 << EOF;
|
||||
ns1 IN A $PUBLIC_IP
|
||||
ns2 IN A $PUBLIC_IP
|
||||
EOF
|
||||
fi
|
||||
|
||||
# If OpenDKIM is set up..
|
||||
if [ -f "$STORAGE_ROOT/mail/dkim/mail.txt" ]; then
|
||||
# Append the DKIM TXT record to the zone as generated by OpenDKIM.
|
||||
cat "$STORAGE_ROOT/mail/dkim/mail.txt" >> /etc/nsd/zones/$fn2;
|
||||
|
||||
# Append ADSP (RFC 5617) and DMARC records.
|
||||
cat >> /etc/nsd/zones/$fn2 << EOF;
|
||||
_adsp._domainkey IN TXT "dkim=all"
|
||||
_dmarc IN TXT "v=DMARC1; p=quarantine"
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Add this zone file to the main nsd configuration file.
|
||||
cat >> /etc/nsd/nsd.conf << EOF;
|
||||
zone:
|
||||
name: $zone
|
||||
zonefile: $fn2
|
||||
EOF
|
||||
|
||||
# Append a record to OpenDKIM's KeyTable and SigningTable. The SigningTable maps
|
||||
# email addresses to signing information. The KeyTable maps specify the hostname,
|
||||
# the selector, and the path to the private key.
|
||||
#
|
||||
# DKIM ADSP and DMARC both only support policies where the signing domain matches
|
||||
# the From address, so the KeyTable must specify that the signing domain for a
|
||||
# sender matches the sender's domain.
|
||||
#
|
||||
# In SigningTable, we map every email address to a key record called $zone.
|
||||
# Then we specify for the key record named $zone its domain, selector, and key.
|
||||
echo "$zone $zone:mail:$STORAGE_ROOT/mail/dkim/mail.private" >> /etc/opendkim/KeyTable
|
||||
echo "*@$zone $zone" >> /etc/opendkim/SigningTable
|
||||
|
||||
done
|
||||
|
||||
# Kick nsd.
|
||||
service nsd restart
|
||||
|
||||
# Kick opendkim.
|
||||
service opendkim restart
|
||||
|
||||
28
setup/functions.sh
Normal file
28
setup/functions.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
function apt_install {
|
||||
# Report any packages already installed.
|
||||
PACKAGES=$@
|
||||
TO_INSTALL=""
|
||||
for pkg in $PACKAGES; do
|
||||
if dpkg -s $pkg 2>/dev/null | grep "^Status: install ok installed" > /dev/null; then
|
||||
echo $pkg is already installed \(`dpkg -s $pkg | grep ^Version: | sed -e "s/.*: //"`\)
|
||||
else
|
||||
TO_INSTALL="$TO_INSTALL""$pkg "
|
||||
fi
|
||||
done
|
||||
|
||||
# List the packages about to be installed.
|
||||
if [[ ! -z "$TO_INSTALL" ]]; then
|
||||
echo installing $TO_INSTALL...
|
||||
fi
|
||||
|
||||
# 'DEBIAN_FRONTEND=noninteractive' is to prevent dbconfig-common from asking you questions.
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -qq -y install $PACKAGES > /dev/null;
|
||||
}
|
||||
|
||||
function ufw_allow {
|
||||
if [ -z "$DISABLE_FIREWALL" ]; then
|
||||
# ufw has completely unhelpful output
|
||||
ufw allow $1 > /dev/null;
|
||||
fi
|
||||
}
|
||||
|
||||
249
setup/mail.sh
Executable file
249
setup/mail.sh
Executable file
@@ -0,0 +1,249 @@
|
||||
# SMTP/IMAP: Postfix and Dovecot
|
||||
################################
|
||||
|
||||
# The SMTP server is listening on port 25 for incoming mail (mail for us) and on
|
||||
# port 587 for outgoing mail (i.e. mail you send). Port 587 uses STARTTLS (not SSL)
|
||||
# and you'll authenticate with your full email address and mail password.
|
||||
#
|
||||
# The IMAP server is listening on port 993 and uses SSL. There is no IMAP server
|
||||
# listening on port 143 because it is not encrypted on that port.
|
||||
|
||||
# We configure these together because postfix's configuration relies heavily on dovecot.
|
||||
|
||||
# Install packages.
|
||||
|
||||
source setup/functions.sh # load our functions
|
||||
source /etc/mailinabox.conf # load global vars
|
||||
|
||||
apt_install \
|
||||
postfix postgrey \
|
||||
dovecot-core dovecot-imapd dovecot-lmtpd dovecot-sqlite sqlite3 \
|
||||
openssl
|
||||
|
||||
mkdir -p $STORAGE_ROOT/mail
|
||||
|
||||
# POSTFIX
|
||||
#########
|
||||
|
||||
# Enable the 'submission' port 587 listener.
|
||||
sed -i "s/#submission/submission/" /etc/postfix/master.cf
|
||||
|
||||
# Enable TLS and require it for all user authentication.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtpd_tls_security_level=may\
|
||||
smtpd_tls_auth_only=yes \
|
||||
smtpd_tls_cert_file=$STORAGE_ROOT/ssl/ssl_certificate.pem \
|
||||
smtpd_tls_key_file=$STORAGE_ROOT/ssl/ssl_private_key.pem \
|
||||
smtpd_tls_received_header=yes
|
||||
|
||||
# When connecting to remote SMTP servers, prefer TLS.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtp_tls_security_level=may \
|
||||
smtp_tls_loglevel=2
|
||||
|
||||
# Postfix will query dovecot for user authentication.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtpd_sasl_type=dovecot \
|
||||
smtpd_sasl_path=private/auth \
|
||||
smtpd_sasl_auth_enable=yes
|
||||
|
||||
# Who can send outbound mail?
|
||||
# permit_sasl_authenticated: Authenticated users (i.e. on port 587).
|
||||
# permit_mynetworks: Mail that originates locally.
|
||||
# reject_unauth_destination: No one else. (Permits mail whose destination is local and rejects other mail.)
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtpd_relay_restrictions=permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination
|
||||
|
||||
# Who can send mail to us?
|
||||
# reject_non_fqdn_sender: Reject not-nice-looking return paths.
|
||||
# reject_unknown_sender_domain: Reject return paths with invalid domains.
|
||||
# reject_rhsbl_sender: Reject return paths that use blacklisted domains.
|
||||
# permit_sasl_authenticated: Authenticated users (i.e. on port 587).
|
||||
# permit_mynetworks: Mail that originates locally.
|
||||
# reject_rbl_client: Reject connections from IP addresses blacklisted in zen.spamhaus.org
|
||||
# check_policy_service: Apply greylisting using postgrey.
|
||||
#
|
||||
# Notes:
|
||||
# permit_dnswl_client can pass through mail from whitelisted IP addresses, which would be good to put before greylisting
|
||||
# so these IPs get mail delivered quickly. But when an IP is not listed in the permit_dnswl_client list (i.e. it is not
|
||||
# whitelisted) then postfix does a DEFER_IF_REJECT, which results in all "unknown user" sorts of messages turning into
|
||||
# "450 4.7.1 Client host rejected: Service unavailable". This is a retry code, so the mail doesn't properly bounce.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtpd_sender_restrictions="reject_non_fqdn_sender,reject_unknown_sender_domain,reject_rhsbl_sender dbl.spamhaus.org"
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtpd_recipient_restrictions=permit_sasl_authenticated,permit_mynetworks,"reject_rbl_client zen.spamhaus.org","check_policy_service inet:127.0.0.1:10023"
|
||||
|
||||
# Have postfix listen on all network interfaces, set our name (the Debian default seems to be localhost),
|
||||
# and set the name of the local machine to localhost for xxx@localhost mail (but I don't think this will have any effect because
|
||||
# there is no true local mail delivery). Also set the banner (must have the hostname first, then anything).
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
inet_interfaces=all \
|
||||
myhostname=$PUBLIC_HOSTNAME\
|
||||
smtpd_banner="\$myhostname ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://github.com/joshdata/mailinabox)" \
|
||||
mydestination=localhost
|
||||
|
||||
# Handle all local mail delivery by passing it directly to dovecot over LMTP.
|
||||
tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:unix:private/dovecot-lmtp
|
||||
|
||||
# Use a Sqlite3 database to check whether a destination email address exists,
|
||||
# and to perform any email alias rewrites.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
virtual_mailbox_domains=sqlite:/etc/postfix/virtual-mailbox-domains.cf \
|
||||
virtual_mailbox_maps=sqlite:/etc/postfix/virtual-mailbox-maps.cf \
|
||||
virtual_alias_maps=sqlite:/etc/postfix/virtual-alias-maps.cf \
|
||||
local_recipient_maps=\$virtual_mailbox_maps
|
||||
|
||||
# Here's the path to the database.
|
||||
db_path=$STORAGE_ROOT/mail/users.sqlite
|
||||
|
||||
# SQL statement to check if we handle mail for a domain, either for users or aliases.
|
||||
cat > /etc/postfix/virtual-mailbox-domains.cf << EOF;
|
||||
dbpath=$db_path
|
||||
query = SELECT 1 FROM users WHERE email LIKE '%%@%s' UNION SELECT 1 FROM aliases WHERE source LIKE '%%@%s'
|
||||
EOF
|
||||
|
||||
# SQL statement to check if we handle mail for a user.
|
||||
cat > /etc/postfix/virtual-mailbox-maps.cf << EOF;
|
||||
dbpath=$db_path
|
||||
query = SELECT 1 FROM users WHERE email='%s'
|
||||
EOF
|
||||
|
||||
# SQL statement to rewrite an email address if an alias is present.
|
||||
cat > /etc/postfix/virtual-alias-maps.cf << EOF;
|
||||
dbpath=$db_path
|
||||
query = SELECT destination FROM aliases WHERE source='%s'
|
||||
EOF
|
||||
|
||||
# Create an empty database if it doesn't yet exist.
|
||||
if [ ! -f $db_path ]; then
|
||||
echo Creating new user database: $db_path;
|
||||
echo "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, extra);" | sqlite3 $db_path;
|
||||
echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL);" | sqlite3 $db_path;
|
||||
fi
|
||||
|
||||
# DOVECOT
|
||||
#########
|
||||
|
||||
# The dovecot-imapd dovecot-lmtpd packages automatically enable IMAP and LMTP protocols.
|
||||
|
||||
# Set the location where we'll store user mailboxes.
|
||||
tools/editconf.py /etc/dovecot/conf.d/10-mail.conf \
|
||||
mail_location=maildir:$STORAGE_ROOT/mail/mailboxes/%d/%n \
|
||||
mail_privileged_group=mail \
|
||||
first_valid_uid=0
|
||||
|
||||
# Require that passwords are sent over SSL only, and allow the usual IMAP authentication mechanisms.
|
||||
# The LOGIN mechanism is supposedly for Microsoft products like Outlook to do SMTP login (I guess
|
||||
# since we're using Dovecot to handle SMTP authentication?).
|
||||
tools/editconf.py /etc/dovecot/conf.d/10-auth.conf \
|
||||
disable_plaintext_auth=yes \
|
||||
"auth_mechanisms=plain login"
|
||||
|
||||
# Query our Sqlite3 database, and not system users, for authentication.
|
||||
sed -i "s/\(\!include auth-system.conf.ext\)/#\1/" /etc/dovecot/conf.d/10-auth.conf
|
||||
sed -i "s/#\(\!include auth-sql.conf.ext\)/\1/" /etc/dovecot/conf.d/10-auth.conf
|
||||
|
||||
# Configure how to access our Sqlite3 database. Not sure what userdb is for.
|
||||
cat > /etc/dovecot/conf.d/auth-sql.conf.ext << EOF;
|
||||
passdb {
|
||||
driver = sql
|
||||
args = /etc/dovecot/dovecot-sql.conf.ext
|
||||
}
|
||||
userdb {
|
||||
driver = static
|
||||
args = uid=mail gid=mail home=$STORAGE_ROOT/mail/mailboxes/%d/%n
|
||||
}
|
||||
EOF
|
||||
|
||||
# Configure the SQL to query for a user's password.
|
||||
cat > /etc/dovecot/dovecot-sql.conf.ext << EOF;
|
||||
driver = sqlite
|
||||
connect = $db_path
|
||||
default_pass_scheme = SHA512-CRYPT
|
||||
password_query = SELECT email as user, password FROM users WHERE email='%u';
|
||||
EOF
|
||||
chmod 0600 /etc/dovecot/dovecot-sql.conf.ext # per Dovecot instructions
|
||||
|
||||
# Disable in-the-clear IMAP and POP because we're paranoid (we haven't even
|
||||
# enabled POP).
|
||||
sed -i "s/#port = 143/port = 0/" /etc/dovecot/conf.d/10-master.conf
|
||||
sed -i "s/#port = 110/port = 0/" /etc/dovecot/conf.d/10-master.conf
|
||||
|
||||
# Have dovecot provide authorization and LMTP (local mail delivery) services.
|
||||
#
|
||||
# We have dovecot listen on a Unix domain socket for these services
|
||||
# in a manner that made postfix configuration above easy.
|
||||
#
|
||||
# We also have dovecot listen on port 10026 (localhost only) for LMTP
|
||||
# in case we have other services that want to deliver local mail, namely
|
||||
# spampd.
|
||||
#
|
||||
# Also increase the number of allowed connections per mailbox because we
|
||||
# all have so many devices lately.
|
||||
cat > /etc/dovecot/conf.d/99-local.conf << EOF;
|
||||
service auth {
|
||||
unix_listener /var/spool/postfix/private/auth {
|
||||
mode = 0666
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
}
|
||||
service lmtp {
|
||||
unix_listener /var/spool/postfix/private/dovecot-lmtp {
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
inet_listener lmtp {
|
||||
address = 127.0.0.1
|
||||
port = 10026
|
||||
}
|
||||
}
|
||||
|
||||
protocol imap {
|
||||
mail_max_userip_connections = 20
|
||||
}
|
||||
EOF
|
||||
|
||||
# postmaster_address seems to be required or LMTP won't start
|
||||
tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \
|
||||
postmaster_address=postmaster@$PUBLIC_HOSTNAME
|
||||
|
||||
# Drew Crawford sets the auth-worker process to run as the mail user, but we don't care if it runs as root.
|
||||
|
||||
# Enable SSL and specify the location of the SSL certificate and private key files.
|
||||
tools/editconf.py /etc/dovecot/conf.d/10-ssl.conf \
|
||||
ssl=required \
|
||||
"ssl_cert=<$STORAGE_ROOT/ssl/ssl_certificate.pem" \
|
||||
"ssl_key=<$STORAGE_ROOT/ssl/ssl_private_key.pem" \
|
||||
|
||||
# SSL CERTIFICATE
|
||||
|
||||
# Create a self-signed certifiate.
|
||||
mkdir -p $STORAGE_ROOT/ssl
|
||||
if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then
|
||||
openssl genrsa -out $STORAGE_ROOT/ssl/ssl_private_key.pem 2048
|
||||
openssl req -new -key $STORAGE_ROOT/ssl/ssl_private_key.pem -out $STORAGE_ROOT/ssl/ssl_cert_sign_req.csr \
|
||||
-subj "/C=/ST=/L=/O=/CN=$PUBLIC_HOSTNAME"
|
||||
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
|
||||
|
||||
# PERMISSIONS / RESTART SERVICES
|
||||
|
||||
# Ensure configuration files are owned by dovecot and not world readable.
|
||||
chown -R mail:dovecot /etc/dovecot
|
||||
chmod -R o-rwx /etc/dovecot
|
||||
|
||||
# Ensure mailbox files have a directory that exists and are owned by the mail user.
|
||||
mkdir -p $STORAGE_ROOT/mail/mailboxes
|
||||
chown -R mail.mail $STORAGE_ROOT/mail/mailboxes
|
||||
|
||||
# Restart services.
|
||||
service postfix restart
|
||||
service dovecot restart
|
||||
|
||||
# Allow mail-related ports in the firewall.
|
||||
ufw_allow smtp
|
||||
ufw_allow submission
|
||||
ufw_allow imaps
|
||||
|
||||
71
setup/spamassassin.sh
Normal file
71
setup/spamassassin.sh
Normal file
@@ -0,0 +1,71 @@
|
||||
# Spam filtering with spamassassin via spampd
|
||||
#############################################
|
||||
|
||||
# spampd sits between postfix and dovecot. It takes mail from postfix
|
||||
# over the LMTP protocol, runs spamassassin on it, and then passes the
|
||||
# message over LMTP to dovecot for local delivery.
|
||||
|
||||
# In order to move spam automatically into the Spam folder we use the dovecot sieve
|
||||
# plugin. The tools/mail.py tool creates the necessary sieve script for each mail
|
||||
# user when the mail user is created.
|
||||
|
||||
source setup/functions.sh # load our functions
|
||||
|
||||
# Install packages.
|
||||
apt_install spampd razor pyzor dovecot-sieve dovecot-antispam
|
||||
|
||||
# Allow spamassassin to download new rules.
|
||||
tools/editconf.py /etc/default/spamassassin \
|
||||
CRON=1
|
||||
|
||||
# Configure pyzor.
|
||||
pyzor discover
|
||||
|
||||
# Hook into postfix. Replace dovecot with spampd as the mail delivery agent.
|
||||
tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:[127.0.0.1]:10025
|
||||
|
||||
# Pass messages on to docevot on port 10026.
|
||||
# This is actually the default setting but we don't want to lose track of it.
|
||||
# We've already configured Dovecot to listen on this port.
|
||||
tools/editconf.py /etc/default/spampd DESTPORT=10026
|
||||
|
||||
# Enable the Dovecot sieve plugin which let's us set a script that automatically moves
|
||||
# spam into the user's Spam mail filter.
|
||||
# (Note: Be careful if we want to use multiple plugins later.)
|
||||
sudo sed -i "s/#mail_plugins = .*/mail_plugins = \$mail_plugins sieve/" /etc/dovecot/conf.d/20-lmtp.conf
|
||||
|
||||
# Enable the Dovecot antispam plugin to detect when a message moves between folders so we can
|
||||
# pass it to sa-learn for training. (Be careful if we use multiple plugins later.)
|
||||
sudo sed -i "s/#mail_plugins = .*/mail_plugins = \$mail_plugins antispam/" /etc/dovecot/conf.d/20-imap.conf
|
||||
|
||||
# When mail is moved in or out of the Dovecot Spam folder, re-train using this script
|
||||
# that sends the mail to spamassassin.
|
||||
# from http://wiki2.dovecot.org/Plugins/Antispam
|
||||
cat > /usr/bin/sa-learn-pipe.sh << EOF;
|
||||
cat<&0 >> /tmp/sendmail-msg-\$\$.txt
|
||||
/usr/bin/sa-learn \$* /tmp/sendmail-msg-\$\$.txt > /dev/null
|
||||
rm -f /tmp/sendmail-msg-\$\$.txt
|
||||
exit 0
|
||||
EOF
|
||||
chmod a+x /usr/bin/sa-learn-pipe.sh
|
||||
|
||||
# Configure the antispam plugin to call sa-learn-pipe.sh.
|
||||
cat > /etc/dovecot/conf.d/99-local-spampd.conf << EOF;
|
||||
plugin {
|
||||
antispam_backend = pipe
|
||||
antispam_spam_pattern_ignorecase = SPAM
|
||||
antispam_allow_append_to_spam = yes
|
||||
antispam_pipe_program_spam_args = /usr/bin/sa-learn-pipe.sh;--spam
|
||||
antispam_pipe_program_notspam_args = /usr/bin/sa-learn-pipe.sh;--ham
|
||||
antispam_pipe_program = /bin/bash
|
||||
}
|
||||
EOF
|
||||
|
||||
# Initial training?
|
||||
# sa-learn --ham storage/mail/mailboxes/*/*/cur/
|
||||
# sa-learn --spam storage/mail/mailboxes/*/*/.Spam/cur/
|
||||
|
||||
# Kick services.
|
||||
sudo service spampd restart
|
||||
sudo service dovecot restart
|
||||
|
||||
87
setup/start.sh
Executable file
87
setup/start.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# This is the entry point for configuring the system.
|
||||
#####################################################
|
||||
|
||||
# Check system setup.
|
||||
|
||||
if [ "`lsb_release -d | sed 's/.*:\s*//'`" != "Ubuntu 14.04 LTS" ]; then
|
||||
echo "Mail-in-a-Box only supports being installed on Ubuntu 14.04, sorry. You are running:"
|
||||
echo
|
||||
lsb_release -d | sed 's/.*:\s*//'
|
||||
echo
|
||||
echo "We can't write scripts that run on every possible setup, sorry."
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
# Gather information from the user about the hostname and public IP
|
||||
# address of this host.
|
||||
if [ -z "$PUBLIC_HOSTNAME" ]; then
|
||||
echo
|
||||
echo "Enter the hostname you want to assign to this machine."
|
||||
echo "We've guessed a value. Just backspace it if it's wrong."
|
||||
echo "Josh uses box.occams.info as his hostname. Yours should"
|
||||
echo "be similar."
|
||||
echo
|
||||
read -e -i "`hostname`" -p "Hostname: " PUBLIC_HOSTNAME
|
||||
fi
|
||||
|
||||
if [ -z "$PUBLIC_IP" ]; then
|
||||
echo
|
||||
echo "Enter the public IP address of this machine, as given to"
|
||||
echo "you by your ISP. We've guessed a value, but just backspace"
|
||||
echo "it if it's wrong."
|
||||
echo
|
||||
read -e -i "`hostname -i`" -p "Public IP: " PUBLIC_IP
|
||||
fi
|
||||
|
||||
# Create the user named "user-data" and store all persistent user
|
||||
# data (mailboxes, etc.) in that user's home directory.
|
||||
if [ -z "$STORAGE_ROOT" ]; then
|
||||
STORAGE_USER=user-data
|
||||
if [ ! -d /home/$STORAGE_USER ]; then useradd -m $STORAGE_USER; fi
|
||||
STORAGE_ROOT=/home/$STORAGE_USER
|
||||
mkdir -p $STORAGE_ROOT
|
||||
fi
|
||||
|
||||
# Save the global options in /etc/mailinabox.conf so that standalone
|
||||
# tools know where to look for data.
|
||||
cat > /etc/mailinabox.conf << EOF;
|
||||
STORAGE_ROOT=$STORAGE_ROOT
|
||||
PUBLIC_HOSTNAME=$PUBLIC_HOSTNAME
|
||||
PUBLIC_IP=$PUBLIC_IP
|
||||
EOF
|
||||
|
||||
# For docker, we don't want any of our scripts to start daemons.
|
||||
# Mask the 'service' program by defining a function of the same name
|
||||
# so that whenever we try to restart a service we just silently do
|
||||
# nothing.
|
||||
if [ "$NO_RESTART_SERVICES" == "1" ]; then
|
||||
function service {
|
||||
# we could output some status, but it's not important
|
||||
echo skipping service $@ > /dev/null;
|
||||
}
|
||||
fi
|
||||
|
||||
# Start service configuration.
|
||||
. setup/system.sh
|
||||
. setup/dns.sh
|
||||
. setup/mail.sh
|
||||
. setup/dkim.sh
|
||||
. setup/spamassassin.sh
|
||||
. setup/dns_update.sh
|
||||
. setup/web.sh
|
||||
. setup/webmail.sh
|
||||
|
||||
if [ -t 0 ]; then # are we in an interactive shell?
|
||||
if [ -z "`tools/mail.py user`" ]; then
|
||||
# The outut of "tools/mail.py user" is a list of mail users. If there
|
||||
# are none configured, ask the user to configure one.
|
||||
echo
|
||||
echo "Let's create your first mail user."
|
||||
read -e -i "user@$PUBLIC_HOSTNAME" -p "Email Address: " EMAIL_ADDR
|
||||
tools/mail.py user add $EMAIL_ADDR # will ask for password
|
||||
tools/mail.py alias add hostmaster@$PUBLIC_HOSTNAME $EMAIL_ADDR
|
||||
tools/mail.py alias add postmaster@$PUBLIC_HOSTNAME $EMAIL_ADDR
|
||||
fi
|
||||
fi
|
||||
47
setup/system.sh
Executable file
47
setup/system.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
source setup/functions.sh # load our functions
|
||||
|
||||
# Base system configuration.
|
||||
|
||||
apt-get -qq update
|
||||
apt-get -qq -y upgrade
|
||||
|
||||
# Install openssh-server to ensure that the end result is consistent across all Mail-in-a-Boxes.
|
||||
apt_install openssh-server
|
||||
|
||||
# Check that SSH login with password is disabled. Stop if it's enabled.
|
||||
if grep -q "^PasswordAuthentication yes" /etc/ssh/sshd_config \
|
||||
|| ! grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config ; then
|
||||
echo
|
||||
echo "The SSH server on this machine permits password-based login."
|
||||
echo "Add your SSH public key to $HOME/.ssh/authorized_keys, check"
|
||||
echo "check that you can log in without a password, set the option"
|
||||
echo "'PasswordAuthentication no' in /etc/ssh/sshd_config, and then"
|
||||
echo "restart the openssh via 'sudo service ssh restart'"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Install basic utilities.
|
||||
|
||||
apt_install python3 wget curl bind9-host
|
||||
|
||||
# Turn on basic services:
|
||||
#
|
||||
# ntp: keeps the system time correct
|
||||
#
|
||||
# fail2ban: scans log files for repeated failed login attempts and blocks the remote IP at the firewall
|
||||
#
|
||||
# These services don't need further configuration and are started immediately after installation.
|
||||
|
||||
apt_install ntp fail2ban
|
||||
|
||||
if [ -z "$DISABLE_FIREWALL" ]; then
|
||||
# Turn on the firewall. First allow incoming SSH, then turn on the firewall.
|
||||
# Other ports will be opened at the point where we set up those services.
|
||||
#
|
||||
# Various virtualized environments like Docker and some VPSs don't provide
|
||||
# a kernel that supports iptables. To avoid error-like output in these cases,
|
||||
# let us disable the firewall.
|
||||
apt_install ufw
|
||||
ufw_allow ssh;
|
||||
ufw --force enable;
|
||||
fi
|
||||
39
setup/web.sh
Executable file
39
setup/web.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
# HTTP: Turn on a web server serving static files
|
||||
#################################################
|
||||
|
||||
source setup/functions.sh # load our functions
|
||||
|
||||
apt_install nginx php5-cgi
|
||||
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
STORAGE_ROOT_ESC=$(echo $STORAGE_ROOT|sed 's/[\\\/&]/\\&/g')
|
||||
PUBLIC_HOSTNAME_ESC=$(echo $PUBLIC_HOSTNAME|sed 's/[\\\/&]/\\&/g')
|
||||
|
||||
# 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
|
||||
|
||||
# make a default homepage
|
||||
mkdir -p $STORAGE_ROOT/www/static
|
||||
cp conf/www_default.html $STORAGE_ROOT/www/static/index.html
|
||||
chown -R $STORAGE_USER $STORAGE_ROOT/www/static/index.html
|
||||
|
||||
# Create an init script to start the PHP FastCGI daemon and keep it
|
||||
# running after a reboot. Allows us to serve Roundcube for webmail.
|
||||
rm -f /etc/init.d/php-fastcgi
|
||||
ln -s $(pwd)/conf/phpfcgi-initscript /etc/init.d/php-fastcgi
|
||||
update-rc.d php-fastcgi defaults
|
||||
|
||||
# Start services.
|
||||
service nginx restart
|
||||
service php-fastcgi restart
|
||||
|
||||
# Open ports.
|
||||
ufw_allow http
|
||||
ufw_allow https
|
||||
|
||||
90
setup/webmail.sh
Executable file
90
setup/webmail.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
# Webmail: Using roundcube
|
||||
##########################
|
||||
|
||||
source setup/functions.sh # load our functions
|
||||
source /etc/mailinabox.conf # load global vars
|
||||
|
||||
# Ubuntu's roundcube-core has dependencies on Apache & MySQL, which we don't want, so we can't
|
||||
# install roundcube directly via apt-get install. We'll use apt-get to manually install the
|
||||
# dependencies of roundcube that we know we need, and then we'll manually install debs for
|
||||
# roundcube using dpkg so that dependencies aren't triggered.
|
||||
|
||||
# These dependencies are from 'apt-cache showpkg roundcube-core'.
|
||||
apt_install \
|
||||
dbconfig-common \
|
||||
php5 php5-sqlite php5-mcrypt php5-intl php5-json php5-common php-auth php-net-smtp php-net-socket php-net-sieve php-mail-mime php-crypt-gpg php5-gd php5-pspell \
|
||||
tinymce libjs-jquery libjs-jquery-mousewheel libmagic1
|
||||
|
||||
mkdir -p /tmp/roundcube_debs
|
||||
pushd /tmp/roundcube_debs
|
||||
apt-get download roundcube roundcube-core roundcube-sqlite3 roundcube-plugins
|
||||
DEBIAN_FRONTEND=noninteractive dpkg -Gi *.deb
|
||||
popd
|
||||
rm -rf /tmp/roundcube_debs
|
||||
apt-mark hold roundcube-core # hopefully apt-get won't attempt to upgrade it, which might trigger dependenciees?
|
||||
|
||||
# Buuuut.... the .deb is missing things?
|
||||
src_fn=roundcube_0.9.5.orig.tar.gz
|
||||
src_dir=roundcubemail-0.9.5-dep
|
||||
mkdir -p externals
|
||||
wget -nc -P externals http://ftp.debian.org/debian/pool/main/r/roundcube/$src_fn
|
||||
tar -C /tmp -xzf $(pwd)/externals/$src_fn
|
||||
if [ ! -d /var/lib/roundcube/SQL ]; then mv /tmp/$src_dir/SQL/ /var/lib/roundcube/; fi
|
||||
rm -rf /tmp/$src_dir
|
||||
|
||||
# Settings
|
||||
tools/editconf.py /etc/roundcube/main.inc.php \
|
||||
"\$rcmail_config['default_host']='ssl://localhost';" \
|
||||
"\$rcmail_config['default_port']=993;" \
|
||||
"\$rcmail_config['imap_timeout']=30;" \
|
||||
"\$rcmail_config['smtp_server']='tls://localhost';"\
|
||||
"\$rcmail_config['smtp_user']='%u';"\
|
||||
"\$rcmail_config['smtp_pass']='%p';"\
|
||||
"\$rcmail_config['smtp_timeout']=30;" \
|
||||
"\$rcmail_config['use_https']=true;" \
|
||||
"\$rcmail_config['session_lifetime']=60*24*3;" \
|
||||
"\$rcmail_config['password_charset']='utf8';" \
|
||||
"\$rcmail_config['message_sort_col']='arrival';" \
|
||||
"\$rcmail_config['junk_mbox']='Spam';" \
|
||||
"\$rcmail_config['default_folders']=array('INBOX', 'Drafts', 'Sent', 'Spam', 'Trash');" \
|
||||
"\$rcmail_config['draft_autosave']=30;" \
|
||||
"\$rcmail_config['plugins']=array('password');"
|
||||
|
||||
# Password changing plugin settings
|
||||
# The config comes empty by default, so we need the settings
|
||||
# we're not planning to change in config.inc.dist...
|
||||
cp /usr/share/roundcube/plugins/password/config.inc.php.dist \
|
||||
/etc/roundcube/plugins/password/config.inc.php
|
||||
|
||||
tools/editconf.py /etc/roundcube/plugins/password/config.inc.php \
|
||||
"\$rcmail_config['password_minimum_length']=6;" \
|
||||
"\$rcmail_config['password_db_dsn']='sqlite:///$STORAGE_ROOT/mail/users.sqlite';" \
|
||||
"\$rcmail_config['password_query']='UPDATE users SET password=%D WHERE email=%u';" \
|
||||
"\$rcmail_config['password_dovecotpw']='/usr/bin/doveadm pw';" \
|
||||
"\$rcmail_config['password_dovecotpw_method']='SHA512-CRYPT';" \
|
||||
"\$rcmail_config['password_dovecotpw_with_method']=true;"
|
||||
|
||||
# Configure storage of user preferences.
|
||||
mkdir -p $STORAGE_ROOT/mail/roundcube
|
||||
cat - > /etc/roundcube/debian-db.php <<EOF;
|
||||
<?php
|
||||
\$dbtype = 'sqlite';
|
||||
\$basepath = '$STORAGE_ROOT/mail/roundcube';
|
||||
\$dbname = 'roundcube.sqlite';
|
||||
?>
|
||||
EOF
|
||||
chown -R www-data.www-data $STORAGE_ROOT/mail/roundcube
|
||||
|
||||
# so PHP can use doveadm
|
||||
usermod -a -G dovecot www-data
|
||||
|
||||
# set permissions so that PHP can use users.sqlite
|
||||
# could use dovecot instead of www-data, but not sure it matters
|
||||
chown root.www-data $STORAGE_ROOT/mail
|
||||
chmod 775 $STORAGE_ROOT/mail
|
||||
chown root.www-data $STORAGE_ROOT/mail/users.sqlite
|
||||
chmod 664 $STORAGE_ROOT/mail/users.sqlite
|
||||
|
||||
# Enable PHP modules.
|
||||
php5enmod mcrypt
|
||||
service php-fastcgi restart
|
||||
Reference in New Issue
Block a user