1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2025-04-21 03:02:09 +00:00

Added Travis CI, more checks to scripts, output more system information and fixed bugs.

This commit is contained in:
Teal Dulcet 2018-09-29 02:01:47 -07:00
parent 504a9b0abc
commit 0e70f2bb49
30 changed files with 539 additions and 419 deletions

27
.travis.yml Normal file
View File

@ -0,0 +1,27 @@
language: bash
sudo: enabled
env:
global:
- NONINTERACTIVE=1
- PUBLIC_IP=auto
- PUBLIC_IPV6=auto
- PRIMARY_HOSTNAME=auto
- SKIP_NETWORK_CHECKS=1
before_script:
- sudo sed -i "s/^127.0.1.1.*/127.0.1.1\t$HOSTNAME.example.com\t$HOSTNAME/" /etc/hosts
- sudo apt-get -yqq update
- sudo apt-get -yqq dist-upgrade
- sudo rm -f /opt/jdk_switcher/jdk_switcher.sh
script:
- sudo setup/start.sh
- curl -IkL "https://$HOSTNAME/"
- curl -IkL "https://$HOSTNAME/admin"
- curl -IkL "https://$HOSTNAME/mail/"
- curl -IkL "https://$HOSTNAME/cloud/"
- bash -c 'shopt -s globstar; shellcheck -s bash **/*.sh || true'
# - cd test
# - pip install -r requirements.txt
# - pytest

View File

@ -2,13 +2,13 @@
Mail-in-a-Box is an open source community project about working, as a group, to empower ourselves and others to have control over our own digital communications. Just as we hope to increase technological diversity on the Internet through decentralization, we also believe that diverse viewpoints and voices among our community members foster innovation and creative solutions to the challenges we face.
We are committed to providing a safe, welcoming, and harrassment-free space for collaboration, for everyone, without regard to age, disability, economic situation, ethnicity, gender identity and expression, language fluency, level of knowledge or experience, nationality, personal appearance, race, religion, sexual identity and orientation, or any other attribute. Community comes first. This policy supersedes all other project goals.
We are committed to providing a safe, welcoming, and harassment-free space for collaboration, for everyone, without regard to age, disability, economic situation, ethnicity, gender identity and expression, language fluency, level of knowledge or experience, nationality, personal appearance, race, religion, sexual identity and orientation, or any other attribute. Community comes first. This policy supersedes all other project goals.
The maintainers of Mail-in-a-Box share the dual responsibility of leading by example and enforcing these policies as necessary to maintain an open and welcoming environment. All community members should be excellent to each other.
## Scope
This Code of Conduct applies to all places where Mail-in-a-Box community activity is ocurring, including on GitHub, in discussion forums, on Slack, on social media, and in real life. The Code of Conduct applies not only on websites/at events run by the Mail-in-a-Box community (e.g. our GitHub organization, our Slack team) but also at any other location where the Mail-in-a-Box community is present (e.g. in issues of other GitHub organizations where Mail-in-a-Box community members are discussing problems related to Mail-in-a-Box, or real-life professional conferences), or whenever a Mail-in-a-Box community member is representing Mail-in-a-Box to the public at large or acting on behalf of Mail-in-a-Box.
This Code of Conduct applies to all places where Mail-in-a-Box community activity is occurring, including on GitHub, in discussion forums, on Slack, on social media, and in real life. The Code of Conduct applies not only on websites/at events run by the Mail-in-a-Box community (e.g. our GitHub organization, our Slack team) but also at any other location where the Mail-in-a-Box community is present (e.g. in issues of other GitHub organizations where Mail-in-a-Box community members are discussing problems related to Mail-in-a-Box, or real-life professional conferences), or whenever a Mail-in-a-Box community member is representing Mail-in-a-Box to the public at large or acting on behalf of Mail-in-a-Box.
This code does not apply to activity on a server running Mail-in-a-Box software, unless your server is hosting a service for the Mail-in-a-Box community at large.

View File

@ -16,15 +16,17 @@ With Vagrant set up, the following should boot up Mail-in-a-Box inside a virtual
$ vagrant up --provision
_If you're seeing an error message about your *IP address being listed in the Spamhaus Block List*, simply uncomment the `export SKIP_NETWORK_CHECKS=1` line in `Vagrantfile`. It's normal, you're probably using a dynamic IP address assigned by your Internet providerthey're almost all listed._
_If you are seeing an error message about your *IP address being listed in the Spamhaus Block List*, simply uncomment the `export SKIP_NETWORK_CHECKS=1` line in `Vagrantfile`. It is normal, you are probably using a dynamic IP address assigned by your Internet providerthey are almost all listed._
### Modifying your `hosts` file
After a while, Mail-in-a-Box will be available at `192.168.50.4` (unless you changed that in your `Vagrantfile`). To be able to use the web-based bits, we recommend to add a hostname to your `hosts` file:
After a while, Mail-in-a-Box will be available at `192.168.50.4` (unless you changed that in your `Vagrantfile`). To be able to use the web-based bits, we recommend adding a hostname to your `hosts` file:
$ echo "192.168.50.4 mailinabox.lan" | sudo tee -a /etc/hosts
$ echo -e "192.168.50.4\tmailinabox.lan" | sudo tee -a /etc/hosts
or
$ sudo sed -i "s/^127.0.1.1.*/192.168.50.4\tmailinabox.lan/" /etc/hosts
You should now be able to navigate to https://mailinabox.lan/admin using your browser. There should be an initial admin user with the name `me@mailinabox.lan` and the password `12345678`.
You should now be able to navigate to https://mailinabox.lan/admin using your browser. There should be an initial admin user with the name `me@mailinabox.lan` and the randomly generated password from the output.
### Making changes
@ -43,7 +45,7 @@ Once inside the VM, you can re-run individual parts of the setup like in this ex
### Tests
Mail-in-a-Box needs more tests. If you're still looking for a way to help out, writing and contributing tests would be a great start!
Mail-in-a-Box needs more tests. If you were still looking for a way to help out, writing and contributing tests would be a great start!
## Public domain

View File

@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/mail-in-a-box/mailinabox.svg?branch=master)](https://travis-ci.org/mail-in-a-box/mailinabox)
Mail-in-a-Box
=============
@ -13,7 +15,7 @@ Our goals are to:
* Make deploying a good mail server easy.
* Promote [decentralization](http://redecentralize.org/), innovation, and privacy on the web.
* Have automated, auditable, and [idempotent](https://sharknet.us/2014/02/01/automated-configuration-management-challenges-with-idempotency/) configuration.
* Have automated, auditable and [idempotent](https://sharknet.us/2014/02/01/automated-configuration-management-challenges-with-idempotency/) configuration.
* **Not** make a totally unhackable, NSA-proof server.
* **Not** make something customizable by power users.
@ -28,11 +30,11 @@ It is a one-click email appliance. There are no user-configurable setup options.
The components installed are:
* SMTP ([postfix](http://www.postfix.org/)), IMAP ([dovecot](http://dovecot.org/)), CardDAV/CalDAV ([Nextcloud](https://nextcloud.com/)), Exchange ActiveSync ([z-push](http://z-push.org/))
* Webmail ([Roundcube](http://roundcube.net/)), static website hosting ([nginx](http://nginx.org/))
* Spam filtering ([spamassassin](https://spamassassin.apache.org/)), greylisting ([postgrey](http://postgrey.schweikert.ch/))
* DNS ([nsd4](https://www.nlnetlabs.nl/projects/nsd/)) with [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework), DKIM ([OpenDKIM](http://www.opendkim.org/)), [DMARC](https://en.wikipedia.org/wiki/DMARC), [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC), [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities), and [SSHFP](https://tools.ietf.org/html/rfc4255) records automatically set
* Backups ([duplicity](http://duplicity.nongnu.org/)), firewall ([ufw](https://launchpad.net/ufw)), intrusion protection ([fail2ban](http://www.fail2ban.org/wiki/index.php/Main_Page)), system monitoring ([munin](http://munin-monitoring.org/))
* SMTP ([Postfix](http://www.postfix.org/)), POP3/IMAP ([Dovecot](https://dovecot.org/)), [CardDAV](https://en.wikipedia.org/wiki/CardDAV)/[CalDAV](https://en.wikipedia.org/wiki/CalDAV) ([Nextcloud](https://nextcloud.com/)), Exchange ActiveSync ([Z-Push](http://z-push.org/))
* Webmail ([Roundcube](https://roundcube.net/)), static website hosting ([nginx](https://nginx.org/))
* Spam filtering ([SpamAssassin](https://spamassassin.apache.org/)), greylisting ([Postgrey](http://postgrey.schweikert.ch/))
* DNS ([nsd4](https://www.nlnetlabs.nl/projects/nsd/)) with [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework), [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) ([OpenDKIM](http://www.opendkim.org/)), [DMARC](https://en.wikipedia.org/wiki/DMARC) ([OpenDMARC](http://www.trusteddomain.org/opendmarc/)), [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC), [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities), and [SSHFP](https://tools.ietf.org/html/rfc4255) records automatically set
* Backups ([duplicity](http://duplicity.nongnu.org/)), firewall ([UFW](https://launchpad.net/ufw)), intrusion protection ([Fail2Ban](https://www.fail2ban.org/wiki/index.php/Main_Page)), system monitoring ([munin](http://munin-monitoring.org/))
It also includes:
@ -53,8 +55,7 @@ Clone this repository:
$ git clone https://github.com/mail-in-a-box/mailinabox
$ cd mailinabox
_Optional:_ Download my PGP key and then verify that the sources were signed
by me:
_Optional:_ Download my PGP key and then verify that I signed the sources:
$ curl -s https://keybase.io/joshdata/key.asc | gpg --import
gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported
@ -66,9 +67,9 @@ by me:
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 5F4C 0E73 13CC D744 693B 2AEA B920 41F4 C10B DD81
You'll get a lot of warnings, but that's OK. Check that the primary key fingerprint matches the
You will get many warnings, but that is OK. Check that the primary key fingerprint matches the
fingerprint in the key details at [https://keybase.io/joshdata](https://keybase.io/joshdata)
and on my [personal homepage](https://razor.occams.info/). (Of course, if this repository has been compromised you can't trust these instructions.)
and on my [personal homepage](https://razor.occams.info/). (Of course, if this repository has been compromised you cannot trust these instructions.)
Checkout the tag corresponding to the most recent release:
@ -80,7 +81,7 @@ Begin the installation.
For help, DO NOT contact me directly --- I don't do tech support by email or tweet (no exceptions).
Post your question on the [discussion forum](https://discourse.mailinabox.email/) instead, where me and other Mail-in-a-Box users may be able to help you.
Post your question on the [discussion forum](https://discourse.mailinabox.email/) instead, where I and other Mail-in-a-Box users may be able to help you.
Contributing and Development
----------------------------
@ -98,8 +99,8 @@ Mail-in-a-Box is similar to [iRedMail](http://www.iredmail.org/) and [Modoboa](h
The History
-----------
* In 2007 I wrote a relatively popular Mozilla Thunderbird extension that added client-side SPF and DKIM checks to mail to warn users about possible phishing: [add-on page](https://addons.mozilla.org/en-us/thunderbird/addon/sender-verification-anti-phish/), [source](https://github.com/JoshData/thunderbird-spf).
* In August 2013 I began Mail-in-a-Box by combining my own mail server configuration with the setup in ["NSA-proof your email in 2 hours"](http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/) and making the setup steps reproducible with bash scripts.
* In 2007, I wrote a relatively popular Mozilla Thunderbird extension that added client-side SPF and DKIM checks to mail to warn users about possible phishing: [add-on page](https://addons.mozilla.org/en-us/thunderbird/addon/sender-verification-anti-phish/), [source](https://github.com/JoshData/thunderbird-spf).
* In August 2013, I began Mail-in-a-Box by combining my own mail server configuration with the setup in ["NSA-proof your email in 2 hours"](http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/) and making the setup steps reproducible with bash scripts.
* Mail-in-a-Box was a semifinalist in the 2014 [Knight News Challenge](https://www.newschallenge.org/challenge/2014/submissions/mail-in-a-box), but it was not selected as a winner.
* Mail-in-a-Box hit the front page of Hacker News in [April](https://news.ycombinator.com/item?id=7634514) 2014, [September](https://news.ycombinator.com/item?id=8276171) 2014, [May](https://news.ycombinator.com/item?id=9624267) 2015, and [November](https://news.ycombinator.com/item?id=13050500) 2016.
* FastCompany mentioned Mail-in-a-Box a [roundup of privacy projects](http://www.fastcompany.com/3047645/your-own-private-cloud) on June 26, 2015.

View File

@ -11,7 +11,7 @@ export LC_TYPE=en_US.UTF-8
# On Mondays, i.e. once a week, send the administrator a report of total emails
# sent and received so the admin might notice server abuse.
if [ `date "+%u"` -eq 1 ]; then
if [ "$(date "+%u")" -eq 1 ]; then
management/mail_log.py -t week | management/email_administrator.py "Mail-in-a-Box Usage Report"
fi

View File

@ -15,7 +15,7 @@ The primary goal of Mail-in-a-Box is to make deploying a good mail server easy,
* Do not have physical access to the box (i.e., we do not aim to protect the box from physical access).
* Have not been given Unix accounts on the box (i.e., we assume all users with shell access are trusted).
On the other hand, we do assume that adversaries are performing passive surveillance and, possibly, active man-in-the-middle attacks. And so:
On the other hand, we do assume that adversaries are performing passive surveillance and, possibly, active man-in-the-middle attacks. Therefore:
* User credentials are always sent through SSH/TLS, never in the clear, with modern TLS settings.
* Outbound mail is sent with the highest level of TLS possible.
@ -41,11 +41,11 @@ The services all follow these rules:
* TLS certificates are generated with 2048-bit RSA keys and SHA-256 fingerprints. The box provides a self-signed certificate by default. The [setup guide](https://mailinabox.email/guide.html) explains how to verify the certificate fingerprint on first login. Users are encouraged to replace the certificate with a proper CA-signed one. ([source](setup/ssl.sh))
* Only TLSv1, TLSv1.1 and TLSv1.2 are offered (the older SSL protocols are not offered).
* HTTPS, IMAP, and POP track the [Mozilla Intermediate Ciphers Recommendation](https://wiki.mozilla.org/Security/Server_Side_TLS), balancing security with supporting a wide range of mail clients. Diffie-Hellman ciphers use a 2048-bit key for forward secrecy. For more details, see the [output of SSLyze for these ports](tests/tls_results.txt).
* SMTP (port 25) uses the Postfix medium grade ciphers and SMTP Submission (port 587) uses the Postfix high grade ciphers ([more info](http://www.postfix.org/postconf.5.html#smtpd_tls_mandatory_ciphers)).
* SMTP (port 25) uses the Postfix medium grade ciphers and SMTP Submission (port 587) uses the Postfix high-grade ciphers ([more info](http://www.postfix.org/postconf.5.html#smtpd_tls_mandatory_ciphers)).
Additionally:
* SMTP Submission (port 587) will not accept user credentials without STARTTLS (true also of SMTP on port 25 in case of client misconfiguration), and the submission port won't accept mail without encryption. The minimum cipher key length is 128 bits. (The box is of course configured not to be an open relay. User credentials are required to send outbound mail.) ([source](setup/mail-postfix.sh))
* SMTP Submission (port 587) will not accept user credentials without STARTTLS (true also of SMTP on port 25 in case of client misconfiguration), and the submission port will not accept mail without encryption. The minimum cipher key length is 128 bits. (The box is of course configured not to be an open relay. User credentials are required to send outbound mail.) ([source](setup/mail-postfix.sh))
* HTTPS (port 443): The HTTPS Strict Transport Security header is set. A redirect from HTTP to HTTPS is offered. The [Qualys SSL Labs test](https://www.ssllabs.com/ssltest) should report an A+ grade. ([source 1](conf/nginx-ssl.conf), [source 2](conf/nginx.conf))
### Password Storage
@ -66,20 +66,20 @@ If DNSSEC is enabled at the box's domain name's registrar, the SSHFP record that
`fail2ban` provides some protection from brute-force login attacks (repeated logins that guess account passwords) by blocking offending IP addresses at the network level.
The following services are protected: SSH, IMAP (dovecot), SMTP submission (postfix), webmail (roundcube), Nextcloud/CalDAV/CardDAV (over HTTP), and the Mail-in-a-Box control panel & munin (over HTTP).
The following services are protected: SSH, IMAP (Dovecot), SMTP submission (Postfix), webmail (Roundcube), Nextcloud/CalDAV/CardDAV (over HTTP), and the Mail-in-a-Box control panel & munin (over HTTP).
Some other services running on the box may be missing fail2ban filters.
Some other services running on the box may be missing Fail2Ban filters.
`fail2ban` only blocks IPv4 addresses, however. If the box has a public IPv6 address, it is not protected from these attacks.
Outbound Mail
-------------
The basic protocols of email delivery did not plan for the presence of adversaries on the network. For a number of reasons it is not possible in most cases to guarantee that a connection to a recipient server is secure.
The basic protocols of email delivery did not plan for the presence of adversaries on the network. For a number of reasons, it is not possible in most cases to guarantee that a connection to a recipient server is secure.
### DNSSEC
The first step in resolving the destination server for an email address is performing a DNS look-up for the MX record of the domain name. The box uses a locally-running [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC)-aware nameserver to perform the lookup. If the domain name has DNSSEC enabled, DNSSEC guards against DNS records being tampered with.
The first step in resolving the destination server for an email address is performing a DNS look-up for the MX record of the domain name. The box uses a locally running [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC)-aware nameserver to perform the lookup. If the domain name has DNSSEC enabled, DNSSEC guards against DNS records being tampered with.
### Encryption
@ -87,15 +87,15 @@ The box (along with the vast majority of mail servers) uses [opportunistic encry
### DANE
If the recipient's domain name supports DNSSEC and has published a [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) record, then on-the-wire encryption is forced between the box and the recipient MTA and this encryption is not subject to a man-in-the-middle attack. The TLSA record contains a certificate fingerprint which the receiving MTA (server) must present to the box. ([source](setup/mail-postfix.sh))
If the recipient's domain name supports DNSSEC and has published a [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities) record, then on-the-wire encryption is forced between the box and the recipient MTA and this encryption is not subject to a man-in-the-middle attack. The TLSA record contains a certificate fingerprint, which the receiving MTA (server) must present to the box. ([source](setup/mail-postfix.sh))
### Domain Policy Records
Domain policy records allow recipient MTAs to detect when the _domain_ part of of the sender address in incoming mail has been spoofed. All outbound mail is signed with [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) and "quarantine" [DMARC](https://en.wikipedia.org/wiki/DMARC) records are automatically set in DNS. Receiving MTAs that implement DMARC will automatically quarantine mail that is "From:" a domain hosted by the box but which was not sent by the box. (Strong [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework) records are also automatically set in DNS.) ([source](management/dns_update.py))
Domain policy records allow recipient MTAs to detect when the _domain_ part of the sender address in incoming mail has been spoofed. All outbound mail is signed with [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) and "quarantine" [DMARC](https://en.wikipedia.org/wiki/DMARC) records are automatically set in DNS. Receiving MTAs that implement DMARC will automatically quarantine mail that is "From:" a domain hosted by the box, but which was not sent by the box. (Strong [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework) records are also automatically set in DNS.) ([source](management/dns_update.py))
### User Policy
While domain policy records prevent other servers from sending mail with a "From:" header that matches a domain hosted on the box (see above), those policy records do not guarnatee that the user portion of the sender email address matches the actual sender. In enterprise environments where the box may host the mail of untrusted users, it is important to guard against users impersonating other users.
While domain policy records prevent other servers from sending mail with a "From:" header that matches a domain hosted on the box (see above), those policy records do not guarantee that the user portion of the sender email address matches the actual sender. In enterprise environments where the box may host the mail of untrusted users, it is important to guard against users impersonating other users.
The box restricts the envelope sender address (also called the return path or MAIL FROM address --- this is different from the "From:" header) that users may put into outbound mail. The envelope sender address must be either their own email address (their SMTP login username) or any alias that they are listed as a permitted sender of. (There is currently no restriction on the contents of the "From:" header.)
@ -112,4 +112,4 @@ When DNSSEC is enabled at the box's domain name's registrar, [DANE TLSA](https:/
### Filters
Incoming mail is run through several filters. Email is bounced if the sender's IP address is listed in the [Spamhaus Zen blacklist](http://www.spamhaus.org/zen/) or if the sender's domain is listed in the [Spamhaus Domain Block List](http://www.spamhaus.org/dbl/). Greylisting (with [postgrey](http://postgrey.schweikert.ch/)) is also used to cut down on spam. ([source](setup/mail-postfix.sh))
Incoming mail is run through several filters. Email is bounced if the sender's IP address is listed in the [Spamhaus Zen blacklist](http://www.spamhaus.org/zen/) or if the sender's domain is listed in the [Spamhaus Domain Block List](http://www.spamhaus.org/dbl/). Greylisting (with [Postgrey](http://postgrey.schweikert.ch/)) is also used to cut down on spam. ([source](setup/mail-postfix.sh))

View File

@ -2,7 +2,8 @@
#########################################################
# This script is intended to be run like this:
#
# curl https://mailinabox.email/setup.sh | sudo bash
# wget https://mailinabox.email/setup.sh -qO - | sudo bash -s
# curl -s https://mailinabox.email/setup.sh | sudo bash -s
#
#########################################################
@ -10,41 +11,58 @@ if [ -z "$TAG" ]; then
TAG=v0.28
fi
if [[ "$#" -ne 0 ]]; then
echo "Usage: \"wget https://mailinabox.email/setup.sh -qO - | sudo bash -s\" or \"curl -s https://mailinabox.email/setup.sh | sudo bash -s\"" >&2
exit 1
fi
# Are we running as root?
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root. Did you leave out sudo?"
exit
echo "This script must be run as root. Did you leave out sudo?" >&2
exit 1
fi
# Check if on Linux
if ! echo "$OSTYPE" | grep -iq "linux"; then
echo "Error: This script must be run on Linux." >&2
exit 1
fi
# Check connectivity
if ! ping -q -c 3 mailinabox.email > /dev/null 2>&1; then
echo "Error: Could not reach mailinabox.email, please check your internet connection and run this script again." >&2
exit 1
fi
# Clone the Mail-in-a-Box repository if it doesn't exist.
if [ ! -d $HOME/mailinabox ]; then
if [ ! -d "$HOME/mailinabox" ]; then
if [ ! -f /usr/bin/git ]; then
echo Installing git . . .
apt-get -q -q update
DEBIAN_FRONTEND=noninteractive apt-get -q -q install -y git < /dev/null
echo "Installing git . . ."
apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -yqq install git < /dev/null
echo
fi
echo Downloading Mail-in-a-Box $TAG. . .
echo "Downloading Mail-in-a-Box $TAG. . ."
git clone \
-b $TAG --depth 1 \
https://github.com/mail-in-a-box/mailinabox \
$HOME/mailinabox \
< /dev/null 2> /dev/null
"$HOME/mailinabox" \
< /dev/null
echo
fi
# Change directory to it.
cd $HOME/mailinabox
cd "$HOME/mailinabox"
# Update it.
if [ "$TAG" != `git describe` ]; then
echo Updating Mail-in-a-Box to $TAG . . .
if [ "$TAG" != "$(git describe)" ]; then
echo "Updating Mail-in-a-Box to $TAG . . ."
git fetch --depth 1 --force --prune origin tag $TAG
if ! git checkout -q $TAG; then
echo "Update failed. Did you modify something in `pwd`?"
exit
echo "Update failed. Did you modify something in $(pwd)?" >&2
exit 1
fi
echo
fi

View File

@ -10,12 +10,12 @@ source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars
# Install DKIM...
echo Installing OpenDKIM/OpenDMARC...
echo "Installing OpenDKIM/OpenDMARC..."
apt_install opendkim opendkim-tools opendmarc
# Make sure configuration directories exist.
mkdir -p /etc/opendkim;
mkdir -p $STORAGE_ROOT/mail/dkim
mkdir -p "$STORAGE_ROOT/mail/dkim"
# Used in InternalHosts and ExternalIgnoreList configuration directives.
# Not quite sure why.
@ -47,12 +47,12 @@ fi
# such as Google. But they and others use a 2048 bit key, so we'll
# do the same. Keys beyond 2048 bits may exceed DNS record limits.
if [ ! -f "$STORAGE_ROOT/mail/dkim/mail.private" ]; then
opendkim-genkey -b 2048 -r -s mail -D $STORAGE_ROOT/mail/dkim
opendkim-genkey -b 2048 -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
chown -R opendkim:opendkim "$STORAGE_ROOT/mail/dkim"
chmod go-rwx "$STORAGE_ROOT/mail/dkim"
tools/editconf.py /etc/opendmarc.conf -s \
"Syslog=true" \

View File

@ -92,13 +92,13 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then
# ldns-keygen uses /dev/random for generating random numbers by default.
# This is slow and unecessary if we ensure /dev/urandom is seeded properly,
# so we use /dev/urandom. See system.sh for an explanation. See #596, #115.
KSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -r /dev/urandom -a $algo -b 2048 -k _domain_);
KSK=$(umask 077; cd "$STORAGE_ROOT/dns/dnssec"; ldns-keygen -r /dev/urandom -a $algo -b 2048 -k _domain_);
# Now create a Zone-Signing Key (ZSK) which is expected to be
# rotated more often than a KSK, although we have no plans to
# rotate it (and doing so would be difficult to do without
# disturbing DNS availability.) Omit `-k` and use a shorter key length.
ZSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -r /dev/urandom -a $algo -b 1024 _domain_);
ZSK=$(umask 077; cd "$STORAGE_ROOT/dns/dnssec"; ldns-keygen -r /dev/urandom -a $algo -b 1024 _domain_);
# These generate two sets of files like:
#
@ -110,7 +110,7 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then
# options. So we'll store the names of the files we just generated.
# We might have multiple keys down the road. This will identify
# what keys are the current keys.
cat > $STORAGE_ROOT/dns/dnssec/$algo.conf << EOF;
cat > "$STORAGE_ROOT/dns/dnssec/$algo.conf" << EOF;
KSK=$KSK
ZSK=$ZSK
EOF
@ -126,7 +126,7 @@ cat > /etc/cron.daily/mailinabox-dnssec << EOF;
#!/bin/bash
# Mail-in-a-Box
# Re-sign any DNS zones with DNSSEC because the signatures expire periodically.
`pwd`/tools/dns_update
$(pwd)/tools/dns_update
EOF
chmod +x /etc/cron.daily/mailinabox-dnssec

View File

@ -1,6 +1,6 @@
# If there aren't any mail users yet, create one.
if [ -z "`tools/mail.py user`" ]; then
# The outut of "tools/mail.py user" is a list of mail users. If there
if [ -z "$(tools/mail.py user)" ]; then
# The output of "tools/mail.py user" is a list of mail users. If there
# aren't any yet, it'll be empty.
# If we didn't ask for an email address at the start, do so now.
@ -10,23 +10,23 @@ if [ -z "`tools/mail.py user`" ]; then
input_box "Mail Account" \
"Let's create your first mail account.
\n\nWhat email address do you want?" \
me@`get_default_hostname` \
"me@$(get_default_hostname)" \
EMAIL_ADDR
if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel
exit
exit 1
fi
while ! management/mailconfig.py validate-email "$EMAIL_ADDR"
do
input_box "Mail Account" \
"That's not a valid email address.
\n\nWhat email address do you want?" \
$EMAIL_ADDR \
"$EMAIL_ADDR" \
EMAIL_ADDR
if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel
exit
exit 1
fi
done
@ -35,9 +35,10 @@ if [ -z "`tools/mail.py user`" ]; then
else
# Use me@PRIMARY_HOSTNAME
EMAIL_ADDR=me@$PRIMARY_HOSTNAME
EMAIL_PW=12345678
EMAIL_PW=$(openssl rand -base64 8)
echo
echo "Creating a new administrative mail account for $EMAIL_ADDR with password $EMAIL_PW."
echo -e "Creating a new administrative mail account for: $EMAIL_ADDR\n\t\t\t\t with password: $EMAIL_PW"
echo "Warning: This is a security risk. Please change the password after your first login."
echo
fi
else
@ -47,11 +48,11 @@ if [ -z "`tools/mail.py user`" ]; then
fi
# Create the user's mail account. This will ask for a password if none was given above.
tools/mail.py user add $EMAIL_ADDR $EMAIL_PW
tools/mail.py user add "$EMAIL_ADDR" "$EMAIL_PW"
# Make it an admin.
hide_output tools/mail.py user make-admin $EMAIL_ADDR
hide_output tools/mail.py user make-admin "$EMAIL_ADDR"
# Create an alias to which we'll direct all automatically-created administrative aliases.
tools/mail.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR > /dev/null
tools/mail.py alias add "administrator@$PRIMARY_HOSTNAME" "$EMAIL_ADDR" > /dev/null
fi

View File

@ -3,25 +3,27 @@ function hide_output {
# and returns a non-zero exit code.
# Get a temporary file.
OUTPUT=$(tempfile)
OUTPUT=$(mktemp)
# Execute command, redirecting stderr/stdout to the temporary file.
$@ &> $OUTPUT
"$@" &> "$OUTPUT"
# If the command failed, show the output that was captured in the temporary file.
E=$?
if [ $E != 0 ]; then
# Something failed.
echo
echo FAILED: $@
echo -----------------------------------------
cat $OUTPUT
echo -----------------------------------------
echo "FAILED: $*"
echo "-----------------------------------------"
cat "$OUTPUT"
echo "-----------------------------------------"
# Remove temporary file.
rm -f "$OUTPUT"
exit $E
fi
# Remove temporary file.
rm -f $OUTPUT
rm -f "$OUTPUT"
}
function apt_get_quiet {
@ -44,8 +46,8 @@ function apt_install {
# install' for all of the packages. Calling `dpkg` on each package is slow,
# and doesn't affect what we actually do, except in the messages, so let's
# not do that anymore.
PACKAGES=$@
apt_get_quiet install $PACKAGES
PACKAGES=( "$@" )
apt_get_quiet install "${PACKAGES[@]}"
}
function apt_add_repository_to_unattended_upgrades {
@ -61,9 +63,9 @@ function get_default_hostname {
# Guess the machine's hostname. It should be a fully qualified
# domain name suitable for DNS. None of these calls may provide
# the right value, but it's the best guess we can make.
set -- $(hostname --fqdn 2>/dev/null ||
set -- "$(hostname --fqdn 2>/dev/null ||
hostname --all-fqdns 2>/dev/null ||
hostname 2>/dev/null)
hostname 2>/dev/null)"
printf '%s\n' "$1" # return this value
}
@ -121,28 +123,28 @@ function get_default_privateip {
route=$(ip -$1 -o route get $target | grep -v unreachable)
# Parse the address out of the route information.
address=$(echo $route | sed "s/.* src \([^ ]*\).*/\1/")
address=$(echo "$route" | sed "s/.* src \([^ ]*\).*/\1/")
if [[ "$1" == "6" && $address == fe80:* ]]; then
# For IPv6 link-local addresses, parse the interface out
# of the route information and append it with a '%'.
interface=$(echo $route | sed "s/.* dev \([^ ]*\).*/\1/")
interface=$(echo "$route" | sed "s/.* dev \([^ ]*\).*/\1/")
address=$address%$interface
fi
echo $address
echo "$address"
}
function ufw_allow {
if [ -z "$DISABLE_FIREWALL" ]; then
# ufw has completely unhelpful output
ufw allow $1 > /dev/null;
# UFW has completely unhelpful output
ufw allow "$1" > /dev/null;
fi
}
function restart_service {
hide_output service $1 restart
hide_output service "$1" restart
}
## Dialog Functions ##
@ -167,7 +169,7 @@ function input_menu {
declare -n result=$4
declare -n result_code=$4_EXITCODE
local IFS=^$'\n'
result=$(dialog --stdout --title "$1" --menu "$2" 0 0 0 $3)
result=$(dialog --stdout --title "$1" --menu "$2" 0 0 0 "$3")
result_code=$?
}
@ -178,17 +180,17 @@ function wget_verify {
HASH=$2
DEST=$3
CHECKSUM="$HASH $DEST"
rm -f $DEST
hide_output wget -O $DEST $URL
rm -f "$DEST"
hide_output wget -O "$DEST" "$URL"
if ! echo "$CHECKSUM" | sha1sum --check --strict > /dev/null; then
echo "------------------------------------------------------------"
echo "Download of $URL did not match expected checksum."
echo "Found:"
sha1sum $DEST
sha1sum "$DEST"
echo
echo "Expected:"
echo "$CHECKSUM"
rm -f $DEST
rm -f "$DEST"
exit 1
fi
}
@ -204,9 +206,9 @@ function git_clone {
SUBDIR=$3
TARGETPATH=$4
TMPPATH=/tmp/git-clone-$$
rm -rf $TMPPATH $TARGETPATH
git clone -q $REPO $TMPPATH || exit 1
(cd $TMPPATH; git checkout -q $TREEISH;) || exit 1
mv $TMPPATH/$SUBDIR $TARGETPATH
rm -rf $TMPPATH "$TARGETPATH"
git clone -q "$REPO" $TMPPATH || exit 1
(cd $TMPPATH; git checkout -q "$TREEISH";) || exit 1
mv "$TMPPATH/$SUBDIR" "$TARGETPATH"
rm -rf $TMPPATH
}

View File

@ -12,7 +12,7 @@
# mail as defined in a "sieve" script.
#
# Dovecot's LDA role comes after spam filtering. Postfix hands mail off
# to Spamassassin which in turn hands it off to Dovecot. This all happens
# to SpamAssassin which in turn hands it off to Dovecot. This all happens
# using the LMTP protocol.
source setup/functions.sh # load our functions
@ -23,7 +23,7 @@ source /etc/mailinabox.conf # load global vars
# but dovecot-lucene is packaged by *us* in the Mail-in-a-Box PPA,
# not by Ubuntu.
echo "Installing Dovecot (IMAP server)..."
echo "Installing Dovecot (POP3/IMAP server)..."
apt_install \
dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-sqlite sqlite3 \
dovecot-sieve dovecot-managesieved dovecot-lucene
@ -45,8 +45,8 @@ apt_install \
# - https://www.dovecot.org/list/dovecot/2012-August/137569.html
# - https://www.dovecot.org/list/dovecot/2011-December/132455.html
tools/editconf.py /etc/dovecot/conf.d/10-master.conf \
default_process_limit=$(echo "`nproc` * 250" | bc) \
default_vsz_limit=$(echo "`free -tm | tail -1 | awk '{print $2}'` / 3" | bc)M \
default_process_limit="$((CPU_CORES * 250))" \
default_vsz_limit="$(($(free -tm | tail -1 | awk '{print $2}') / 3))M" \
log_path=/var/log/mail.log
# The inotify `max_user_instances` default is 128, which constrains
@ -61,7 +61,7 @@ tools/editconf.py /etc/sysctl.conf \
# username part of the user's email address. We'll ensure that no bad domains or email addresses
# are created within the management daemon.
tools/editconf.py /etc/dovecot/conf.d/10-mail.conf \
mail_location=maildir:$STORAGE_ROOT/mail/mailboxes/%d/%n \
mail_location="maildir:$STORAGE_ROOT/mail/mailboxes/%d/%n" \
mail_privileged_group=mail \
first_valid_uid=0
@ -126,7 +126,7 @@ EOF
# ### LDA (LMTP)
# Enable Dovecot's LDA service with the LMTP protocol. It will listen
# on port 10026, and Spamassassin will be configured to pass mail there.
# on port 10026, and SpamAssassin will be configured to pass mail there.
#
# The disabled unix socket listener is normally how Postfix and Dovecot
# would communicate (see the Postfix setup script for the corresponding
@ -154,7 +154,7 @@ EOF
# Setting a `postmaster_address` is required or LMTP won't start. An alias
# will be created automatically by our management daemon.
tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \
postmaster_address=postmaster@$PRIMARY_HOSTNAME
postmaster_address="postmaster@$PRIMARY_HOSTNAME"
# ### Sieve
@ -163,7 +163,7 @@ tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \
sed -i "s/#mail_plugins = .*/mail_plugins = \$mail_plugins sieve/" /etc/dovecot/conf.d/20-lmtp.conf
# Configure sieve. We'll create a global script that moves mail marked
# as spam by Spamassassin into the user's Spam folder.
# as spam by SpamAssassin into the user's Spam folder.
#
# * `sieve_before`: The path to our global sieve which handles moving spam to the Spam folder.
#
@ -202,14 +202,14 @@ 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
mkdir -p "$STORAGE_ROOT/mail/mailboxes"
chown -R mail.mail "$STORAGE_ROOT/mail/mailboxes"
# Same for the sieve scripts.
mkdir -p $STORAGE_ROOT/mail/sieve
mkdir -p $STORAGE_ROOT/mail/sieve/global_before
mkdir -p $STORAGE_ROOT/mail/sieve/global_after
chown -R mail.mail $STORAGE_ROOT/mail/sieve
mkdir -p "$STORAGE_ROOT/mail/sieve"
mkdir -p "$STORAGE_ROOT/mail/sieve/global_before"
mkdir -p "$STORAGE_ROOT/mail/sieve/global_after"
chown -R mail.mail "$STORAGE_ROOT/mail/sieve"
# Allow the IMAP/POP ports in the firewall.
ufw_allow imaps

View File

@ -10,11 +10,11 @@
# other servers on the Internet. It is responsible for very
# basic email filtering such as by IP address and greylisting,
# it checks that the destination address is valid, rewrites
# destinations according to aliases, and passses email on to
# destinations according to aliases, and passes email on to
# another service for local mail delivery.
#
# The first hop in local mail delivery is to Spamassassin via
# LMTP. Spamassassin then passes mail over to Dovecot for
# The first hop in local mail delivery is to SpamAssassin via
# LMTP. SpamAssassin then passes mail over to Dovecot for
# storage in the user's mailbox.
#
# Postfix also listens on port 587 (SMTP+STARTLS) for
@ -37,7 +37,7 @@ source /etc/mailinabox.conf # load global vars
# * `postfix`: The SMTP server.
# * `postfix-pcre`: Enables header filtering.
# * `postgrey`: A mail policy service that soft-rejects mail the first time
# it is received. Spammers don't usually try agian. Legitimate mail
# it is received. Spammers don't usually try again. Legitimate mail
# always will.
# * `ca-certificates`: A trust store used to squelch postfix warnings about
# untrusted opportunistically-encrypted connections.
@ -46,7 +46,7 @@ source /etc/mailinabox.conf # load global vars
# a modified version of postgrey that lets senders whitelisted by dnswl.org
# pass through without being greylisted. So please note [dnswl's license terms](https://www.dnswl.org/?page_id=9):
# > Every user with more than 100000 queries per day on the public nameserver
# > infrastructure and every commercial vendor of dnswl.org data (eg through
# > infrastructure and every commercial vendor of dnswl.org data (e.g. through
# > anti-spam solutions) must register with dnswl.org and purchase a subscription.
echo "Installing Postfix (SMTP server)..."
@ -63,9 +63,9 @@ apt_install postfix postfix-pcre postgrey ca-certificates
# * Set the SMTP banner (which must have the hostname first, then anything).
tools/editconf.py /etc/postfix/main.cf \
inet_interfaces=all \
smtp_bind_address=$PRIVATE_IP \
smtp_bind_address6=$PRIVATE_IPV6 \
myhostname=$PRIMARY_HOSTNAME\
smtp_bind_address="$PRIVATE_IP" \
smtp_bind_address6="$PRIVATE_IPV6" \
myhostname="$PRIMARY_HOSTNAME"\
smtpd_banner="\$myhostname ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)" \
mydestination=localhost
@ -84,7 +84,7 @@ tools/editconf.py /etc/postfix/main.cf \
# * Do not add the OpenDMAC Authentication-Results header. That should only be added
# on incoming mail. Omit the OpenDMARC milter by re-setting smtpd_milters to the
# OpenDKIM milter only. See dkim.sh.
# * Even though we dont allow auth over non-TLS connections (smtpd_tls_auth_only below, and without auth the client cant
# * Even though we don't allow auth over non-TLS connections (smtpd_tls_auth_only below, and without auth the client cant
# send outbound mail), don't allow non-TLS mail submission on this port anyway to prevent accidental misconfiguration.
# * Require the best ciphers for incoming connections per http://baldric.net/2013/12/07/tls-ciphers-in-postfix-and-dovecot/.
# By putting this setting here we leave opportunistic TLS on incoming mail at default cipher settings (any cipher is better than none).
@ -121,9 +121,9 @@ sed -i "s/PUBLIC_IP/$PUBLIC_IP/" /etc/postfix/outgoing_mail_header_filters
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_dh1024_param_file=$STORAGE_ROOT/ssl/dh2048.pem \
smtpd_tls_cert_file="$STORAGE_ROOT/ssl/ssl_certificate.pem" \
smtpd_tls_key_file="$STORAGE_ROOT/ssl/ssl_private_key.pem" \
smtpd_tls_dh1024_param_file="$STORAGE_ROOT/ssl/dh2048.pem" \
smtpd_tls_protocols=\!SSLv2,\!SSLv3 \
smtpd_tls_ciphers=medium \
smtpd_tls_exclude_ciphers=aNULL,RC4 \
@ -172,14 +172,14 @@ tools/editconf.py /etc/postfix/main.cf \
# ### Incoming Mail
# Pass any incoming mail over to a local delivery agent. Spamassassin
# Pass any incoming mail over to a local delivery agent. SpamAssassin
# will act as the LDA agent at first. It is listening on port 10025
# with LMTP. Spamassassin will pass the mail over to Dovecot after.
# with LMTP. SpamAssassin will pass the mail over to Dovecot after.
#
# In a basic setup we would pass mail directly to Dovecot by setting
# virtual_transport to `lmtp:unix:private/dovecot-lmtp`.
#
tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:[127.0.0.1]:10025
tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:\[127.0.0.1\]:10025
# Who can send mail to us? Some basic filters.
#
@ -200,7 +200,7 @@ tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:[127.0.0.1]:10025
# "450 4.7.1 Client host rejected: Service unavailable". This is a retry code, so the mail doesn't properly bounce. #NODOC
tools/editconf.py /etc/postfix/main.cf \
smtpd_sender_restrictions="reject_non_fqdn_sender,reject_unknown_sender_domain,reject_authenticated_sender_login_mismatch,reject_rhsbl_sender dbl.spamhaus.org" \
smtpd_recipient_restrictions=permit_sasl_authenticated,permit_mynetworks,"reject_rbl_client zen.spamhaus.org",reject_unlisted_recipient,"check_policy_service inet:127.0.0.1:10023"
smtpd_recipient_restrictions="permit_sasl_authenticated,permit_mynetworks,reject_rbl_client zen.spamhaus.org,reject_unlisted_recipient,check_policy_service inet:127.0.0.1:10023"
# Postfix connects to Postgrey on the 127.0.0.1 interface specifically. Ensure that
# Postgrey listens on the same interface (and not IPv6, for instance).

View File

@ -5,7 +5,7 @@
#
# This script configures user authentication for Dovecot
# and Postfix (which relies on Dovecot) and destination
# validation by quering an Sqlite3 database of mail users.
# validation by querying an Sqlite3 database of mail users.
source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars
@ -18,10 +18,10 @@ source /etc/mailinabox.conf # load global vars
db_path=$STORAGE_ROOT/mail/users.sqlite
# 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, privileges TEXT NOT NULL DEFAULT '');" | sqlite3 $db_path;
echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 $db_path;
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, privileges TEXT NOT NULL DEFAULT '');" | sqlite3 "$db_path";
echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 "$db_path";
fi
# ### User Authentication
@ -128,7 +128,7 @@ EOF
# query the aliases table but also the users table when resolving
# aliases, i.e. we turn users into aliases from themselves to
# themselves. That means users will match in postfix's first query
# before postfix gets to the third query for catch-alls/domain alises.
# before postfix gets to the third query for catch-alls/domain aliases.
#
# If there is both an alias and a user for the same address either
# might be returned by the UNION, so the whole query is wrapped in

View File

@ -9,7 +9,7 @@ echo "Installing Mail-in-a-Box system management daemon..."
# We used to install management daemon-related Python packages
# directly to /usr/local/lib. We moved to a virtualenv because
# these packages might conflict with apt-installed packages.
# We may have a lingering version of acme that conflcits with
# We may have a lingering version of acme that conflicts with
# certbot, which we're about to install below, so remove it
# first. Once acme is installed by an apt package, this might
# break the package version and `apt-get install --reinstall python3-acme`
@ -29,7 +29,7 @@ done
#
# certbot installs EFF's certbot which we use to
# provision free TLS certificates.
apt_install duplicity python-pip python-virtualenv certbot
apt_install duplicity python-pip python-virtualenv certbot python-certbot-nginx
hide_output pip2 install --upgrade boto
# Create a virtualenv for the installation of Python 3 packages
@ -55,9 +55,9 @@ hide_output $venv/bin/pip install --upgrade \
# CONFIGURATION
# Create a backup directory and a random key for encrypting backups.
mkdir -p $STORAGE_ROOT/backup
if [ ! -f $STORAGE_ROOT/backup/secret_key.txt ]; then
$(umask 077; openssl rand -base64 2048 > $STORAGE_ROOT/backup/secret_key.txt)
mkdir -p "$STORAGE_ROOT/backup"
if [ ! -f "$STORAGE_ROOT/backup/secret_key.txt" ]; then
umask 077; openssl rand -base64 2048 > "$STORAGE_ROOT/backup/secret_key.txt"
fi
@ -91,11 +91,11 @@ rm -f /usr/local/bin/mailinabox-daemon # old path
cat > $inst_dir/start <<EOF;
#!/bin/bash
source $venv/bin/activate
exec python `pwd`/management/daemon.py
exec python $(pwd)/management/daemon.py
EOF
chmod +x $inst_dir/start
rm -f /etc/init.d/mailinabox
ln -s $(pwd)/conf/management-initscript /etc/init.d/mailinabox
ln -s "$(pwd)/conf/management-initscript" /etc/init.d/mailinabox
hide_output update-rc.d mailinabox defaults
# Remove old files we no longer use.
@ -108,7 +108,7 @@ rm -f /etc/cron.daily/mailinabox-statuschecks
cat > /etc/cron.d/mailinabox-nightly << EOF;
# Mail-in-a-Box --- Do not edit / will be overwritten on update.
# Run nightly tasks: backup, status checks.
0 3 * * * root (cd `pwd` && management/daily_tasks.sh)
0 3 * * * root (cd $(pwd) && management/daily_tasks.sh)
EOF
# Start the management server.

View File

@ -40,7 +40,7 @@ chown munin. /var/log/munin/munin-cgi-graph.log
# ensure munin-node knows the name of this machine
# and reduce logging level to warning
tools/editconf.py /etc/munin/munin-node.conf -s \
host_name=$PRIMARY_HOSTNAME \
host_name="$PRIMARY_HOSTNAME" \
log_level=1
# Update the activated plugins through munin's autoconfiguration.
@ -52,9 +52,9 @@ find /etc/munin/plugins/ -lname /usr/share/munin/plugins/ntp_ -print0 | xargs -0
# Deactivate monitoring of network interfaces that are not up. Otherwise we can get a lot of empty charts.
for f in $(find /etc/munin/plugins/ \( -lname /usr/share/munin/plugins/if_ -o -lname /usr/share/munin/plugins/if_err_ -o -lname /usr/share/munin/plugins/bonding_err_ \)); do
IF=$(echo $f | sed s/.*_//);
if ! ifquery $IF >/dev/null 2>/dev/null; then
rm $f;
IF=$(echo "$f" | sed s/.*_//);
if ! ifquery "$IF" >/dev/null 2>/dev/null; then
rm "$f";
fi;
done

View File

@ -6,15 +6,15 @@ apt_get_quiet install bind9-host sed netcat-openbsd
# The user might have chosen a name that was previously in use by a spammer
# and will not be able to reliably send mail. Do this after any automatic
# choices made above.
if host $PRIMARY_HOSTNAME.dbl.spamhaus.org > /dev/null; then
echo
echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the"
echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/"
echo "and http://www.spamhaus.org/query/domain/$PRIMARY_HOSTNAME."
echo
echo "You will not be able to send mail using this domain name, so"
echo "setup cannot continue."
echo
if host "$PRIMARY_HOSTNAME.dbl.spamhaus.org" > /dev/null; then
echo >&2
echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the" >&2
echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/" >&2
echo "and http://www.spamhaus.org/query/domain/$PRIMARY_HOSTNAME." >&2
echo >&2
echo "You will not be able to send mail using this domain name, so" >&2
echo "setup cannot continue." >&2
echo >&2
exit 1
fi
@ -22,19 +22,19 @@ fi
# The user might have ended up on an IP address that was previously in use
# by a spammer, or the user may be deploying on a residential network. We
# will not be able to reliably send mail in these cases.
REVERSED_IPV4=$(echo $PUBLIC_IP | sed "s/\([0-9]*\).\([0-9]*\).\([0-9]*\).\([0-9]*\)/\4.\3.\2.\1/")
if host $REVERSED_IPV4.zen.spamhaus.org > /dev/null; then
echo
echo "The IP address $PUBLIC_IP is listed in the Spamhaus Block List."
echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP."
echo
echo "You will not be able to send mail using this machine, so setup"
echo "cannot continue."
echo
echo "Associate a different IP address with this machine if possible."
echo "Many residential network IP addresses are listed, so Mail-in-a-Box"
echo "typically cannot be used on a residential Internet connection."
echo
REVERSED_IPV4=$(echo "$PUBLIC_IP" | sed "s/\([0-9]*\).\([0-9]*\).\([0-9]*\).\([0-9]*\)/\4.\3.\2.\1/")
if host "$REVERSED_IPV4.zen.spamhaus.org" > /dev/null; then
echo >&2
echo "The IP address $PUBLIC_IP is listed in the Spamhaus Block List." >&2
echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP." >&2
echo >&2
echo "You will not be able to send mail using this machine, so setup" >&2
echo "cannot continue." >&2
echo >&2
echo "Associate a different IP address with this machine if possible." >&2
echo "Many residential network IP addresses are listed, so Mail-in-a-Box" >&2
echo "typically cannot be used on a residential Internet connection." >&2
echo >&2
exit 1
fi
@ -42,16 +42,16 @@ fi
# networks block outbound port 25 to prevent their network from sending spam.
# See if we can reach one of Google's MTAs with a 5-second timeout.
if ! nc -z -w5 aspmx.l.google.com 25; then
echo
echo "Outbound mail (port 25) seems to be blocked by your network."
echo
echo "You will not be able to send mail using this machine, so setup"
echo "cannot continue."
echo
echo "Many residential networks block port 25 to prevent hijacked"
echo "machines from being able to send spam. I just tried to connect"
echo "to Google's mail server on port 25 but the connection did not"
echo "succeed."
echo
echo >&2
echo "Outbound mail (port 25) seems to be blocked by your network." >&2
echo >&2
echo "You will not be able to send mail using this machine, so setup" >&2
echo "cannot continue." >&2
echo >&2
echo "Many residential networks block port 25 to prevent hijacked" >&2
echo "machines from being able to send spam. I just tried to connect" >&2
echo "to Google's mail server on port 25 but the connection did not" >&2
echo "succeed." >&2
echo >&2
exit 1
fi

View File

@ -9,7 +9,7 @@ source /etc/mailinabox.conf # load global vars
echo "Installing Nextcloud (contacts/calendar)..."
# Keep the php5 dependancies for the owncloud upgrades
# Keep the php5 dependencies for the ownCloud upgrades
apt_install \
dbconfig-common \
php5-cli php5-sqlite php5-gd php5-imap php5-curl php-pear php-apc curl libapr1 libtool libcurl4-openssl-dev php-xml-parser \
@ -23,14 +23,14 @@ apt_install php7.0 php7.0-fpm \
# Migrate <= v0.10 setups that stored the ownCloud config.php in /usr/local rather than
# in STORAGE_ROOT. Move the file to STORAGE_ROOT.
if [ ! -f $STORAGE_ROOT/owncloud/config.php ] \
if [ ! -f "$STORAGE_ROOT/owncloud/config.php" ] \
&& [ -f /usr/local/lib/owncloud/config/config.php ]; then
# Move config.php and symlink back into previous location.
echo "Migrating owncloud/config.php to new location."
mv /usr/local/lib/owncloud/config/config.php $STORAGE_ROOT/owncloud/config.php \
mv /usr/local/lib/owncloud/config/config.php "$STORAGE_ROOT/owncloud/config.php" \
&& \
ln -sf $STORAGE_ROOT/owncloud/config.php /usr/local/lib/owncloud/config/config.php
ln -sf "$STORAGE_ROOT/owncloud/config.php" /usr/local/lib/owncloud/config/config.php
fi
InstallNextcloud() {
@ -42,11 +42,11 @@ InstallNextcloud() {
echo "Upgrading to Nextcloud version $version"
echo
# Remove the current owncloud/Nextcloud
# Remove the current ownCloud/Nextcloud
rm -rf /usr/local/lib/owncloud
# Download and verify
wget_verify https://download.nextcloud.com/server/releases/nextcloud-$version.zip $hash /tmp/nextcloud.zip
wget_verify "https://download.nextcloud.com/server/releases/nextcloud-$version.zip" "$hash" /tmp/nextcloud.zip
# Extract ownCloud/Nextcloud
unzip -q /tmp/nextcloud.zip -d /usr/local/lib
@ -54,7 +54,7 @@ InstallNextcloud() {
rm -f /tmp/nextcloud.zip
# The two apps we actually want are not in Nextcloud core. Download the releases from
# their github repositories.
# their GitHub repositories.
mkdir -p /usr/local/lib/owncloud/apps
wget_verify https://github.com/nextcloud/contacts/releases/download/v2.1.5/contacts.tar.gz b7460d15f1b78d492ed502d778c0c458d503ba17 /tmp/contacts.tgz
@ -70,44 +70,46 @@ InstallNextcloud() {
# Create a symlink to the config.php in STORAGE_ROOT (for upgrades we're restoring the symlink we previously
# put in, and in new installs we're creating a symlink and will create the actual config later).
ln -sf $STORAGE_ROOT/owncloud/config.php /usr/local/lib/owncloud/config/config.php
ln -sf "$STORAGE_ROOT/owncloud/config.php" /usr/local/lib/owncloud/config/config.php
# Make sure permissions are correct or the upgrade step won't run.
# $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress
# that error.
chown -f -R www-data.www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud
chown -f -R www-data.www-data "$STORAGE_ROOT/owncloud" /usr/local/lib/owncloud
# If this isn't a new installation, immediately run the upgrade script.
# Then check for success (0=ok and 3=no upgrade needed, both are success).
if [ -e $STORAGE_ROOT/owncloud/owncloud.db ]; then
if [ -e "$STORAGE_ROOT/owncloud/owncloud.db" ]; then
# ownCloud 8.1.1 broke upgrades. It may fail on the first attempt, but
# that can be OK.
sudo -u www-data php /usr/local/lib/owncloud/occ upgrade
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then
E=$?
if [ $E -ne 0 ] && [ $E -ne 3 ]; then
echo "Trying ownCloud upgrade again to work around ownCloud upgrade bug..."
sudo -u www-data php /usr/local/lib/owncloud/occ upgrade
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi
E=$?
if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi
sudo -u www-data php /usr/local/lib/owncloud/occ maintenance:mode --off
echo "...which seemed to work."
fi
fi
}
# We only install ownCloud intermediate versions to be able to seemlesly upgrade to Nextcloud
# We only install ownCloud intermediate versions to be able to seamlessly upgrade to Nextcloud
InstallOwncloud() {
version=$1
hash=$2
echo
echo "Upgrading to OwnCloud version $version"
echo "Upgrading to ownCloud version $version"
echo
# Remove the current owncloud/Nextcloud
# Remove the current ownCloud/Nextcloud
rm -rf /usr/local/lib/owncloud
# Download and verify
wget_verify https://download.owncloud.org/community/owncloud-$version.tar.bz2 $hash /tmp/owncloud.tar.bz2
wget_verify "https://download.owncloud.org/community/owncloud-$version.tar.bz2" "$hash" /tmp/owncloud.tar.bz2
# Extract ownCloud
@ -115,7 +117,7 @@ InstallOwncloud() {
rm -f /tmp/owncloud.tar.bz2
# The two apps we actually want are not in Nextcloud core. Download the releases from
# their github repositories.
# their GitHub repositories.
mkdir -p /usr/local/lib/owncloud/apps
wget_verify https://github.com/owncloud/contacts/releases/download/v1.4.0.0/contacts.tar.gz c1c22d29699456a45db447281682e8bc3f10e3e7 /tmp/contacts.tgz
@ -131,23 +133,25 @@ InstallOwncloud() {
# Create a symlink to the config.php in STORAGE_ROOT (for upgrades we're restoring the symlink we previously
# put in, and in new installs we're creating a symlink and will create the actual config later).
ln -sf $STORAGE_ROOT/owncloud/config.php /usr/local/lib/owncloud/config/config.php
ln -sf "$STORAGE_ROOT/owncloud/config.php" /usr/local/lib/owncloud/config/config.php
# Make sure permissions are correct or the upgrade step won't run.
# $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress
# that error.
chown -f -R www-data.www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud
chown -f -R www-data.www-data "$STORAGE_ROOT/owncloud" /usr/local/lib/owncloud
# If this isn't a new installation, immediately run the upgrade script.
# Then check for success (0=ok and 3=no upgrade needed, both are success).
if [ -e $STORAGE_ROOT/owncloud/owncloud.db ]; then
if [ -e "$STORAGE_ROOT/owncloud/owncloud.db" ]; then
# ownCloud 8.1.1 broke upgrades. It may fail on the first attempt, but
# that can be OK.
sudo -u www-data php5 /usr/local/lib/owncloud/occ upgrade
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then
E=$?
if [ $E -ne 0 ] && [ $E -ne 3 ]; then
echo "Trying ownCloud upgrade again to work around ownCloud upgrade bug..."
sudo -u www-data php5 /usr/local/lib/owncloud/occ upgrade
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi
E=$?
if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi
sudo -u www-data php5 /usr/local/lib/owncloud/occ maintenance:mode --off
echo "...which seemed to work."
fi
@ -161,40 +165,40 @@ owncloud_hash=e2b4a4bebd4fac14feae1e6e8997682f73fa8b50
if [ ! -d /usr/local/lib/owncloud/ ] \
|| ! grep -q $owncloud_ver /usr/local/lib/owncloud/version.php; then
# Stop php-fpm if running. If theyre not running (which happens on a previously failed install), dont bail.
# Stop php-fpm if running. If they're not running (which happens on a previously failed install), don't bail.
service php7.0-fpm stop &> /dev/null || /bin/true
service php5-fpm stop &> /dev/null || /bin/true
# Backup the existing ownCloud/Nextcloud.
# Create a backup directory to store the current installation and database to
BACKUP_DIRECTORY=$STORAGE_ROOT/owncloud-backup/`date +"%Y-%m-%d-%T"`
BACKUP_DIRECTORY=$STORAGE_ROOT/owncloud-backup/$(date +"%Y-%m-%d-%T")
mkdir -p "$BACKUP_DIRECTORY"
if [ -d /usr/local/lib/owncloud/ ]; then
echo "upgrading ownCloud/Nextcloud to $owncloud_flavor $owncloud_ver (backing up existing installation, configuration and database to directory to $BACKUP_DIRECTORY..."
cp -r /usr/local/lib/owncloud "$BACKUP_DIRECTORY/owncloud-install"
fi
if [ -e /home/user-data/owncloud/owncloud.db ]; then
cp /home/user-data/owncloud/owncloud.db $BACKUP_DIRECTORY
cp /home/user-data/owncloud/owncloud.db "$BACKUP_DIRECTORY"
fi
if [ -e /home/user-data/owncloud/config.php ]; then
cp /home/user-data/owncloud/config.php $BACKUP_DIRECTORY
cp /home/user-data/owncloud/config.php "$BACKUP_DIRECTORY"
fi
# We only need to check if we do upgrades when owncloud/Nextcloud was previously installed
# We only need to check if we do upgrades when ownCloud/Nextcloud was previously installed
if [ -e /usr/local/lib/owncloud/version.php ]; then
if grep -q "OC_VersionString = '8\.1\.[0-9]" /usr/local/lib/owncloud/version.php; then
echo "We are running 8.1.x, upgrading to 8.2.11 first"
InstallOwncloud 8.2.11 e4794938fc2f15a095018ba9d6ee18b53f6f299c
fi
# If we are upgrading from 8.2.x we should go to 9.0 first. Owncloud doesn't support skipping minor versions
# If we are upgrading from 8.2.x we should go to 9.0 first. ownCloud doesn't support skipping minor versions
if grep -q "OC_VersionString = '8\.2\.[0-9]" /usr/local/lib/owncloud/version.php; then
echo "We are running version 8.2.x, upgrading to 9.0.11 first"
# We need to disable memcached. The upgrade and install fails
# with memcached
CONFIG_TEMP=$(/bin/mktemp)
php <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php;
php <<EOF > "$CONFIG_TEMP" && mv "$CONFIG_TEMP" "$STORAGE_ROOT/owncloud/config.php";
<?php
include("$STORAGE_ROOT/owncloud/config.php");
@ -205,19 +209,19 @@ if [ ! -d /usr/local/lib/owncloud/ ] \
echo ";";
?>
EOF
chown www-data.www-data $STORAGE_ROOT/owncloud/config.php
chown www-data.www-data "$STORAGE_ROOT/owncloud/config.php"
# We can now install owncloud 9.0.11
# We can now install ownCloud 9.0.11
InstallOwncloud 9.0.11 fc8bad8a62179089bc58c406b28997fb0329337b
# The owncloud 9 migration doesn't migrate calendars and contacts
# The ownCloud 9 migration doesn't migrate calendars and contacts
# The option to migrate these are removed in 9.1
# So the migrations should be done when we have 9.0 installed
sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:migrate-addressbooks
# The following migration has to be done for each owncloud user
for directory in $STORAGE_ROOT/owncloud/*@*/ ; do
# The following migration has to be done for each ownCloud user
for directory in "$STORAGE_ROOT"/owncloud/*@*/ ; do
username=$(basename "${directory}")
sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:migrate-calendar $username
sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:migrate-calendar "$username"
done
sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:sync-birthday-calendar
fi
@ -260,13 +264,13 @@ fi
# Setup Nextcloud if the Nextcloud database does not yet exist. Running setup when
# the database does exist wipes the database and user data.
if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then
if [ ! -f "$STORAGE_ROOT/owncloud/owncloud.db" ]; then
# Create user data directory
mkdir -p $STORAGE_ROOT/owncloud
mkdir -p "$STORAGE_ROOT/owncloud"
# Create an initial configuration file.
instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1)
cat > $STORAGE_ROOT/owncloud/config.php <<EOF;
instanceid=oc$(echo "$PRIMARY_HOSTNAME" | sha1sum | fold -w 10 | head -n 1)
cat > "$STORAGE_ROOT/owncloud/config.php" <<EOF;
<?php
\$CONFIG = array (
'datadirectory' => '$STORAGE_ROOT/owncloud',
@ -317,7 +321,7 @@ EOF
EOF
# Set permissions
chown -R www-data.www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud
chown -R www-data.www-data "$STORAGE_ROOT/owncloud" /usr/local/lib/owncloud
# Execute Nextcloud's setup step, which creates the Nextcloud sqlite database.
# It also wipes it if it exists. And it updates config.php with database
@ -330,15 +334,15 @@ fi
# so set it here. It also can change if the box's PRIMARY_HOSTNAME changes, so
# this will make sure it has the right value.
# * Some settings weren't included in previous versions of Mail-in-a-Box.
# * We need to set the timezone to the system timezone to allow fail2ban to ban
# * We need to set the timezone to the system timezone to allow Fail2Ban to ban
# users within the proper timeframe
# * We need to set the logdateformat to something that will work correctly with fail2ban
# * We need to set the logdateformat to something that will work correctly with Fail2Ban
# * mail_domain' needs to be set every time we run the setup. Making sure we are setting
# the correct domain name if the domain is being change from the previous setup.
# Use PHP to read the settings file, modify it, and write out the new settings array.
TIMEZONE=$(cat /etc/timezone)
CONFIG_TEMP=$(/bin/mktemp)
php <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php;
php <<EOF > "$CONFIG_TEMP" && mv "$CONFIG_TEMP" "$STORAGE_ROOT/owncloud/config.php";
<?php
include("$STORAGE_ROOT/owncloud/config.php");
@ -358,7 +362,7 @@ var_export(\$CONFIG);
echo ";";
?>
EOF
chown www-data.www-data $STORAGE_ROOT/owncloud/config.php
chown www-data.www-data "$STORAGE_ROOT/owncloud/config.php"
# Enable/disable apps. Note that this must be done after the Nextcloud setup.
# The firstrunwizard gave Josh all sorts of problems, so disabling that.
@ -373,7 +377,8 @@ hide_output sudo -u www-data php /usr/local/lib/owncloud/console.php app:enable
# the first upgrade at the top won't work because apps may be disabled during upgrade?
# Check for success (0=ok, 3=no upgrade needed).
sudo -u www-data php /usr/local/lib/owncloud/occ upgrade
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi
E=$?
if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi
# Set PHP FPM values to support large file uploads
# (semicolon is the comment character in this file, hashes produce deprecation warnings)

View File

@ -1,20 +1,38 @@
# Are we running as root?
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root. Please re-run like this:"
echo
echo "sudo $0"
echo
exit
echo "This script must be run as root. Please re-run like this:" >&2
echo >&2
echo "sudo $0" >&2
echo >&2
exit 1
fi
# Check if on Linux
if ! echo "$OSTYPE" | grep -iq "linux"; then
echo "Error: This script must be run on Linux." >&2
exit 1
fi
. /etc/os-release
# Check that we are running on Ubuntu 14.04 LTS (or 14.04.xx).
if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" != "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
if ! echo "$ID" | grep -iq "ubuntu" || ! echo "$VERSION_ID" | grep -iq "14.04"; then
if echo "$ID" | grep -iq "ubuntu" && echo "$VERSION_ID" | grep -iq "18.04"; then
echo "Ubuntu 18.04 is not yet fully supported, but is available for testing at: https://github.com/mail-in-a-box/mailinabox/tree/ubuntu_bionic" >&2
exit 1
else
echo "Mail-in-a-Box only supports being installed on Ubuntu 14.04, sorry. You are running:" >&2
echo >&2
echo "${PRETTY_NAME:-$ID-$VERSION_ID}" >&2
echo >&2
echo "We can't write scripts that run on every possible setup, sorry." >&2
exit 1
fi
fi
# Check for the Windows Subsystem for Linux (WSL)
if uname -r | grep -iq "microsoft"; then
echo "Warning: The Windows Subsystem for Linux (WSL) is not yet fully supported by this script."
fi
# Check that we have enough memory.
@ -25,32 +43,38 @@ fi
# We will display a warning if the memory is below 768 MB which is 750000 kibibytes
#
# Skip the check if we appear to be running inside of Vagrant, because that's really just for testing.
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}')
if [ $TOTAL_PHYSICAL_MEM -lt 500000 ]; then
if [ ! -d /vagrant ]; then
TOTAL_PHYSICAL_MEM=$(expr \( \( $TOTAL_PHYSICAL_MEM \* 1024 \) / 1000 \) / 1000)
echo "Your Mail-in-a-Box needs more memory (RAM) to function properly."
echo "Please provision a machine with at least 512 MB, 1 GB recommended."
echo "This machine has $TOTAL_PHYSICAL_MEM MB memory."
exit
TOTAL_PHYSICAL_MEM=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
TOTAL_SWAP=$(awk '/^SwapTotal:/ {print $2}' /proc/meminfo)
if [ "$TOTAL_PHYSICAL_MEM" -lt 500000 ]; then
if [ ! -d /vagrant ]; then
echo "Your Mail-in-a-Box needs more memory (RAM) to function properly." >&2
echo "Please provision a machine with at least 512 MB, 1 GB (1024 MB) recommended." >&2
echo "This machine has $(printf "%'d" $((((TOTAL_PHYSICAL_MEM * 1024) / 1000) / 1000))) MB ($(printf "%'d" $((TOTAL_PHYSICAL_MEM / 1024))) MiB) memory."
exit 1
fi
fi
fi
if [ $TOTAL_PHYSICAL_MEM -lt 750000 ]; then
if [ "$TOTAL_PHYSICAL_MEM" -lt 750000 ]; then
echo "WARNING: Your Mail-in-a-Box has less than 768 MB of memory."
echo " It might run unreliably when under heavy load."
fi
# Check connectivity
if ! ping -q -c 3 mailinabox.email > /dev/null 2>&1; then
echo "Error: Could not reach mailinabox.email, please check your internet connection and run this script again." >&2
exit 1
fi
# Check that tempfs is mounted with exec
MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts)
if [ -n "$MOUNTED_TMP_AS_NO_EXEC" ]; then
echo "Mail-in-a-Box has to have exec rights on /tmp, please mount /tmp with exec"
exit
echo "Mail-in-a-Box has to have exec rights on /tmp, please mount /tmp with exec" >&2
exit 1
fi
# Check that no .wgetrc exists
if [ -e ~/.wgetrc ]; then
echo "Mail-in-a-Box expects no overrides to wget defaults, ~/.wgetrc exists"
exit
echo "Mail-in-a-Box expects no overrides to wget defaults, ~/.wgetrc exists" >&2
exit 1
fi
# Check that we are running on x86_64 or i686, any other architecture is unsupported and
@ -58,11 +82,11 @@ fi
#
# Set ARM=1 to ignore this check if you have built the packages yourself. If you do this
# you are on your own!
ARCHITECTURE=$(uname -m)
if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "i686" ]; then
if [ -z "$ARM" ]; then
echo "Mail-in-a-Box only supports x86_64 or i686 and will not work on any other architecture, like ARM."
echo "Your architecture is $ARCHITECTURE"
exit
fi
ARCHITECTURE=$(getconf LONG_BIT)
if [ "$HOSTTYPE" != "x86_64" ] && [ "$HOSTTYPE" != "i686" ]; then
if [ -z "$ARM" ]; then
echo "Mail-in-a-Box only supports x86_64 or i686 and will not work on any other architecture, like ARM." >&2
echo "Your architecture is $HOSTTYPE ($ARCHITECTURE-bit)"
exit 1
fi
fi

View File

@ -7,8 +7,8 @@ if [ -z "$NONINTERACTIVE" ]; then
#
# Also install dependencies needed to validate the email address.
if [ ! -f /usr/bin/dialog ] || [ ! -f /usr/bin/python3 ] || [ ! -f /usr/bin/pip3 ]; then
echo Installing packages needed for setup...
apt-get -q -q update
echo "Installing packages needed for setup..."
hide_output apt-get update
apt_get_quiet install dialog python3 python3-pip || exit 1
fi
@ -31,7 +31,7 @@ if [ -z "$PRIMARY_HOSTNAME" ]; then
# domain the user possibly wants to use is example.com then.
# We strip the string "box." from the hostname to get the mail
# domain. If the hostname differs, nothing happens here.
DEFAULT_DOMAIN_GUESS=$(echo $(get_default_hostname) | sed -e 's/^box\.//')
DEFAULT_DOMAIN_GUESS=$(get_default_hostname | sed -e 's/^box\.//')
# This is the first run. Ask the user for his email address so we can
# provide the best default for the box's hostname.
@ -49,23 +49,23 @@ you really want.
if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel
exit
exit 1
fi
while ! python3 management/mailconfig.py validate-email "$EMAIL_ADDR"
do
input_box "Your Email Address" \
"That's not a valid email address.\n\nWhat email address are you setting this box up to manage?" \
$EMAIL_ADDR \
"$EMAIL_ADDR" \
EMAIL_ADDR
if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel
exit
exit 1
fi
done
# Take the part after the @-sign as the user's domain name, and add
# 'box.' to the beginning to create a default hostname for this machine.
DEFAULT_PRIMARY_HOSTNAME=box.$(echo $EMAIL_ADDR | sed 's/.*@//')
DEFAULT_PRIMARY_HOSTNAME=box.$(echo "$EMAIL_ADDR" | sed 's/.*@//')
fi
input_box "Hostname" \
@ -74,12 +74,19 @@ you really want.
address, so we're suggesting $DEFAULT_PRIMARY_HOSTNAME.
\n\nYou can change it, but we recommend you don't.
\n\nHostname:" \
$DEFAULT_PRIMARY_HOSTNAME \
"$DEFAULT_PRIMARY_HOSTNAME" \
PRIMARY_HOSTNAME
RE='^.+\.localdomain$'
RE1='^.{4,253}$'
RE2='^([[:alnum:]][[:alnum:]\-]{0,61}[[:alnum:]]\.)+[a-zA-Z]{2,63}$'
if [ -z "$PRIMARY_HOSTNAME" ]; then
# user hit ESC/cancel
exit
exit 1
elif [[ $PRIMARY_HOSTNAME =~ $RE ]]; then
echo "Warning: Hostname cannot be *.localdomain."
elif ! [[ $PRIMARY_HOSTNAME =~ $RE1 && $PRIMARY_HOSTNAME =~ $RE2 ]]; then
echo "Warning: Hostname is not a valid fully qualified domain name (FQDN)."
fi
fi
@ -92,7 +99,7 @@ if [ -z "$PUBLIC_IP" ]; then
# On the first run, if we got an answer from the Internet then don't
# ask the user.
if [[ -z "$DEFAULT_PUBLIC_IP" && ! -z "$GUESSED_IP" ]]; then
if [[ -z "$DEFAULT_PUBLIC_IP" && -n "$GUESSED_IP" ]]; then
PUBLIC_IP=$GUESSED_IP
# Otherwise on the first run at least provide a default.
@ -109,12 +116,12 @@ if [ -z "$PUBLIC_IP" ]; then
input_box "Public IP Address" \
"Enter the public IP address of this machine, as given to you by your ISP.
\n\nPublic IP address:" \
$DEFAULT_PUBLIC_IP \
"$DEFAULT_PUBLIC_IP" \
PUBLIC_IP
if [ -z "$PUBLIC_IP" ]; then
# user hit ESC/cancel
exit
exit 1
fi
fi
fi
@ -125,7 +132,7 @@ if [ -z "$PUBLIC_IPV6" ]; then
# Ask the Internet.
GUESSED_IP=$(get_publicip_from_web_service 6)
MATCHED=0
if [[ -z "$DEFAULT_PUBLIC_IPV6" && ! -z "$GUESSED_IP" ]]; then
if [[ -z "$DEFAULT_PUBLIC_IPV6" && -n "$GUESSED_IP" ]]; then
PUBLIC_IPV6=$GUESSED_IP
elif [[ "$DEFAULT_PUBLIC_IPV6" == "$GUESSED_IP" ]]; then
# No IPv6 entered and machine seems to have none, or what
@ -141,12 +148,12 @@ if [ -z "$PUBLIC_IPV6" ]; then
"Enter the public IPv6 address of this machine, as given to you by your ISP.
\n\nLeave blank if the machine does not have an IPv6 address.
\n\nPublic IPv6 address:" \
$DEFAULT_PUBLIC_IPV6 \
"$DEFAULT_PUBLIC_IPV6" \
PUBLIC_IPV6
if [ ! $PUBLIC_IPV6_EXITCODE ]; then
if [ ! "$PUBLIC_IPV6_EXITCODE" ]; then
# user hit ESC/cancel
exit
exit 1
fi
fi
fi
@ -162,13 +169,13 @@ if [ -z "$PRIVATE_IPV6" ]; then
fi
if [[ -z "$PRIVATE_IP" && -z "$PRIVATE_IPV6" ]]; then
echo
echo "I could not determine the IP or IPv6 address of the network inteface"
echo "I could not determine the IP or IPv6 address of the network interface"
echo "for connecting to the Internet. Setup must stop."
echo
hostname -I
route
echo
exit
exit 1
fi
# Automatic configuration, e.g. as used in our Vagrant configuration.
@ -194,19 +201,50 @@ if [ -z "$STORAGE_ROOT" ]; then
fi
# Show the configuration, since the user may have not entered it manually.
echo
echo "Primary Hostname: $PRIMARY_HOSTNAME"
echo "Public IP Address: $PUBLIC_IP"
if [ ! -z "$PUBLIC_IPV6" ]; then
echo "Public IPv6 Address: $PUBLIC_IPV6"
echo -e "\nLinux Distribution:\t\t${PRETTY_NAME:-$ID-$VERSION_ID}"
CPU=( $(sed -n 's/^model name[[:space:]]*: *//p' /proc/cpuinfo | uniq) )
if [ -n "$CPU" ]; then
echo -e "Processor (CPU):\t\t${CPU[*]}"
fi
if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then
echo "Private IP Address: $PRIVATE_IP"
CPU_CORES=$(nproc --all)
echo -e "CPU Cores:\t\t\t$CPU_CORES"
echo -e "Architecture:\t\t\t$HOSTTYPE ($ARCHITECTURE-bit)"
echo -e "Total memory (RAM):\t\t$(printf "%'d" $((TOTAL_PHYSICAL_MEM / 1024))) MiB ($(printf "%'d" $((((TOTAL_PHYSICAL_MEM * 1024) / 1000) / 1000))) MB)"
echo -e "Total swap space:\t\t$(printf "%'d" $((TOTAL_SWAP / 1024))) MiB ($(printf "%'d" $((((TOTAL_SWAP * 1024) / 1000) / 1000))) MB)"
if command -v lspci >/dev/null; then
GPU=( $(lspci 2>/dev/null | grep -i 'vga\|3d\|2d' | sed -n 's/^.*: //p') )
fi
if [ "$PRIVATE_IPV6" != "$PUBLIC_IPV6" ]; then
echo "Private IPv6 Address: $PRIVATE_IPV6"
if [ -n "$GPU" ]; then
echo -e "Graphics Processor (GPU):\t${GPU[*]}"
fi
if [ -f /usr/bin/git ] && [ -d .git ]; then
echo "Mail-in-a-Box Version: " $(git describe)
echo -e "Computer name:\t\t\t$HOSTNAME"
echo -e "Primary Hostname:\t\t$PRIMARY_HOSTNAME"
if [ -n "$PUBLIC_IPV6" ]; then
echo -e "Public IPv4 Address:\t\t$PUBLIC_IP"
echo -e "Public IPv6 Address:\t\t$PUBLIC_IPV6"
else
echo -e "Public IP Address:\t\t$PUBLIC_IP"
fi
if [ -n "$PRIVATE_IPV6" ]; then
if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then
echo -e "Private IPv4 Address:\t\t$PRIVATE_IP"
fi
if [ "$PRIVATE_IPV6" != "$PUBLIC_IPV6" ]; then
echo -e "Private IPv6 Address:\t\t$PRIVATE_IPV6"
fi
else
if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then
echo -e "Private IP Address:\t\t$PRIVATE_IP"
fi
fi
TIME_ZONE=$(timedatectl 2>/dev/null | grep -i 'time zone\|timezone' | sed -n 's/^.*: //p')
echo -e "Time zone:\t\t\t$TIME_ZONE\n"
if command -v systemd-detect-virt >/dev/null && CONTAINER=$(systemd-detect-virt -c); then
echo -e "Virtualization container:\t$CONTAINER\n"
fi
if command -v systemd-detect-virt >/dev/null && VM=$(systemd-detect-virt -v); then
echo -e "Virtual Machine (VM) hypervisor:$VM\n"
fi
if command -v git >/dev/null && [ -d .git ]; then
echo -e "Mail-in-a-Box Version:\t\t$(git describe)\n"
fi
echo

View File

@ -1,9 +1,9 @@
#!/bin/bash
# Spam filtering with spamassassin via spampd
# 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
# 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
@ -16,13 +16,13 @@ source setup/functions.sh # load our functions
# ----------------------------------------
# Install packages.
# libmail-dkim-perl is needed to make the spamassassin DKIM module work.
# libmail-dkim-perl is needed to make the SpamAssassin DKIM module work.
# For more information see Debian Bug #689414:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=689414
echo "Installing SpamAssassin..."
apt_install spampd razor pyzor dovecot-antispam libmail-dkim-perl
# Allow spamassassin to download new rules.
# Allow SpamAssassin to download new rules.
tools/editconf.py /etc/default/spamassassin \
CRON=1
@ -41,26 +41,26 @@ echo "public.pyzor.org:24441" > /etc/spamassassin/pyzor/servers
# check with: pyzor --homedir /etc/mail/spamassassin/pyzor ping
# Configure spampd:
# * Pass messages on to docevot on port 10026. This is actually the default setting but we don't
# * Pass messages on to Dovecot on port 10026. This is actually the default setting but we don't
# want to lose track of it. (We've configured Dovecot to listen on this port elsewhere.)
# * Increase the maximum message size of scanned messages from the default of 64KB to 500KB, which
# is Spamassassin (spamc)'s own default. Specified in KBytes.
# is SpamAssassin (spamc)'s own default. Specified in KBytes.
# * Disable localmode so Pyzor, DKIM and DNS checks can be used.
tools/editconf.py /etc/default/spampd \
DESTPORT=10026 \
ADDOPTS="\"--maxsize=2000\"" \
LOCALONLY=0
# Spamassassin normally wraps spam as an attachment inside a fresh
# SpamAssassin normally wraps spam as an attachment inside a fresh
# email with a report about the message. This also protects the user
# from accidentally openening a message with embedded malware.
# from accidentally opening a message with embedded malware.
#
# It's nice to see what rules caused the message to be marked as spam,
# but it's also annoying to get to the original message when it is an
# attachment, modern mail clients are safer now and don't load remote
# content or execute scripts, and it is probably confusing to most users.
#
# Tell Spamassassin not to modify the original message except for adding
# Tell SpamAssassin not to modify the original message except for adding
# the X-Spam-Status & X-Spam-Score mail headers and related headers.
tools/editconf.py /etc/spamassassin/local.cf -s \
report_safe=0 \
@ -70,26 +70,26 @@ tools/editconf.py /etc/spamassassin/local.cf -s \
# Bayesean learning
# -----------------
#
# Spamassassin can learn from mail marked as spam or ham, but it needs to be
# SpamAssassin can learn from mail marked as spam or ham, but it needs to be
# configured. We'll store the learning data in our storage area.
#
# These files must be:
#
# * Writable by sa-learn-pipe script below, which run as the 'mail' user, for manual tagging of mail as spam/ham.
# * Writeable by sa-learn-pipe script below, which run as the 'mail' user, for manual tagging of mail as spam/ham.
# * Readable by the spampd process ('spampd' user) during mail filtering.
# * Writable by the debian-spamd user, which runs /etc/cron.daily/spamassassin.
# * Writeable by the debian-spamd user, which runs /etc/cron.daily/spamassassin.
#
# We'll have these files owned by spampd and grant access to the other two processes.
#
# Spamassassin will change the access rights back to the defaults, so we must also configure
# SpamAssassin will change the access rights back to the defaults, so we must also configure
# the filemode in the config file.
tools/editconf.py /etc/spamassassin/local.cf -s \
bayes_path=$STORAGE_ROOT/mail/spamassassin/bayes \
bayes_path="$STORAGE_ROOT/mail/spamassassin/bayes" \
bayes_file_mode=0666
mkdir -p $STORAGE_ROOT/mail/spamassassin
chown -R spampd:spampd $STORAGE_ROOT/mail/spamassassin
mkdir -p "$STORAGE_ROOT/mail/spamassassin"
chown -R spampd:spampd "$STORAGE_ROOT/mail/spamassassin"
# To mark mail as spam or ham, just drag it in or out of the Spam folder. We'll
# use the Dovecot antispam plugin to detect the message move operation and execute
@ -134,8 +134,8 @@ chmod a+x /usr/local/bin/sa-learn-pipe.sh
# Create empty bayes training data (if it doesn't exist). Once the files exist,
# ensure they are group-writable so that the Dovecot process has access.
sudo -u spampd /usr/bin/sa-learn --sync 2>/dev/null
chmod -R 660 $STORAGE_ROOT/mail/spamassassin
chmod 770 $STORAGE_ROOT/mail/spamassassin
chmod -R 660 "$STORAGE_ROOT/mail/spamassassin"
chmod 770 "$STORAGE_ROOT/mail/spamassassin"
# Initial training?
# sa-learn --ham storage/mail/mailboxes/*/*/cur/

View File

@ -26,9 +26,9 @@ source /etc/mailinabox.conf # load global vars
# Show a status line if we are going to take any action in this file.
if [ ! -f /usr/bin/openssl ] \
|| [ ! -f $STORAGE_ROOT/ssl/ssl_private_key.pem ] \
|| [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ] \
|| [ ! -f $STORAGE_ROOT/ssl/dh2048.pem ]; then
|| [ ! -f "$STORAGE_ROOT/ssl/ssl_private_key.pem" ] \
|| [ ! -f "$STORAGE_ROOT/ssl/ssl_certificate.pem" ] \
|| [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then
echo "Creating initial SSL certificate and perfect forward secrecy Diffie-Hellman parameters..."
fi
@ -38,7 +38,7 @@ apt_install openssl
# Create a directory to store TLS-related things like "SSL" certificates.
mkdir -p $STORAGE_ROOT/ssl
mkdir -p "$STORAGE_ROOT/ssl"
# Generate a new private key.
#
@ -60,39 +60,39 @@ mkdir -p $STORAGE_ROOT/ssl
#
# Since we properly seed /dev/urandom in system.sh we should be fine, but I leave
# in the rest of the notes in case that ever changes.
if [ ! -f $STORAGE_ROOT/ssl/ssl_private_key.pem ]; then
if [ ! -f "$STORAGE_ROOT/ssl/ssl_private_key.pem" ]; then
# Set the umask so the key file is never world-readable.
(umask 077; hide_output \
openssl genrsa -out $STORAGE_ROOT/ssl/ssl_private_key.pem 2048)
openssl genrsa -out "$STORAGE_ROOT/ssl/ssl_private_key.pem" 2048)
fi
# Generate a self-signed SSL certificate because things like nginx, dovecot,
# etc. won't even start without some certificate in place, and we need nginx
# so we can offer the user a control panel to install a better certificate.
if [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ]; then
if [ ! -f "$STORAGE_ROOT/ssl/ssl_certificate.pem" ]; then
# Generate a certificate signing request.
CSR=/tmp/ssl_cert_sign_req-$$.csr
hide_output \
openssl req -new -key $STORAGE_ROOT/ssl/ssl_private_key.pem -out $CSR \
openssl req -new -key "$STORAGE_ROOT/ssl/ssl_private_key.pem" -out $CSR \
-sha256 -subj "/CN=$PRIMARY_HOSTNAME"
# Generate the self-signed certificate.
CERT=$STORAGE_ROOT/ssl/$PRIMARY_HOSTNAME-selfsigned-$(date --rfc-3339=date | sed s/-//g).pem
hide_output \
openssl x509 -req -days 365 \
-in $CSR -signkey $STORAGE_ROOT/ssl/ssl_private_key.pem -out $CERT
-in $CSR -signkey "$STORAGE_ROOT/ssl/ssl_private_key.pem" -out "$CERT"
# Delete the certificate signing request because it has no other purpose.
rm -f $CSR
# Symlink the certificate into the system certificate path, so system services
# can find it.
ln -s $CERT $STORAGE_ROOT/ssl/ssl_certificate.pem
ln -s "$CERT" "$STORAGE_ROOT/ssl/ssl_certificate.pem"
fi
# Generate some Diffie-Hellman cipher bits.
# openssl's default bit length for this is 1024 bits, but we'll create
# 2048 bits of bits per the latest recommendations.
if [ ! -f $STORAGE_ROOT/ssl/dh2048.pem ]; then
openssl dhparam -out $STORAGE_ROOT/ssl/dh2048.pem 2048
if [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then
openssl dhparam -out "$STORAGE_ROOT/ssl/dh2048.pem" 2048
fi

View File

@ -2,6 +2,11 @@
# This is the entry point for configuring the system.
#####################################################
if [[ "$#" -ne 0 ]]; then
echo "Usage: sudo $0" >&2
exit 1
fi
source setup/functions.sh # load our functions
# Check system setup: Are we running as root on Ubuntu 14.04 on a
@ -35,7 +40,8 @@ if [ -f /etc/mailinabox.conf ]; then
# Load the old .conf file to get existing configuration options loaded
# into variables with a DEFAULT_ prefix.
cat /etc/mailinabox.conf | sed s/^/DEFAULT_/ > /tmp/mailinabox.prev.conf
cp /etc/mailinabox.conf /tmp/mailinabox.prev.conf
sed -i 's/^/DEFAULT_/' /tmp/mailinabox.prev.conf
source /tmp/mailinabox.prev.conf
rm -f /tmp/mailinabox.prev.conf
else
@ -46,7 +52,7 @@ fi
# in the first dialog prompt, so we should do this before that starts.
cat > /usr/local/bin/mailinabox << EOF;
#!/bin/bash
cd `pwd`
cd $(pwd)
source setup/start.sh
EOF
chmod +x /usr/local/bin/mailinabox
@ -61,9 +67,9 @@ source setup/questions.sh
# Skip on existing installs since we don't want this to block the ability to
# upgrade, and these checks are also in the control panel status checks.
if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then
if [ -z "$SKIP_NETWORK_CHECKS" ]; then
source setup/network-checks.sh
fi
if [ -z "$SKIP_NETWORK_CHECKS" ]; then
source setup/network-checks.sh
fi
fi
# Create the STORAGE_USER and STORAGE_ROOT directory if they don't already exist.
@ -71,15 +77,15 @@ fi
# migration (schema) number for the files stored there, assume this is a fresh
# installation to that directory and write the file to contain the current
# migration number for this version of Mail-in-a-Box.
if ! id -u $STORAGE_USER >/dev/null 2>&1; then
useradd -m $STORAGE_USER
if ! id -u "$STORAGE_USER" >/dev/null 2>&1; then
useradd -m "$STORAGE_USER"
fi
if [ ! -d $STORAGE_ROOT ]; then
mkdir -p $STORAGE_ROOT
if [ ! -d "$STORAGE_ROOT" ]; then
mkdir -p "$STORAGE_ROOT"
fi
if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then
echo $(setup/migrate.py --current) > $STORAGE_ROOT/mailinabox.version
chown $STORAGE_USER.$STORAGE_USER $STORAGE_ROOT/mailinabox.version
if [ ! -f "$STORAGE_ROOT/mailinabox.version" ]; then
setup/migrate.py --current > "$STORAGE_ROOT/mailinabox.version"
chown "$STORAGE_USER.$STORAGE_USER" "$STORAGE_ROOT/mailinabox.version"
fi
@ -114,7 +120,7 @@ source setup/munin.sh
# Wait for the management daemon to start...
until nc -z -w 4 127.0.0.1 10222
do
echo Waiting for the Mail-in-a-Box management daemon to start...
echo "Waiting for the Mail-in-a-Box management daemon to start..."
sleep 2
done
@ -123,8 +129,8 @@ done
tools/dns_update
tools/web_update
# Give fail2ban another restart. The log files may not all have been present when
# fail2ban was first configured, but they should exist now.
# Give Fail2Ban another restart. The log files may not all have been present when
# Fail2Ban was first configured, but they should exist now.
restart_service fail2ban
# If there aren't any mail users yet, create one.
@ -134,41 +140,41 @@ source setup/firstuser.sh
# We'd let certbot ask the user interactively, but when this script is
# run in the recommended curl-pipe-to-bash method there is no TTY and
# certbot will fail if it tries to ask.
if [ ! -d $STORAGE_ROOT/ssl/lets_encrypt/accounts/acme-v02.api.letsencrypt.org/ ]; then
echo
echo "-----------------------------------------------"
echo "Mail-in-a-Box uses Let's Encrypt to provision free SSL/TLS certificates"
echo "to enable HTTPS connections to your box. We're automatically"
echo "agreeing you to their subscriber agreement. See https://letsencrypt.org."
echo
certbot register --register-unsafely-without-email --agree-tos --config-dir $STORAGE_ROOT/ssl/lets_encrypt
if [ ! -d "$STORAGE_ROOT/ssl/lets_encrypt/accounts/acme-v02.api.letsencrypt.org/" ]; then
echo
echo "-----------------------------------------------"
echo "Mail-in-a-Box uses Let's Encrypt to provision free SSL/TLS certificates"
echo "to enable HTTPS connections to your box. We're automatically"
echo "agreeing you to their subscriber agreement. See https://letsencrypt.org."
echo
certbot register -n --register-unsafely-without-email --agree-tos --config-dir "$STORAGE_ROOT/ssl/lets_encrypt"
fi
# Done.
echo
echo "-----------------------------------------------"
echo
echo Your Mail-in-a-Box is running.
echo "Your Mail-in-a-Box is running."
echo
echo Please log in to the control panel for further instructions at:
echo "Please log in to the control panel for further instructions at:"
echo
if management/status_checks.py --check-primary-hostname; then
# Show the nice URL if it appears to be resolving and has a valid certificate.
echo https://$PRIMARY_HOSTNAME/admin
echo "https://$PRIMARY_HOSTNAME/admin"
echo
echo "If you have a DNS problem put the box's IP address in the URL"
echo "(https://$PUBLIC_IP/admin) but then check the TLS fingerprint:"
openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\
openssl x509 -in "$STORAGE_ROOT/ssl/ssl_certificate.pem" -noout -fingerprint -sha256\
| sed "s/SHA256 Fingerprint=//"
else
echo https://$PUBLIC_IP/admin
echo "https://$PUBLIC_IP/admin"
echo
echo You will be alerted that the website has an invalid certificate. Check that
echo the certificate fingerprint matches:
echo "You will be alerted that the website has an invalid certificate. Check that"
echo "the certificate fingerprint matches:"
echo
openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint -sha256\
openssl x509 -in "$STORAGE_ROOT/ssl/ssl_certificate.pem" -noout -fingerprint -sha256\
| sed "s/SHA256 Fingerprint=//"
echo
echo Then you can confirm the security exception and continue.
echo "Then you can confirm the security exception and continue."
echo
fi

View File

@ -11,13 +11,12 @@ source setup/functions.sh # load our functions
#
# First set the hostname in the configuration file, then activate the setting
echo $PRIMARY_HOSTNAME > /etc/hostname
hostname $PRIMARY_HOSTNAME
hostnamectl set-hostname "$PRIMARY_HOSTNAME"
# ### Add swap space to the system
# If the physical memory of the system is below 2GB it is wise to create a
# swap file. This will make the system more resiliant to memory spikes and
# swap file. This will make the system more resilient to memory spikes and
# prevent for instance spam filtering from crashing
# We will create a 1G file, this should be a good balance between disk usage
@ -36,7 +35,7 @@ hostname $PRIMARY_HOSTNAME
# See https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04
# for reference
SWAP_MOUNTED=$(cat /proc/swaps | tail -n+2)
SWAP_MOUNTED=$(< /proc/swaps tail -n+2)
SWAP_IN_FSTAB=$(grep "swap" /etc/fstab)
ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts)
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}')
@ -46,14 +45,14 @@ if
[ -z "$SWAP_IN_FSTAB" ] &&
[ ! -e /swapfile ] &&
[ -z "$ROOT_IS_BTRFS" ] &&
[ $TOTAL_PHYSICAL_MEM -lt 1900000 ] &&
[ $AVAILABLE_DISK_SPACE -gt 5242880 ]
[ "$TOTAL_PHYSICAL_MEM" -lt 1900000 ] &&
[ "$AVAILABLE_DISK_SPACE" -gt 5242880 ]
then
echo "Adding a swap file to the system..."
# Allocate and activate the swap file. Allocate in 1KB chuncks
# Allocate and activate the swap file. Allocate in 1KB chunks
# doing it in one go, could fail on low memory systems
dd if=/dev/zero of=/swapfile bs=1024 count=$[1024*1024] status=none
dd if=/dev/zero of=/swapfile bs=1024 count=$((1024*1024)) status=none
if [ -e /swapfile ]; then
chmod 600 /swapfile
hide_output mkswap /swapfile
@ -95,9 +94,9 @@ hide_output add-apt-repository -y ppa:certbot/certbot
# of things from Ubuntu, as well as the directory of packages provide by the
# PPAs so we can install those packages later.
echo Updating system packages...
echo "Updating system packages..."
hide_output apt-get update
apt_get_quiet upgrade
apt_get_quiet dist-upgrade
# Old kernels pile up over time and take up a lot of disk space, and because of Mail-in-a-Box
# changes there may be other packages that are no longer needed. Clear out anything apt knows
@ -117,12 +116,12 @@ apt_get_quiet autoremove
# * ntp: keeps the system time correct
# * fail2ban: scans log files for repeated failed login attempts and blocks the remote IP at the firewall
# * netcat-openbsd: `nc` command line networking tool
# * git: we install some things directly from github
# * git: we install some things directly from GitHub
# * sudo: allows privileged users to execute commands as root without being root
# * coreutils: includes `nproc` tool to report number of processors, mktemp
# * bc: allows us to do math to compute sane defaults
echo Installing system packages...
echo "Installing system packages..."
apt_install python3 python3-dev python3-pip \
netcat-openbsd wget curl git sudo coreutils bc \
haveged pollinate unzip \
@ -157,13 +156,13 @@ fi
# things (i.e. late at night in whatever timezone the user actually lives
# in).
#
# However, changing the timezone once it is set seems to confuse fail2ban
# and requires restarting fail2ban (done below in the fail2ban
# However, changing the timezone once it is set seems to confuse Fail2Ban
# and requires restarting Fail2Ban (done below in the Fail2Ban
# section) and syslog (see #328). There might be other issues, and it's
# not likely the user will want to change this, so we only ask on first
# setup.
if [ -z "$NONINTERACTIVE" ]; then
if [ ! -f /etc/timezone ] || [ ! -z $FIRST_TIME_SETUP ]; then
if [ ! -f /etc/timezone ] || [ -n "$FIRST_TIME_SETUP" ]; then
# If the file is missing or this is the user's first time running
# Mail-in-a-Box setup, run the interactive timezone configuration
# tool.
@ -226,7 +225,7 @@ fi
# hardware entropy to get going, by drawing from /dev/random. haveged makes this
# less likely to stall for very long.
echo Initializing system random number generator...
echo "Initializing system random number generator..."
dd if=/dev/random of=/dev/urandom bs=1 count=32 2> /dev/null
# This is supposedly sufficient. But because we're not sure if hardware entropy
@ -237,7 +236,7 @@ pollinate -q -r
# Between these two, we really ought to be all set.
# We need an ssh key to store backups via rsync, if it doesn't exist create one
# We need an SSH key to store backups via rsync, if it doesn't exist create one
if [ ! -f /root/.ssh/id_rsa_miab ]; then
echo 'Creating SSH key for backup…'
ssh-keygen -t rsa -b 2048 -a 100 -f /root/.ssh/id_rsa_miab -N '' -q
@ -270,11 +269,11 @@ if [ -z "$DISABLE_FIREWALL" ]; then
# settings, find the port it is supposedly running on, and open that port #NODOC
# too. #NODOC
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC
if [ ! -z "$SSH_PORT" ]; then
if [ -n "$SSH_PORT" ]; then
if [ "$SSH_PORT" != "22" ]; then
echo Opening alternate SSH port $SSH_PORT. #NODOC
ufw_allow $SSH_PORT #NODOC
echo "Opening alternate SSH port $SSH_PORT." #NODOC
ufw_allow "$SSH_PORT" #NODOC
fi
fi
@ -328,15 +327,14 @@ restart_service resolvconf
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix, ssh, etc.
rm -f /etc/fail2ban/jail.local # we used to use this file but don't anymore
cat conf/fail2ban/jails.conf \
| sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
< conf/fail2ban/jails.conf sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
| sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \
> /etc/fail2ban/jail.d/mailinabox.conf
cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/
# On first installation, the log files that the jails look at don't all exist.
# e.g., The roundcube error log isn't normally created until someone logs into
# Roundcube for the first time. This causes fail2ban to fail to start. Later
# scripts will ensure the files exist and then fail2ban is given another
# e.g., The Roundcube error log isn't normally created until someone logs into
# Roundcube for the first time. This causes Fail2Ban to fail to start. Later
# scripts will ensure the files exist and then Fail2Ban is given another
# restart at the very end of setup.
restart_service fail2ban

View File

@ -8,7 +8,7 @@ source /etc/mailinabox.conf # load global vars
# Some Ubuntu images start off with Apache. Remove it since we
# will use nginx. Use autoremove to remove any Apache depenencies.
if [ -f /usr/sbin/apache2 ]; then
echo Removing apache...
echo "Removing Apache..."
hide_output apt-get -y purge apache2 apache2-*
hide_output apt-get -y --purge autoremove
fi
@ -17,7 +17,7 @@ fi
#
# Turn off nginx's default website.
echo "Installing Nginx (web server)..."
echo "Installing nginx (web server)..."
apt_install nginx php7.0-cli php7.0-fpm
@ -67,8 +67,7 @@ tools/editconf.py /etc/php/7.0/fpm/pool.d/www.conf -c ';' \
# nginx configuration at /mailinabox-mobileconfig.
mkdir -p /var/lib/mailinabox
chmod a+rx /var/lib/mailinabox
cat conf/ios-profile.xml \
| sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
< conf/ios-profile.xml sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
| sed "s/UUID1/$(cat /proc/sys/kernel/random/uuid)/" \
| sed "s/UUID2/$(cat /proc/sys/kernel/random/uuid)/" \
| sed "s/UUID3/$(cat /proc/sys/kernel/random/uuid)/" \
@ -81,18 +80,17 @@ chmod a+r /var/lib/mailinabox/mobileconfig.xml
# The format of the file is documented at:
# https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
# and https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo.
cat conf/mozilla-autoconfig.xml \
| sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
< conf/mozilla-autoconfig.xml sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
> /var/lib/mailinabox/mozilla-autoconfig.xml
chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml
# make a default homepage
if [ -d $STORAGE_ROOT/www/static ]; then mv $STORAGE_ROOT/www/static $STORAGE_ROOT/www/default; fi # migration #NODOC
mkdir -p $STORAGE_ROOT/www/default
if [ ! -f $STORAGE_ROOT/www/default/index.html ]; then
cp conf/www_default.html $STORAGE_ROOT/www/default/index.html
if [ -d "$STORAGE_ROOT/www/static" ]; then mv "$STORAGE_ROOT/www/static" "$STORAGE_ROOT/www/default"; fi # migration #NODOC
mkdir -p "$STORAGE_ROOT/www/default"
if [ ! -f "$STORAGE_ROOT/www/default/index.html" ]; then
cp conf/www_default.html "$STORAGE_ROOT/www/default/index.html"
fi
chown -R $STORAGE_USER $STORAGE_ROOT/www
chown -R "$STORAGE_USER" "$STORAGE_ROOT/www"
# We previously installed a custom init script to start the PHP FastCGI daemon. #NODOC
# Remove it now that we're using php5-fpm. #NODOC

View File

@ -15,8 +15,8 @@ source /etc/mailinabox.conf # load global vars
#
# 3. It's packaged incorrectly --- it seems to be missing a directory of files.
#
# So we'll use apt-get to manually install the dependencies of roundcube that we know we need,
# and then we'll manually install roundcube from source.
# So we'll use apt-get to manually install the dependencies of Roundcube that we know we need,
# and then we'll manually install Roundcube from source.
# These dependencies are from `apt-cache showpkg roundcube-core`.
echo "Installing Roundcube (webmail)..."
@ -53,7 +53,7 @@ needs_update=0 #NODOC
if [ ! -f /usr/local/lib/roundcubemail/version ]; then
# not installed yet #NODOC
needs_update=1 #NODOC
elif [[ "$UPDATE_KEY" != `cat /usr/local/lib/roundcubemail/version` ]]; then
elif [[ "$UPDATE_KEY" != $(cat /usr/local/lib/roundcubemail/version) ]]; then
# checks if the version is what we want
needs_update=1 #NODOC
fi
@ -68,10 +68,10 @@ if [ $needs_update == 1 ]; then
mv /usr/local/lib/roundcubemail-$VERSION/ $RCM_DIR
rm -f /tmp/roundcube.tgz
# install roundcube persistent_login plugin
# install Roundcube persistent_login plugin
git_clone https://github.com/mfreiholz/Roundcube-Persistent-Login-Plugin.git $PERSISTENT_LOGIN_VERSION '' ${RCM_PLUGIN_DIR}/persistent_login
# install roundcube html5_notifier plugin
# install Roundcube html5_notifier plugin
git_clone https://github.com/kitist/html5_notifier.git $HTML5_NOTIFIER_VERSION '' ${RCM_PLUGIN_DIR}/html5_notifier
# download and verify the full release of the carddav plugin
@ -96,7 +96,7 @@ SECRET_KEY=$(dd if=/dev/urandom bs=1 count=18 2>/dev/null | base64 | fold -w 24
# Create a configuration file.
#
# For security, temp and log files are not stored in the default locations
# which are inside the roundcube sources directory. We put them instead
# which are inside the Roundcube sources directory. We put them instead
# in normal places.
cat > $RCM_CONFIG <<EOF;
<?php
@ -158,11 +158,11 @@ cat > ${RCM_PLUGIN_DIR}/carddav/config.inc.php <<EOF;
?>
EOF
# Create writable directories.
mkdir -p /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube
chown -R www-data.www-data /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube
# Create writeable directories.
mkdir -p /var/log/roundcubemail /var/tmp/roundcubemail "$STORAGE_ROOT/mail/roundcube"
chown -R www-data.www-data /var/log/roundcubemail /var/tmp/roundcubemail "$STORAGE_ROOT/mail/roundcube"
# Ensure the log file monitored by fail2ban exists, or else fail2ban can't start.
# Ensure the log file monitored by Fail2Ban exists, or else Fail2Ban can't start.
sudo -u www-data touch /var/log/roundcubemail/errors
# Password changing plugin settings
@ -184,10 +184,10 @@ 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
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"
# Fix Carddav permissions:
chown -f -R root.www-data ${RCM_PLUGIN_DIR}/carddav
@ -196,8 +196,8 @@ chmod -R 774 ${RCM_PLUGIN_DIR}/carddav
# Run Roundcube database migration script (database is created if it does not exist)
${RCM_DIR}/bin/updatedb.sh --dir ${RCM_DIR}/SQL --package roundcube
chown www-data:www-data $STORAGE_ROOT/mail/roundcube/roundcube.sqlite
chmod 664 $STORAGE_ROOT/mail/roundcube/roundcube.sqlite
chown www-data:www-data "$STORAGE_ROOT/mail/roundcube/roundcube.sqlite"
chmod 664 "$STORAGE_ROOT/mail/roundcube/roundcube.sqlite"
# Enable PHP modules.
phpenmod -v php7.0 mcrypt imap

View File

@ -27,7 +27,7 @@ TARGETHASH=104d44426852429dac8ec2783a4e9ad7752d4682
needs_update=0 #NODOC
if [ ! -f /usr/local/lib/z-push/version ]; then
needs_update=1 #NODOC
elif [[ $VERSION != `cat /usr/local/lib/z-push/version` ]]; then
elif [[ $VERSION != $(cat /usr/local/lib/z-push/version) ]]; then
# checks if the version
needs_update=1 #NODOC
fi

View File

@ -1,9 +1,9 @@
# Use this script to make an archive of the contents of all
# of the configuration files we edit with editconf.py.
for fn in `grep -hr editconf.py setup | sed "s/tools\/editconf.py //" | sed "s/ .*//" | sort | uniq`; do
echo ======================================================================
echo $fn
echo ======================================================================
cat $fn
for fn in $(grep -hr editconf.py setup | sed "s/tools\/editconf.py //" | sed "s/ .*//" | sort | uniq); do
echo "======================================================================"
echo "$fn"
echo "======================================================================"
cat "$fn"
done

View File

@ -14,15 +14,15 @@ if [ -z "$1" ]; then
echo
echo "Available backups:"
echo
find $STORAGE_ROOT/owncloud-backup/* -maxdepth 0 -type d
find "$STORAGE_ROOT/owncloud-backup/*" -maxdepth 0 -type d
echo
echo "Supply the directory that was created during the last installation as the only commandline argument"
exit
exit 1
fi
if [ ! -f $1/config.php ]; then
if [ ! -f "$1/config.php" ]; then
echo "This isn't a valid backup location"
exit
exit 1
fi
echo "Restoring backup from $1"
@ -37,12 +37,12 @@ cp -r "$1/owncloud-install" /usr/local/lib/owncloud
# restore access rights
chmod 750 /usr/local/lib/owncloud/{apps,config}
cp "$1/owncloud.db" $STORAGE_ROOT/owncloud/
cp "$1/config.php" $STORAGE_ROOT/owncloud/
cp "$1/owncloud.db" "$STORAGE_ROOT/owncloud/"
cp "$1/config.php" "$STORAGE_ROOT/owncloud/"
ln -sf $STORAGE_ROOT/owncloud/config.php /usr/local/lib/owncloud/config/config.php
chown -f -R www-data.www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud
chown www-data.www-data $STORAGE_ROOT/owncloud/config.php
ln -sf "$STORAGE_ROOT/owncloud/config.php" /usr/local/lib/owncloud/config/config.php
chown -f -R www-data.www-data "$STORAGE_ROOT/owncloud /usr/local/lib/owncloud"
chown www-data.www-data "$STORAGE_ROOT/owncloud/config.php"
sudo -u www-data php /usr/local/lib/owncloud/occ maintenance:mode --off

View File

@ -4,20 +4,20 @@
# instance running here.
#
# Run this at your own risk. This is for testing & experimentation
# purpopses only. After this point you are on your own.
# purposes only. After this point you are on your own.
source /etc/mailinabox.conf # load global vars
ADMIN=$(./mail.py user admins | head -n 1)
test -z "$1" || ADMIN=$1
echo I am going to unlock admin features for $ADMIN.
echo You can provide another user to unlock as the first argument of this script.
echo "I am going to unlock admin features for $ADMIN."
echo "You can provide another user to unlock as the first argument of this script."
echo
echo WARNING: you could break mail-in-a-box when fiddling around with Nextcloud\'s admin interface
echo If in doubt, press CTRL-C to cancel.
echo "WARNING: you could break mail-in-a-box when fiddling around with Nextcloud\'s admin interface"
echo "If in doubt, press CTRL-C to cancel."
echo
echo Press enter to continue.
echo "Press enter to continue."
read
sudo -u www-data php /usr/local/lib/owncloud/occ group:adduser admin $ADMIN && echo Done.
sudo -u www-data php /usr/local/lib/owncloud/occ group:adduser admin "$ADMIN" && echo "Done."