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. 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. 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 ## 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. 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 $ 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 ### 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 ### Making changes
@ -43,7 +45,7 @@ Once inside the VM, you can re-run individual parts of the setup like in this ex
### Tests ### 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 ## 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 Mail-in-a-Box
============= =============
@ -13,7 +15,7 @@ Our goals are to:
* Make deploying a good mail server easy. * Make deploying a good mail server easy.
* Promote [decentralization](http://redecentralize.org/), innovation, and privacy on the web. * 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 a totally unhackable, NSA-proof server.
* **Not** make something customizable by power users. * **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: 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/)) * 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](http://roundcube.net/)), static website hosting ([nginx](http://nginx.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/)) * 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 * 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](http://www.fail2ban.org/wiki/index.php/Main_Page)), system monitoring ([munin](http://munin-monitoring.org/)) * 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: It also includes:
@ -53,8 +55,7 @@ Clone this repository:
$ git clone https://github.com/mail-in-a-box/mailinabox $ git clone https://github.com/mail-in-a-box/mailinabox
$ cd mailinabox $ cd mailinabox
_Optional:_ Download my PGP key and then verify that the sources were signed _Optional:_ Download my PGP key and then verify that I signed the sources:
by me:
$ curl -s https://keybase.io/joshdata/key.asc | gpg --import $ curl -s https://keybase.io/joshdata/key.asc | gpg --import
gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported 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. 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 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) 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: 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). 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 Contributing and Development
---------------------------- ----------------------------
@ -98,8 +99,8 @@ Mail-in-a-Box is similar to [iRedMail](http://www.iredmail.org/) and [Modoboa](h
The History 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 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 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 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. * 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. * 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 # 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. # 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" management/mail_log.py -t week | management/email_administrator.py "Mail-in-a-Box Usage Report"
fi 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). * 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). * 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. * 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. * 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)) * 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). * 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). * 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: 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)) * 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 ### 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. `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. `fail2ban` only blocks IPv4 addresses, however. If the box has a public IPv6 address, it is not protected from these attacks.
Outbound Mail 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 ### 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 ### Encryption
@ -87,15 +87,15 @@ The box (along with the vast majority of mail servers) uses [opportunistic encry
### DANE ### 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
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 ### 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.) 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 ### 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: # 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 TAG=v0.28
fi 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? # Are we running as root?
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root. Did you leave out sudo?" echo "This script must be run as root. Did you leave out sudo?" >&2
exit 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 fi
# Clone the Mail-in-a-Box repository if it doesn't exist. # 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 if [ ! -f /usr/bin/git ]; then
echo Installing git . . . echo "Installing git . . ."
apt-get -q -q update apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -q -q install -y git < /dev/null DEBIAN_FRONTEND=noninteractive apt-get -yqq install git < /dev/null
echo echo
fi fi
echo Downloading Mail-in-a-Box $TAG. . . echo "Downloading Mail-in-a-Box $TAG. . ."
git clone \ git clone \
-b $TAG --depth 1 \ -b $TAG --depth 1 \
https://github.com/mail-in-a-box/mailinabox \ https://github.com/mail-in-a-box/mailinabox \
$HOME/mailinabox \ "$HOME/mailinabox" \
< /dev/null 2> /dev/null < /dev/null
echo echo
fi fi
# Change directory to it. # Change directory to it.
cd $HOME/mailinabox cd "$HOME/mailinabox"
# Update it. # Update it.
if [ "$TAG" != `git describe` ]; then if [ "$TAG" != "$(git describe)" ]; then
echo Updating Mail-in-a-Box to $TAG . . . echo "Updating Mail-in-a-Box to $TAG . . ."
git fetch --depth 1 --force --prune origin tag $TAG git fetch --depth 1 --force --prune origin tag $TAG
if ! git checkout -q $TAG; then if ! git checkout -q $TAG; then
echo "Update failed. Did you modify something in `pwd`?" echo "Update failed. Did you modify something in $(pwd)?" >&2
exit exit 1
fi fi
echo echo
fi fi

View File

@ -10,12 +10,12 @@ source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars source /etc/mailinabox.conf # load global vars
# Install DKIM... # Install DKIM...
echo Installing OpenDKIM/OpenDMARC... echo "Installing OpenDKIM/OpenDMARC..."
apt_install opendkim opendkim-tools opendmarc apt_install opendkim opendkim-tools opendmarc
# Make sure configuration directories exist. # Make sure configuration directories exist.
mkdir -p /etc/opendkim; mkdir -p /etc/opendkim;
mkdir -p $STORAGE_ROOT/mail/dkim mkdir -p "$STORAGE_ROOT/mail/dkim"
# Used in InternalHosts and ExternalIgnoreList configuration directives. # Used in InternalHosts and ExternalIgnoreList configuration directives.
# Not quite sure why. # Not quite sure why.
@ -47,12 +47,12 @@ fi
# such as Google. But they and others use a 2048 bit key, so we'll # 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. # do the same. Keys beyond 2048 bits may exceed DNS record limits.
if [ ! -f "$STORAGE_ROOT/mail/dkim/mail.private" ]; then 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 fi
# Ensure files are owned by the opendkim user and are private otherwise. # Ensure files are owned by the opendkim user and are private otherwise.
chown -R opendkim:opendkim $STORAGE_ROOT/mail/dkim chown -R opendkim:opendkim "$STORAGE_ROOT/mail/dkim"
chmod go-rwx $STORAGE_ROOT/mail/dkim chmod go-rwx "$STORAGE_ROOT/mail/dkim"
tools/editconf.py /etc/opendmarc.conf -s \ tools/editconf.py /etc/opendmarc.conf -s \
"Syslog=true" \ "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. # ldns-keygen uses /dev/random for generating random numbers by default.
# This is slow and unecessary if we ensure /dev/urandom is seeded properly, # 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. # 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 # Now create a Zone-Signing Key (ZSK) which is expected to be
# rotated more often than a KSK, although we have no plans to # rotated more often than a KSK, although we have no plans to
# rotate it (and doing so would be difficult to do without # rotate it (and doing so would be difficult to do without
# disturbing DNS availability.) Omit `-k` and use a shorter key length. # 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: # 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. # options. So we'll store the names of the files we just generated.
# We might have multiple keys down the road. This will identify # We might have multiple keys down the road. This will identify
# what keys are the current keys. # what keys are the current keys.
cat > $STORAGE_ROOT/dns/dnssec/$algo.conf << EOF; cat > "$STORAGE_ROOT/dns/dnssec/$algo.conf" << EOF;
KSK=$KSK KSK=$KSK
ZSK=$ZSK ZSK=$ZSK
EOF EOF
@ -126,7 +126,7 @@ cat > /etc/cron.daily/mailinabox-dnssec << EOF;
#!/bin/bash #!/bin/bash
# Mail-in-a-Box # Mail-in-a-Box
# Re-sign any DNS zones with DNSSEC because the signatures expire periodically. # Re-sign any DNS zones with DNSSEC because the signatures expire periodically.
`pwd`/tools/dns_update $(pwd)/tools/dns_update
EOF EOF
chmod +x /etc/cron.daily/mailinabox-dnssec chmod +x /etc/cron.daily/mailinabox-dnssec

View File

@ -1,6 +1,6 @@
# If there aren't any mail users yet, create one. # If there aren't any mail users yet, create one.
if [ -z "`tools/mail.py user`" ]; then if [ -z "$(tools/mail.py user)" ]; then
# The outut of "tools/mail.py user" is a list of mail users. If there # The output of "tools/mail.py user" is a list of mail users. If there
# aren't any yet, it'll be empty. # aren't any yet, it'll be empty.
# If we didn't ask for an email address at the start, do so now. # 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" \ input_box "Mail Account" \
"Let's create your first mail account. "Let's create your first mail account.
\n\nWhat email address do you want?" \ \n\nWhat email address do you want?" \
me@`get_default_hostname` \ "me@$(get_default_hostname)" \
EMAIL_ADDR EMAIL_ADDR
if [ -z "$EMAIL_ADDR" ]; then if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel # user hit ESC/cancel
exit exit 1
fi fi
while ! management/mailconfig.py validate-email "$EMAIL_ADDR" while ! management/mailconfig.py validate-email "$EMAIL_ADDR"
do do
input_box "Mail Account" \ input_box "Mail Account" \
"That's not a valid email address. "That's not a valid email address.
\n\nWhat email address do you want?" \ \n\nWhat email address do you want?" \
$EMAIL_ADDR \ "$EMAIL_ADDR" \
EMAIL_ADDR EMAIL_ADDR
if [ -z "$EMAIL_ADDR" ]; then if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel # user hit ESC/cancel
exit exit 1
fi fi
done done
@ -35,9 +35,10 @@ if [ -z "`tools/mail.py user`" ]; then
else else
# Use me@PRIMARY_HOSTNAME # Use me@PRIMARY_HOSTNAME
EMAIL_ADDR=me@$PRIMARY_HOSTNAME EMAIL_ADDR=me@$PRIMARY_HOSTNAME
EMAIL_PW=12345678 EMAIL_PW=$(openssl rand -base64 8)
echo 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 echo
fi fi
else else
@ -47,11 +48,11 @@ if [ -z "`tools/mail.py user`" ]; then
fi fi
# Create the user's mail account. This will ask for a password if none was given above. # 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. # 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. # 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 fi

View File

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

View File

@ -12,7 +12,7 @@
# mail as defined in a "sieve" script. # mail as defined in a "sieve" script.
# #
# Dovecot's LDA role comes after spam filtering. Postfix hands mail off # 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. # using the LMTP protocol.
source setup/functions.sh # load our functions 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, # but dovecot-lucene is packaged by *us* in the Mail-in-a-Box PPA,
# not by Ubuntu. # not by Ubuntu.
echo "Installing Dovecot (IMAP server)..." echo "Installing Dovecot (POP3/IMAP server)..."
apt_install \ apt_install \
dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-sqlite sqlite3 \ dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-sqlite sqlite3 \
dovecot-sieve dovecot-managesieved dovecot-lucene 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/2012-August/137569.html
# - https://www.dovecot.org/list/dovecot/2011-December/132455.html # - https://www.dovecot.org/list/dovecot/2011-December/132455.html
tools/editconf.py /etc/dovecot/conf.d/10-master.conf \ tools/editconf.py /etc/dovecot/conf.d/10-master.conf \
default_process_limit=$(echo "`nproc` * 250" | bc) \ default_process_limit="$((CPU_CORES * 250))" \
default_vsz_limit=$(echo "`free -tm | tail -1 | awk '{print $2}'` / 3" | bc)M \ default_vsz_limit="$(($(free -tm | tail -1 | awk '{print $2}') / 3))M" \
log_path=/var/log/mail.log log_path=/var/log/mail.log
# The inotify `max_user_instances` default is 128, which constrains # 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 # username part of the user's email address. We'll ensure that no bad domains or email addresses
# are created within the management daemon. # are created within the management daemon.
tools/editconf.py /etc/dovecot/conf.d/10-mail.conf \ 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 \ mail_privileged_group=mail \
first_valid_uid=0 first_valid_uid=0
@ -126,7 +126,7 @@ EOF
# ### LDA (LMTP) # ### LDA (LMTP)
# Enable Dovecot's LDA service with the LMTP protocol. It will listen # 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 # The disabled unix socket listener is normally how Postfix and Dovecot
# would communicate (see the Postfix setup script for the corresponding # 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 # Setting a `postmaster_address` is required or LMTP won't start. An alias
# will be created automatically by our management daemon. # will be created automatically by our management daemon.
tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \ tools/editconf.py /etc/dovecot/conf.d/15-lda.conf \
postmaster_address=postmaster@$PRIMARY_HOSTNAME postmaster_address="postmaster@$PRIMARY_HOSTNAME"
# ### Sieve # ### 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 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 # 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. # * `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 chmod -R o-rwx /etc/dovecot
# Ensure mailbox files have a directory that exists and are owned by the mail user. # Ensure mailbox files have a directory that exists and are owned by the mail user.
mkdir -p $STORAGE_ROOT/mail/mailboxes mkdir -p "$STORAGE_ROOT/mail/mailboxes"
chown -R mail.mail $STORAGE_ROOT/mail/mailboxes chown -R mail.mail "$STORAGE_ROOT/mail/mailboxes"
# Same for the sieve scripts. # Same for the sieve scripts.
mkdir -p $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_before"
mkdir -p $STORAGE_ROOT/mail/sieve/global_after mkdir -p "$STORAGE_ROOT/mail/sieve/global_after"
chown -R mail.mail $STORAGE_ROOT/mail/sieve chown -R mail.mail "$STORAGE_ROOT/mail/sieve"
# Allow the IMAP/POP ports in the firewall. # Allow the IMAP/POP ports in the firewall.
ufw_allow imaps ufw_allow imaps

View File

@ -10,11 +10,11 @@
# other servers on the Internet. It is responsible for very # other servers on the Internet. It is responsible for very
# basic email filtering such as by IP address and greylisting, # basic email filtering such as by IP address and greylisting,
# it checks that the destination address is valid, rewrites # 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. # another service for local mail delivery.
# #
# The first hop in local mail delivery is to Spamassassin via # The first hop in local mail delivery is to SpamAssassin via
# LMTP. Spamassassin then passes mail over to Dovecot for # LMTP. SpamAssassin then passes mail over to Dovecot for
# storage in the user's mailbox. # storage in the user's mailbox.
# #
# Postfix also listens on port 587 (SMTP+STARTLS) for # 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`: The SMTP server.
# * `postfix-pcre`: Enables header filtering. # * `postfix-pcre`: Enables header filtering.
# * `postgrey`: A mail policy service that soft-rejects mail the first time # * `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. # always will.
# * `ca-certificates`: A trust store used to squelch postfix warnings about # * `ca-certificates`: A trust store used to squelch postfix warnings about
# untrusted opportunistically-encrypted connections. # 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 # 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): # 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 # > 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. # > anti-spam solutions) must register with dnswl.org and purchase a subscription.
echo "Installing Postfix (SMTP server)..." 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). # * Set the SMTP banner (which must have the hostname first, then anything).
tools/editconf.py /etc/postfix/main.cf \ tools/editconf.py /etc/postfix/main.cf \
inet_interfaces=all \ inet_interfaces=all \
smtp_bind_address=$PRIVATE_IP \ smtp_bind_address="$PRIVATE_IP" \
smtp_bind_address6=$PRIVATE_IPV6 \ smtp_bind_address6="$PRIVATE_IPV6" \
myhostname=$PRIMARY_HOSTNAME\ myhostname="$PRIMARY_HOSTNAME"\
smtpd_banner="\$myhostname ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)" \ smtpd_banner="\$myhostname ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)" \
mydestination=localhost 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 # * 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 # on incoming mail. Omit the OpenDMARC milter by re-setting smtpd_milters to the
# OpenDKIM milter only. See dkim.sh. # 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. # 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/. # * 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). # 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 \ tools/editconf.py /etc/postfix/main.cf \
smtpd_tls_security_level=may\ smtpd_tls_security_level=may\
smtpd_tls_auth_only=yes \ smtpd_tls_auth_only=yes \
smtpd_tls_cert_file=$STORAGE_ROOT/ssl/ssl_certificate.pem \ smtpd_tls_cert_file="$STORAGE_ROOT/ssl/ssl_certificate.pem" \
smtpd_tls_key_file=$STORAGE_ROOT/ssl/ssl_private_key.pem \ smtpd_tls_key_file="$STORAGE_ROOT/ssl/ssl_private_key.pem" \
smtpd_tls_dh1024_param_file=$STORAGE_ROOT/ssl/dh2048.pem \ smtpd_tls_dh1024_param_file="$STORAGE_ROOT/ssl/dh2048.pem" \
smtpd_tls_protocols=\!SSLv2,\!SSLv3 \ smtpd_tls_protocols=\!SSLv2,\!SSLv3 \
smtpd_tls_ciphers=medium \ smtpd_tls_ciphers=medium \
smtpd_tls_exclude_ciphers=aNULL,RC4 \ smtpd_tls_exclude_ciphers=aNULL,RC4 \
@ -172,14 +172,14 @@ tools/editconf.py /etc/postfix/main.cf \
# ### Incoming Mail # ### 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 # 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 # In a basic setup we would pass mail directly to Dovecot by setting
# virtual_transport to `lmtp:unix:private/dovecot-lmtp`. # 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. # 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 # "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 \ 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_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 # 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). # Postgrey listens on the same interface (and not IPv6, for instance).

View File

@ -5,7 +5,7 @@
# #
# This script configures user authentication for Dovecot # This script configures user authentication for Dovecot
# and Postfix (which relies on Dovecot) and destination # 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 setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars 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 db_path=$STORAGE_ROOT/mail/users.sqlite
# Create an empty database if it doesn't yet exist. # Create an empty database if it doesn't yet exist.
if [ ! -f $db_path ]; then if [ ! -f "$db_path" ]; then
echo Creating new user database: $db_path; 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 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; 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 fi
# ### User Authentication # ### User Authentication
@ -128,7 +128,7 @@ EOF
# query the aliases table but also the users table when resolving # query the aliases table but also the users table when resolving
# aliases, i.e. we turn users into aliases from themselves to # aliases, i.e. we turn users into aliases from themselves to
# themselves. That means users will match in postfix's first query # 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 # 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 # 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 # We used to install management daemon-related Python packages
# directly to /usr/local/lib. We moved to a virtualenv because # directly to /usr/local/lib. We moved to a virtualenv because
# these packages might conflict with apt-installed packages. # 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 # certbot, which we're about to install below, so remove it
# first. Once acme is installed by an apt package, this might # first. Once acme is installed by an apt package, this might
# break the package version and `apt-get install --reinstall python3-acme` # 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 # certbot installs EFF's certbot which we use to
# provision free TLS certificates. # 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 hide_output pip2 install --upgrade boto
# Create a virtualenv for the installation of Python 3 packages # Create a virtualenv for the installation of Python 3 packages
@ -55,9 +55,9 @@ hide_output $venv/bin/pip install --upgrade \
# CONFIGURATION # CONFIGURATION
# Create a backup directory and a random key for encrypting backups. # Create a backup directory and a random key for encrypting backups.
mkdir -p $STORAGE_ROOT/backup mkdir -p "$STORAGE_ROOT/backup"
if [ ! -f $STORAGE_ROOT/backup/secret_key.txt ]; then if [ ! -f "$STORAGE_ROOT/backup/secret_key.txt" ]; then
$(umask 077; openssl rand -base64 2048 > $STORAGE_ROOT/backup/secret_key.txt) umask 077; openssl rand -base64 2048 > "$STORAGE_ROOT/backup/secret_key.txt"
fi fi
@ -91,11 +91,11 @@ rm -f /usr/local/bin/mailinabox-daemon # old path
cat > $inst_dir/start <<EOF; cat > $inst_dir/start <<EOF;
#!/bin/bash #!/bin/bash
source $venv/bin/activate source $venv/bin/activate
exec python `pwd`/management/daemon.py exec python $(pwd)/management/daemon.py
EOF EOF
chmod +x $inst_dir/start chmod +x $inst_dir/start
rm -f /etc/init.d/mailinabox 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 hide_output update-rc.d mailinabox defaults
# Remove old files we no longer use. # 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; cat > /etc/cron.d/mailinabox-nightly << EOF;
# Mail-in-a-Box --- Do not edit / will be overwritten on update. # Mail-in-a-Box --- Do not edit / will be overwritten on update.
# Run nightly tasks: backup, status checks. # 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 EOF
# Start the management server. # 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 # ensure munin-node knows the name of this machine
# and reduce logging level to warning # and reduce logging level to warning
tools/editconf.py /etc/munin/munin-node.conf -s \ tools/editconf.py /etc/munin/munin-node.conf -s \
host_name=$PRIMARY_HOSTNAME \ host_name="$PRIMARY_HOSTNAME" \
log_level=1 log_level=1
# Update the activated plugins through munin's autoconfiguration. # 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. # 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 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=$(echo "$f" | sed s/.*_//);
if ! ifquery $IF >/dev/null 2>/dev/null; then if ! ifquery "$IF" >/dev/null 2>/dev/null; then
rm $f; rm "$f";
fi; fi;
done 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 # 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 # and will not be able to reliably send mail. Do this after any automatic
# choices made above. # choices made above.
if host $PRIMARY_HOSTNAME.dbl.spamhaus.org > /dev/null; then if host "$PRIMARY_HOSTNAME.dbl.spamhaus.org" > /dev/null; then
echo echo >&2
echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the" echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the" >&2
echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/" echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/" >&2
echo "and http://www.spamhaus.org/query/domain/$PRIMARY_HOSTNAME." echo "and http://www.spamhaus.org/query/domain/$PRIMARY_HOSTNAME." >&2
echo echo >&2
echo "You will not be able to send mail using this domain name, so" echo "You will not be able to send mail using this domain name, so" >&2
echo "setup cannot continue." echo "setup cannot continue." >&2
echo echo >&2
exit 1 exit 1
fi fi
@ -22,19 +22,19 @@ fi
# The user might have ended up on an IP address that was previously in use # 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 # 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. # 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/") 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 if host "$REVERSED_IPV4.zen.spamhaus.org" > /dev/null; then
echo echo >&2
echo "The IP address $PUBLIC_IP is listed in the Spamhaus Block List." echo "The IP address $PUBLIC_IP is listed in the Spamhaus Block List." >&2
echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP." echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP." >&2
echo echo >&2
echo "You will not be able to send mail using this machine, so setup" echo "You will not be able to send mail using this machine, so setup" >&2
echo "cannot continue." echo "cannot continue." >&2
echo echo >&2
echo "Associate a different IP address with this machine if possible." 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" echo "Many residential network IP addresses are listed, so Mail-in-a-Box" >&2
echo "typically cannot be used on a residential Internet connection." echo "typically cannot be used on a residential Internet connection." >&2
echo echo >&2
exit 1 exit 1
fi fi
@ -42,16 +42,16 @@ fi
# networks block outbound port 25 to prevent their network from sending spam. # 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. # 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 if ! nc -z -w5 aspmx.l.google.com 25; then
echo echo >&2
echo "Outbound mail (port 25) seems to be blocked by your network." echo "Outbound mail (port 25) seems to be blocked by your network." >&2
echo echo >&2
echo "You will not be able to send mail using this machine, so setup" echo "You will not be able to send mail using this machine, so setup" >&2
echo "cannot continue." echo "cannot continue." >&2
echo echo >&2
echo "Many residential networks block port 25 to prevent hijacked" echo "Many residential networks block port 25 to prevent hijacked" >&2
echo "machines from being able to send spam. I just tried to connect" 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" echo "to Google's mail server on port 25 but the connection did not" >&2
echo "succeed." echo "succeed." >&2
echo echo >&2
exit 1 exit 1
fi fi

View File

@ -9,7 +9,7 @@ source /etc/mailinabox.conf # load global vars
echo "Installing Nextcloud (contacts/calendar)..." echo "Installing Nextcloud (contacts/calendar)..."
# Keep the php5 dependancies for the owncloud upgrades # Keep the php5 dependencies for the ownCloud upgrades
apt_install \ apt_install \
dbconfig-common \ 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 \ 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 # Migrate <= v0.10 setups that stored the ownCloud config.php in /usr/local rather than
# in STORAGE_ROOT. Move the file to STORAGE_ROOT. # 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 && [ -f /usr/local/lib/owncloud/config/config.php ]; then
# Move config.php and symlink back into previous location. # Move config.php and symlink back into previous location.
echo "Migrating owncloud/config.php to new 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 fi
InstallNextcloud() { InstallNextcloud() {
@ -42,11 +42,11 @@ InstallNextcloud() {
echo "Upgrading to Nextcloud version $version" echo "Upgrading to Nextcloud version $version"
echo echo
# Remove the current owncloud/Nextcloud # Remove the current ownCloud/Nextcloud
rm -rf /usr/local/lib/owncloud rm -rf /usr/local/lib/owncloud
# Download and verify # 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 # Extract ownCloud/Nextcloud
unzip -q /tmp/nextcloud.zip -d /usr/local/lib unzip -q /tmp/nextcloud.zip -d /usr/local/lib
@ -54,7 +54,7 @@ InstallNextcloud() {
rm -f /tmp/nextcloud.zip rm -f /tmp/nextcloud.zip
# The two apps we actually want are not in Nextcloud core. Download the releases from # 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 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 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 # 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). # 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. # Make sure permissions are correct or the upgrade step won't run.
# $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress # $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress
# that error. # 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. # 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). # 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 # ownCloud 8.1.1 broke upgrades. It may fail on the first attempt, but
# that can be OK. # that can be OK.
sudo -u www-data php /usr/local/lib/owncloud/occ upgrade 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..." echo "Trying ownCloud upgrade again to work around ownCloud upgrade bug..."
sudo -u www-data php /usr/local/lib/owncloud/occ upgrade 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 sudo -u www-data php /usr/local/lib/owncloud/occ maintenance:mode --off
echo "...which seemed to work." echo "...which seemed to work."
fi fi
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() { InstallOwncloud() {
version=$1 version=$1
hash=$2 hash=$2
echo echo
echo "Upgrading to OwnCloud version $version" echo "Upgrading to ownCloud version $version"
echo echo
# Remove the current owncloud/Nextcloud # Remove the current ownCloud/Nextcloud
rm -rf /usr/local/lib/owncloud rm -rf /usr/local/lib/owncloud
# Download and verify # 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 # Extract ownCloud
@ -115,7 +117,7 @@ InstallOwncloud() {
rm -f /tmp/owncloud.tar.bz2 rm -f /tmp/owncloud.tar.bz2
# The two apps we actually want are not in Nextcloud core. Download the releases from # 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 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 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 # 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). # 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. # Make sure permissions are correct or the upgrade step won't run.
# $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress # $STORAGE_ROOT/owncloud may not yet exist, so use -f to suppress
# that error. # 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. # 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). # 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 # ownCloud 8.1.1 broke upgrades. It may fail on the first attempt, but
# that can be OK. # that can be OK.
sudo -u www-data php5 /usr/local/lib/owncloud/occ upgrade 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..." echo "Trying ownCloud upgrade again to work around ownCloud upgrade bug..."
sudo -u www-data php5 /usr/local/lib/owncloud/occ upgrade 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 sudo -u www-data php5 /usr/local/lib/owncloud/occ maintenance:mode --off
echo "...which seemed to work." echo "...which seemed to work."
fi fi
@ -161,40 +165,40 @@ owncloud_hash=e2b4a4bebd4fac14feae1e6e8997682f73fa8b50
if [ ! -d /usr/local/lib/owncloud/ ] \ if [ ! -d /usr/local/lib/owncloud/ ] \
|| ! grep -q $owncloud_ver /usr/local/lib/owncloud/version.php; then || ! 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 php7.0-fpm stop &> /dev/null || /bin/true
service php5-fpm stop &> /dev/null || /bin/true service php5-fpm stop &> /dev/null || /bin/true
# Backup the existing ownCloud/Nextcloud. # Backup the existing ownCloud/Nextcloud.
# Create a backup directory to store the current installation and database to # 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" mkdir -p "$BACKUP_DIRECTORY"
if [ -d /usr/local/lib/owncloud/ ]; then 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..." 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" cp -r /usr/local/lib/owncloud "$BACKUP_DIRECTORY/owncloud-install"
fi fi
if [ -e /home/user-data/owncloud/owncloud.db ]; then 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 fi
if [ -e /home/user-data/owncloud/config.php ]; then 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 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 [ -e /usr/local/lib/owncloud/version.php ]; then
if grep -q "OC_VersionString = '8\.1\.[0-9]" /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" echo "We are running 8.1.x, upgrading to 8.2.11 first"
InstallOwncloud 8.2.11 e4794938fc2f15a095018ba9d6ee18b53f6f299c InstallOwncloud 8.2.11 e4794938fc2f15a095018ba9d6ee18b53f6f299c
fi 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 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" echo "We are running version 8.2.x, upgrading to 9.0.11 first"
# We need to disable memcached. The upgrade and install fails # We need to disable memcached. The upgrade and install fails
# with memcached # with memcached
CONFIG_TEMP=$(/bin/mktemp) 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 <?php
include("$STORAGE_ROOT/owncloud/config.php"); include("$STORAGE_ROOT/owncloud/config.php");
@ -205,19 +209,19 @@ if [ ! -d /usr/local/lib/owncloud/ ] \
echo ";"; echo ";";
?> ?>
EOF 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 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 # The option to migrate these are removed in 9.1
# So the migrations should be done when we have 9.0 installed # 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 sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:migrate-addressbooks
# The following migration has to be done for each owncloud user # The following migration has to be done for each ownCloud user
for directory in $STORAGE_ROOT/owncloud/*@*/ ; do for directory in "$STORAGE_ROOT"/owncloud/*@*/ ; do
username=$(basename "${directory}") 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 done
sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:sync-birthday-calendar sudo -u www-data php5 /usr/local/lib/owncloud/occ dav:sync-birthday-calendar
fi fi
@ -260,13 +264,13 @@ fi
# Setup Nextcloud if the Nextcloud database does not yet exist. Running setup when # Setup Nextcloud if the Nextcloud database does not yet exist. Running setup when
# the database does exist wipes the database and user data. # 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 # Create user data directory
mkdir -p $STORAGE_ROOT/owncloud mkdir -p "$STORAGE_ROOT/owncloud"
# Create an initial configuration file. # Create an initial configuration file.
instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1) instanceid=oc$(echo "$PRIMARY_HOSTNAME" | sha1sum | fold -w 10 | head -n 1)
cat > $STORAGE_ROOT/owncloud/config.php <<EOF; cat > "$STORAGE_ROOT/owncloud/config.php" <<EOF;
<?php <?php
\$CONFIG = array ( \$CONFIG = array (
'datadirectory' => '$STORAGE_ROOT/owncloud', 'datadirectory' => '$STORAGE_ROOT/owncloud',
@ -317,7 +321,7 @@ EOF
EOF EOF
# Set permissions # 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. # 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 # 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 # 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. # this will make sure it has the right value.
# * Some settings weren't included in previous versions of Mail-in-a-Box. # * 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 # 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 # * 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. # 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. # Use PHP to read the settings file, modify it, and write out the new settings array.
TIMEZONE=$(cat /etc/timezone) TIMEZONE=$(cat /etc/timezone)
CONFIG_TEMP=$(/bin/mktemp) 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 <?php
include("$STORAGE_ROOT/owncloud/config.php"); include("$STORAGE_ROOT/owncloud/config.php");
@ -358,7 +362,7 @@ var_export(\$CONFIG);
echo ";"; echo ";";
?> ?>
EOF 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. # Enable/disable apps. Note that this must be done after the Nextcloud setup.
# The firstrunwizard gave Josh all sorts of problems, so disabling that. # 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? # 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). # Check for success (0=ok, 3=no upgrade needed).
sudo -u www-data php /usr/local/lib/owncloud/occ upgrade 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 # Set PHP FPM values to support large file uploads
# (semicolon is the comment character in this file, hashes produce deprecation warnings) # (semicolon is the comment character in this file, hashes produce deprecation warnings)

View File

@ -1,20 +1,38 @@
# Are we running as root? # Are we running as root?
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root. Please re-run like this:" echo "This script must be run as root. Please re-run like this:" >&2
echo echo >&2
echo "sudo $0" echo "sudo $0" >&2
echo echo >&2
exit exit 1
fi 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). # 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 if ! echo "$ID" | grep -iq "ubuntu" || ! echo "$VERSION_ID" | grep -iq "14.04"; then
echo "Mail-in-a-Box only supports being installed on Ubuntu 14.04, sorry. You are running:" if echo "$ID" | grep -iq "ubuntu" && echo "$VERSION_ID" | grep -iq "18.04"; then
echo 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
lsb_release -d | sed 's/.*:\s*//' exit 1
echo else
echo "We can't write scripts that run on every possible setup, sorry." echo "Mail-in-a-Box only supports being installed on Ubuntu 14.04, sorry. You are running:" >&2
exit 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 fi
# Check that we have enough memory. # 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 # 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. # 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}') TOTAL_PHYSICAL_MEM=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
if [ $TOTAL_PHYSICAL_MEM -lt 500000 ]; then TOTAL_SWAP=$(awk '/^SwapTotal:/ {print $2}' /proc/meminfo)
if [ ! -d /vagrant ]; then if [ "$TOTAL_PHYSICAL_MEM" -lt 500000 ]; then
TOTAL_PHYSICAL_MEM=$(expr \( \( $TOTAL_PHYSICAL_MEM \* 1024 \) / 1000 \) / 1000) if [ ! -d /vagrant ]; then
echo "Your Mail-in-a-Box needs more memory (RAM) to function properly." 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 recommended." echo "Please provision a machine with at least 512 MB, 1 GB (1024 MB) recommended." >&2
echo "This machine has $TOTAL_PHYSICAL_MEM MB memory." echo "This machine has $(printf "%'d" $((((TOTAL_PHYSICAL_MEM * 1024) / 1000) / 1000))) MB ($(printf "%'d" $((TOTAL_PHYSICAL_MEM / 1024))) MiB) memory."
exit exit 1
fi
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 "WARNING: Your Mail-in-a-Box has less than 768 MB of memory."
echo " It might run unreliably when under heavy load." echo " It might run unreliably when under heavy load."
fi 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 # Check that tempfs is mounted with exec
MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts) MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts)
if [ -n "$MOUNTED_TMP_AS_NO_EXEC" ]; then 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" echo "Mail-in-a-Box has to have exec rights on /tmp, please mount /tmp with exec" >&2
exit exit 1
fi fi
# Check that no .wgetrc exists # Check that no .wgetrc exists
if [ -e ~/.wgetrc ]; then if [ -e ~/.wgetrc ]; then
echo "Mail-in-a-Box expects no overrides to wget defaults, ~/.wgetrc exists" echo "Mail-in-a-Box expects no overrides to wget defaults, ~/.wgetrc exists" >&2
exit exit 1
fi fi
# Check that we are running on x86_64 or i686, any other architecture is unsupported and # 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 # Set ARM=1 to ignore this check if you have built the packages yourself. If you do this
# you are on your own! # you are on your own!
ARCHITECTURE=$(uname -m) ARCHITECTURE=$(getconf LONG_BIT)
if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "i686" ]; then if [ "$HOSTTYPE" != "x86_64" ] && [ "$HOSTTYPE" != "i686" ]; then
if [ -z "$ARM" ]; 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 "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 $ARCHITECTURE" echo "Your architecture is $HOSTTYPE ($ARCHITECTURE-bit)"
exit exit 1
fi fi
fi fi

View File

@ -7,8 +7,8 @@ if [ -z "$NONINTERACTIVE" ]; then
# #
# Also install dependencies needed to validate the email address. # Also install dependencies needed to validate the email address.
if [ ! -f /usr/bin/dialog ] || [ ! -f /usr/bin/python3 ] || [ ! -f /usr/bin/pip3 ]; then if [ ! -f /usr/bin/dialog ] || [ ! -f /usr/bin/python3 ] || [ ! -f /usr/bin/pip3 ]; then
echo Installing packages needed for setup... echo "Installing packages needed for setup..."
apt-get -q -q update hide_output apt-get update
apt_get_quiet install dialog python3 python3-pip || exit 1 apt_get_quiet install dialog python3 python3-pip || exit 1
fi fi
@ -31,7 +31,7 @@ if [ -z "$PRIMARY_HOSTNAME" ]; then
# domain the user possibly wants to use is example.com then. # domain the user possibly wants to use is example.com then.
# We strip the string "box." from the hostname to get the mail # We strip the string "box." from the hostname to get the mail
# domain. If the hostname differs, nothing happens here. # 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 # This is the first run. Ask the user for his email address so we can
# provide the best default for the box's hostname. # provide the best default for the box's hostname.
@ -49,23 +49,23 @@ you really want.
if [ -z "$EMAIL_ADDR" ]; then if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel # user hit ESC/cancel
exit exit 1
fi fi
while ! python3 management/mailconfig.py validate-email "$EMAIL_ADDR" while ! python3 management/mailconfig.py validate-email "$EMAIL_ADDR"
do do
input_box "Your Email Address" \ input_box "Your Email Address" \
"That's not a valid email address.\n\nWhat email address are you setting this box up to manage?" \ "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 EMAIL_ADDR
if [ -z "$EMAIL_ADDR" ]; then if [ -z "$EMAIL_ADDR" ]; then
# user hit ESC/cancel # user hit ESC/cancel
exit exit 1
fi fi
done done
# Take the part after the @-sign as the user's domain name, and add # 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. # '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 fi
input_box "Hostname" \ input_box "Hostname" \
@ -74,12 +74,19 @@ you really want.
address, so we're suggesting $DEFAULT_PRIMARY_HOSTNAME. address, so we're suggesting $DEFAULT_PRIMARY_HOSTNAME.
\n\nYou can change it, but we recommend you don't. \n\nYou can change it, but we recommend you don't.
\n\nHostname:" \ \n\nHostname:" \
$DEFAULT_PRIMARY_HOSTNAME \ "$DEFAULT_PRIMARY_HOSTNAME" \
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 if [ -z "$PRIMARY_HOSTNAME" ]; then
# user hit ESC/cancel # 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
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 # On the first run, if we got an answer from the Internet then don't
# ask the user. # 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 PUBLIC_IP=$GUESSED_IP
# Otherwise on the first run at least provide a default. # Otherwise on the first run at least provide a default.
@ -109,12 +116,12 @@ if [ -z "$PUBLIC_IP" ]; then
input_box "Public IP Address" \ input_box "Public IP Address" \
"Enter the public IP address of this machine, as given to you by your ISP. "Enter the public IP address of this machine, as given to you by your ISP.
\n\nPublic IP address:" \ \n\nPublic IP address:" \
$DEFAULT_PUBLIC_IP \ "$DEFAULT_PUBLIC_IP" \
PUBLIC_IP PUBLIC_IP
if [ -z "$PUBLIC_IP" ]; then if [ -z "$PUBLIC_IP" ]; then
# user hit ESC/cancel # user hit ESC/cancel
exit exit 1
fi fi
fi fi
fi fi
@ -125,7 +132,7 @@ if [ -z "$PUBLIC_IPV6" ]; then
# Ask the Internet. # Ask the Internet.
GUESSED_IP=$(get_publicip_from_web_service 6) GUESSED_IP=$(get_publicip_from_web_service 6)
MATCHED=0 MATCHED=0
if [[ -z "$DEFAULT_PUBLIC_IPV6" && ! -z "$GUESSED_IP" ]]; then if [[ -z "$DEFAULT_PUBLIC_IPV6" && -n "$GUESSED_IP" ]]; then
PUBLIC_IPV6=$GUESSED_IP PUBLIC_IPV6=$GUESSED_IP
elif [[ "$DEFAULT_PUBLIC_IPV6" == "$GUESSED_IP" ]]; then elif [[ "$DEFAULT_PUBLIC_IPV6" == "$GUESSED_IP" ]]; then
# No IPv6 entered and machine seems to have none, or what # 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. "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\nLeave blank if the machine does not have an IPv6 address.
\n\nPublic IPv6 address:" \ \n\nPublic IPv6 address:" \
$DEFAULT_PUBLIC_IPV6 \ "$DEFAULT_PUBLIC_IPV6" \
PUBLIC_IPV6 PUBLIC_IPV6
if [ ! $PUBLIC_IPV6_EXITCODE ]; then if [ ! "$PUBLIC_IPV6_EXITCODE" ]; then
# user hit ESC/cancel # user hit ESC/cancel
exit exit 1
fi fi
fi fi
fi fi
@ -162,13 +169,13 @@ if [ -z "$PRIVATE_IPV6" ]; then
fi fi
if [[ -z "$PRIVATE_IP" && -z "$PRIVATE_IPV6" ]]; then if [[ -z "$PRIVATE_IP" && -z "$PRIVATE_IPV6" ]]; then
echo 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 "for connecting to the Internet. Setup must stop."
echo echo
hostname -I hostname -I
route route
echo echo
exit exit 1
fi fi
# Automatic configuration, e.g. as used in our Vagrant configuration. # Automatic configuration, e.g. as used in our Vagrant configuration.
@ -194,19 +201,50 @@ if [ -z "$STORAGE_ROOT" ]; then
fi fi
# Show the configuration, since the user may have not entered it manually. # Show the configuration, since the user may have not entered it manually.
echo echo -e "\nLinux Distribution:\t\t${PRETTY_NAME:-$ID-$VERSION_ID}"
echo "Primary Hostname: $PRIMARY_HOSTNAME" CPU=( $(sed -n 's/^model name[[:space:]]*: *//p' /proc/cpuinfo | uniq) )
echo "Public IP Address: $PUBLIC_IP" if [ -n "$CPU" ]; then
if [ ! -z "$PUBLIC_IPV6" ]; then echo -e "Processor (CPU):\t\t${CPU[*]}"
echo "Public IPv6 Address: $PUBLIC_IPV6"
fi fi
if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then CPU_CORES=$(nproc --all)
echo "Private IP Address: $PRIVATE_IP" 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 fi
if [ "$PRIVATE_IPV6" != "$PUBLIC_IPV6" ]; then if [ -n "$GPU" ]; then
echo "Private IPv6 Address: $PRIVATE_IPV6" echo -e "Graphics Processor (GPU):\t${GPU[*]}"
fi fi
if [ -f /usr/bin/git ] && [ -d .git ]; then echo -e "Computer name:\t\t\t$HOSTNAME"
echo "Mail-in-a-Box Version: " $(git describe) 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 fi
echo

View File

@ -1,9 +1,9 @@
#!/bin/bash #!/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 # spampd sits between Postfix and Dovecot. It takes mail from Postfix
# over the LMTP protocol, runs spamassassin on it, and then passes the # over the LMTP protocol, runs SpamAssassin on it, and then passes the
# message over LMTP to dovecot for local delivery. # message over LMTP to dovecot for local delivery.
# #
# In order to move spam automatically into the Spam folder we use the dovecot sieve # 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. # 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: # For more information see Debian Bug #689414:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=689414 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=689414
echo "Installing SpamAssassin..." echo "Installing SpamAssassin..."
apt_install spampd razor pyzor dovecot-antispam libmail-dkim-perl 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 \ tools/editconf.py /etc/default/spamassassin \
CRON=1 CRON=1
@ -41,26 +41,26 @@ echo "public.pyzor.org:24441" > /etc/spamassassin/pyzor/servers
# check with: pyzor --homedir /etc/mail/spamassassin/pyzor ping # check with: pyzor --homedir /etc/mail/spamassassin/pyzor ping
# Configure spampd: # 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.) # 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 # * 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. # * Disable localmode so Pyzor, DKIM and DNS checks can be used.
tools/editconf.py /etc/default/spampd \ tools/editconf.py /etc/default/spampd \
DESTPORT=10026 \ DESTPORT=10026 \
ADDOPTS="\"--maxsize=2000\"" \ ADDOPTS="\"--maxsize=2000\"" \
LOCALONLY=0 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 # 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, # 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 # 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 # attachment, modern mail clients are safer now and don't load remote
# content or execute scripts, and it is probably confusing to most users. # 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. # the X-Spam-Status & X-Spam-Score mail headers and related headers.
tools/editconf.py /etc/spamassassin/local.cf -s \ tools/editconf.py /etc/spamassassin/local.cf -s \
report_safe=0 \ report_safe=0 \
@ -70,26 +70,26 @@ tools/editconf.py /etc/spamassassin/local.cf -s \
# Bayesean learning # 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. # configured. We'll store the learning data in our storage area.
# #
# These files must be: # 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. # * 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. # 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. # the filemode in the config file.
tools/editconf.py /etc/spamassassin/local.cf -s \ 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 bayes_file_mode=0666
mkdir -p $STORAGE_ROOT/mail/spamassassin mkdir -p "$STORAGE_ROOT/mail/spamassassin"
chown -R spampd:spampd $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 # 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 # 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, # 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. # ensure they are group-writable so that the Dovecot process has access.
sudo -u spampd /usr/bin/sa-learn --sync 2>/dev/null sudo -u spampd /usr/bin/sa-learn --sync 2>/dev/null
chmod -R 660 $STORAGE_ROOT/mail/spamassassin chmod -R 660 "$STORAGE_ROOT/mail/spamassassin"
chmod 770 $STORAGE_ROOT/mail/spamassassin chmod 770 "$STORAGE_ROOT/mail/spamassassin"
# Initial training? # Initial training?
# sa-learn --ham storage/mail/mailboxes/*/*/cur/ # 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. # Show a status line if we are going to take any action in this file.
if [ ! -f /usr/bin/openssl ] \ if [ ! -f /usr/bin/openssl ] \
|| [ ! -f $STORAGE_ROOT/ssl/ssl_private_key.pem ] \ || [ ! -f "$STORAGE_ROOT/ssl/ssl_private_key.pem" ] \
|| [ ! -f $STORAGE_ROOT/ssl/ssl_certificate.pem ] \ || [ ! -f "$STORAGE_ROOT/ssl/ssl_certificate.pem" ] \
|| [ ! -f $STORAGE_ROOT/ssl/dh2048.pem ]; then || [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then
echo "Creating initial SSL certificate and perfect forward secrecy Diffie-Hellman parameters..." echo "Creating initial SSL certificate and perfect forward secrecy Diffie-Hellman parameters..."
fi fi
@ -38,7 +38,7 @@ apt_install openssl
# Create a directory to store TLS-related things like "SSL" certificates. # 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. # 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 # 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. # 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. # Set the umask so the key file is never world-readable.
(umask 077; hide_output \ (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 fi
# Generate a self-signed SSL certificate because things like nginx, dovecot, # 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 # 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. # 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. # Generate a certificate signing request.
CSR=/tmp/ssl_cert_sign_req-$$.csr CSR=/tmp/ssl_cert_sign_req-$$.csr
hide_output \ 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" -sha256 -subj "/CN=$PRIMARY_HOSTNAME"
# Generate the self-signed certificate. # Generate the self-signed certificate.
CERT=$STORAGE_ROOT/ssl/$PRIMARY_HOSTNAME-selfsigned-$(date --rfc-3339=date | sed s/-//g).pem CERT=$STORAGE_ROOT/ssl/$PRIMARY_HOSTNAME-selfsigned-$(date --rfc-3339=date | sed s/-//g).pem
hide_output \ hide_output \
openssl x509 -req -days 365 \ 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. # Delete the certificate signing request because it has no other purpose.
rm -f $CSR rm -f $CSR
# Symlink the certificate into the system certificate path, so system services # Symlink the certificate into the system certificate path, so system services
# can find it. # can find it.
ln -s $CERT $STORAGE_ROOT/ssl/ssl_certificate.pem ln -s "$CERT" "$STORAGE_ROOT/ssl/ssl_certificate.pem"
fi fi
# Generate some Diffie-Hellman cipher bits. # Generate some Diffie-Hellman cipher bits.
# openssl's default bit length for this is 1024 bits, but we'll create # openssl's default bit length for this is 1024 bits, but we'll create
# 2048 bits of bits per the latest recommendations. # 2048 bits of bits per the latest recommendations.
if [ ! -f $STORAGE_ROOT/ssl/dh2048.pem ]; then if [ ! -f "$STORAGE_ROOT/ssl/dh2048.pem" ]; then
openssl dhparam -out $STORAGE_ROOT/ssl/dh2048.pem 2048 openssl dhparam -out "$STORAGE_ROOT/ssl/dh2048.pem" 2048
fi fi

View File

@ -2,6 +2,11 @@
# This is the entry point for configuring the system. # 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 source setup/functions.sh # load our functions
# Check system setup: Are we running as root on Ubuntu 14.04 on a # 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 # Load the old .conf file to get existing configuration options loaded
# into variables with a DEFAULT_ prefix. # 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 source /tmp/mailinabox.prev.conf
rm -f /tmp/mailinabox.prev.conf rm -f /tmp/mailinabox.prev.conf
else else
@ -46,7 +52,7 @@ fi
# in the first dialog prompt, so we should do this before that starts. # in the first dialog prompt, so we should do this before that starts.
cat > /usr/local/bin/mailinabox << EOF; cat > /usr/local/bin/mailinabox << EOF;
#!/bin/bash #!/bin/bash
cd `pwd` cd $(pwd)
source setup/start.sh source setup/start.sh
EOF EOF
chmod +x /usr/local/bin/mailinabox 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 # 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. # upgrade, and these checks are also in the control panel status checks.
if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then
if [ -z "$SKIP_NETWORK_CHECKS" ]; then if [ -z "$SKIP_NETWORK_CHECKS" ]; then
source setup/network-checks.sh source setup/network-checks.sh
fi fi
fi fi
# Create the STORAGE_USER and STORAGE_ROOT directory if they don't already exist. # 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 # 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 # installation to that directory and write the file to contain the current
# migration number for this version of Mail-in-a-Box. # migration number for this version of Mail-in-a-Box.
if ! id -u $STORAGE_USER >/dev/null 2>&1; then if ! id -u "$STORAGE_USER" >/dev/null 2>&1; then
useradd -m $STORAGE_USER useradd -m "$STORAGE_USER"
fi fi
if [ ! -d $STORAGE_ROOT ]; then if [ ! -d "$STORAGE_ROOT" ]; then
mkdir -p $STORAGE_ROOT mkdir -p "$STORAGE_ROOT"
fi fi
if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then if [ ! -f "$STORAGE_ROOT/mailinabox.version" ]; then
echo $(setup/migrate.py --current) > $STORAGE_ROOT/mailinabox.version setup/migrate.py --current > "$STORAGE_ROOT/mailinabox.version"
chown $STORAGE_USER.$STORAGE_USER $STORAGE_ROOT/mailinabox.version chown "$STORAGE_USER.$STORAGE_USER" "$STORAGE_ROOT/mailinabox.version"
fi fi
@ -114,7 +120,7 @@ source setup/munin.sh
# Wait for the management daemon to start... # Wait for the management daemon to start...
until nc -z -w 4 127.0.0.1 10222 until nc -z -w 4 127.0.0.1 10222
do 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 sleep 2
done done
@ -123,8 +129,8 @@ done
tools/dns_update tools/dns_update
tools/web_update tools/web_update
# Give fail2ban another restart. The log files may not all have been present when # Give Fail2Ban another restart. The log files may not all have been present when
# fail2ban was first configured, but they should exist now. # Fail2Ban was first configured, but they should exist now.
restart_service fail2ban restart_service fail2ban
# If there aren't any mail users yet, create one. # 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 # 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 # run in the recommended curl-pipe-to-bash method there is no TTY and
# certbot will fail if it tries to ask. # certbot will fail if it tries to ask.
if [ ! -d $STORAGE_ROOT/ssl/lets_encrypt/accounts/acme-v02.api.letsencrypt.org/ ]; then if [ ! -d "$STORAGE_ROOT/ssl/lets_encrypt/accounts/acme-v02.api.letsencrypt.org/" ]; then
echo echo
echo "-----------------------------------------------" echo "-----------------------------------------------"
echo "Mail-in-a-Box uses Let's Encrypt to provision free SSL/TLS certificates" 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 "to enable HTTPS connections to your box. We're automatically"
echo "agreeing you to their subscriber agreement. See https://letsencrypt.org." echo "agreeing you to their subscriber agreement. See https://letsencrypt.org."
echo echo
certbot register --register-unsafely-without-email --agree-tos --config-dir $STORAGE_ROOT/ssl/lets_encrypt certbot register -n --register-unsafely-without-email --agree-tos --config-dir "$STORAGE_ROOT/ssl/lets_encrypt"
fi fi
# Done. # Done.
echo echo
echo "-----------------------------------------------" echo "-----------------------------------------------"
echo echo
echo Your Mail-in-a-Box is running. echo "Your Mail-in-a-Box is running."
echo 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 echo
if management/status_checks.py --check-primary-hostname; then if management/status_checks.py --check-primary-hostname; then
# Show the nice URL if it appears to be resolving and has a valid certificate. # 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
echo "If you have a DNS problem put the box's IP address in the URL" 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:" 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=//" | sed "s/SHA256 Fingerprint=//"
else else
echo https://$PUBLIC_IP/admin echo "https://$PUBLIC_IP/admin"
echo echo
echo You will be alerted that the website has an invalid certificate. Check that echo "You will be alerted that the website has an invalid certificate. Check that"
echo the certificate fingerprint matches: echo "the certificate fingerprint matches:"
echo 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=//" | sed "s/SHA256 Fingerprint=//"
echo echo
echo Then you can confirm the security exception and continue. echo "Then you can confirm the security exception and continue."
echo echo
fi 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 # First set the hostname in the configuration file, then activate the setting
echo $PRIMARY_HOSTNAME > /etc/hostname hostnamectl set-hostname "$PRIMARY_HOSTNAME"
hostname $PRIMARY_HOSTNAME
# ### Add swap space to the system # ### Add swap space to the system
# If the physical memory of the system is below 2GB it is wise to create a # 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 # prevent for instance spam filtering from crashing
# We will create a 1G file, this should be a good balance between disk usage # 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 # See https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04
# for reference # for reference
SWAP_MOUNTED=$(cat /proc/swaps | tail -n+2) SWAP_MOUNTED=$(< /proc/swaps tail -n+2)
SWAP_IN_FSTAB=$(grep "swap" /etc/fstab) SWAP_IN_FSTAB=$(grep "swap" /etc/fstab)
ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts) ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts)
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}') TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}')
@ -46,14 +45,14 @@ if
[ -z "$SWAP_IN_FSTAB" ] && [ -z "$SWAP_IN_FSTAB" ] &&
[ ! -e /swapfile ] && [ ! -e /swapfile ] &&
[ -z "$ROOT_IS_BTRFS" ] && [ -z "$ROOT_IS_BTRFS" ] &&
[ $TOTAL_PHYSICAL_MEM -lt 1900000 ] && [ "$TOTAL_PHYSICAL_MEM" -lt 1900000 ] &&
[ $AVAILABLE_DISK_SPACE -gt 5242880 ] [ "$AVAILABLE_DISK_SPACE" -gt 5242880 ]
then then
echo "Adding a swap file to the system..." 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 # 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 if [ -e /swapfile ]; then
chmod 600 /swapfile chmod 600 /swapfile
hide_output mkswap /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 # of things from Ubuntu, as well as the directory of packages provide by the
# PPAs so we can install those packages later. # PPAs so we can install those packages later.
echo Updating system packages... echo "Updating system packages..."
hide_output apt-get update 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 # 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 # 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 # * ntp: keeps the system time correct
# * fail2ban: scans log files for repeated failed login attempts and blocks the remote IP at the firewall # * fail2ban: scans log files for repeated failed login attempts and blocks the remote IP at the firewall
# * netcat-openbsd: `nc` command line networking tool # * 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 # * sudo: allows privileged users to execute commands as root without being root
# * coreutils: includes `nproc` tool to report number of processors, mktemp # * coreutils: includes `nproc` tool to report number of processors, mktemp
# * bc: allows us to do math to compute sane defaults # * 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 \ apt_install python3 python3-dev python3-pip \
netcat-openbsd wget curl git sudo coreutils bc \ netcat-openbsd wget curl git sudo coreutils bc \
haveged pollinate unzip \ haveged pollinate unzip \
@ -157,13 +156,13 @@ fi
# things (i.e. late at night in whatever timezone the user actually lives # things (i.e. late at night in whatever timezone the user actually lives
# in). # in).
# #
# However, changing the timezone once it is set seems to confuse fail2ban # However, changing the timezone once it is set seems to confuse Fail2Ban
# and requires restarting fail2ban (done below in the fail2ban # and requires restarting Fail2Ban (done below in the Fail2Ban
# section) and syslog (see #328). There might be other issues, and it's # 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 # not likely the user will want to change this, so we only ask on first
# setup. # setup.
if [ -z "$NONINTERACTIVE" ]; then 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 # If the file is missing or this is the user's first time running
# Mail-in-a-Box setup, run the interactive timezone configuration # Mail-in-a-Box setup, run the interactive timezone configuration
# tool. # tool.
@ -226,7 +225,7 @@ fi
# hardware entropy to get going, by drawing from /dev/random. haveged makes this # hardware entropy to get going, by drawing from /dev/random. haveged makes this
# less likely to stall for very long. # 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 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 # 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. # 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 if [ ! -f /root/.ssh/id_rsa_miab ]; then
echo 'Creating SSH key for backup…' echo 'Creating SSH key for backup…'
ssh-keygen -t rsa -b 2048 -a 100 -f /root/.ssh/id_rsa_miab -N '' -q 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 # settings, find the port it is supposedly running on, and open that port #NODOC
# too. #NODOC # too. #NODOC
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #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 if [ "$SSH_PORT" != "22" ]; then
echo Opening alternate SSH port $SSH_PORT. #NODOC echo "Opening alternate SSH port $SSH_PORT." #NODOC
ufw_allow $SSH_PORT #NODOC ufw_allow "$SSH_PORT" #NODOC
fi fi
fi fi
@ -328,15 +327,14 @@ restart_service resolvconf
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix, ssh, etc. # 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 rm -f /etc/fail2ban/jail.local # we used to use this file but don't anymore
cat conf/fail2ban/jails.conf \ < conf/fail2ban/jails.conf sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
| sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
| sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \ | sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \
> /etc/fail2ban/jail.d/mailinabox.conf > /etc/fail2ban/jail.d/mailinabox.conf
cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/ 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. # 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 # 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 # 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 # scripts will ensure the files exist and then Fail2Ban is given another
# restart at the very end of setup. # restart at the very end of setup.
restart_service fail2ban 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 # Some Ubuntu images start off with Apache. Remove it since we
# will use nginx. Use autoremove to remove any Apache depenencies. # will use nginx. Use autoremove to remove any Apache depenencies.
if [ -f /usr/sbin/apache2 ]; then 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 apache2 apache2-*
hide_output apt-get -y --purge autoremove hide_output apt-get -y --purge autoremove
fi fi
@ -17,7 +17,7 @@ fi
# #
# Turn off nginx's default website. # 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 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. # nginx configuration at /mailinabox-mobileconfig.
mkdir -p /var/lib/mailinabox mkdir -p /var/lib/mailinabox
chmod a+rx /var/lib/mailinabox chmod a+rx /var/lib/mailinabox
cat conf/ios-profile.xml \ < conf/ios-profile.xml sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
| sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
| sed "s/UUID1/$(cat /proc/sys/kernel/random/uuid)/" \ | sed "s/UUID1/$(cat /proc/sys/kernel/random/uuid)/" \
| sed "s/UUID2/$(cat /proc/sys/kernel/random/uuid)/" \ | sed "s/UUID2/$(cat /proc/sys/kernel/random/uuid)/" \
| sed "s/UUID3/$(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: # The format of the file is documented at:
# https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat # https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
# and https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo. # and https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration/FileFormat/HowTo.
cat conf/mozilla-autoconfig.xml \ < conf/mozilla-autoconfig.xml sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
| sed "s/PRIMARY_HOSTNAME/$PRIMARY_HOSTNAME/" \
> /var/lib/mailinabox/mozilla-autoconfig.xml > /var/lib/mailinabox/mozilla-autoconfig.xml
chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml chmod a+r /var/lib/mailinabox/mozilla-autoconfig.xml
# make a default homepage # make a default homepage
if [ -d $STORAGE_ROOT/www/static ]; then mv $STORAGE_ROOT/www/static $STORAGE_ROOT/www/default; fi # migration #NODOC 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 mkdir -p "$STORAGE_ROOT/www/default"
if [ ! -f $STORAGE_ROOT/www/default/index.html ]; then if [ ! -f "$STORAGE_ROOT/www/default/index.html" ]; then
cp conf/www_default.html $STORAGE_ROOT/www/default/index.html cp conf/www_default.html "$STORAGE_ROOT/www/default/index.html"
fi 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 # We previously installed a custom init script to start the PHP FastCGI daemon. #NODOC
# Remove it now that we're using php5-fpm. #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. # 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, # 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. # and then we'll manually install Roundcube from source.
# These dependencies are from `apt-cache showpkg roundcube-core`. # These dependencies are from `apt-cache showpkg roundcube-core`.
echo "Installing Roundcube (webmail)..." echo "Installing Roundcube (webmail)..."
@ -53,7 +53,7 @@ needs_update=0 #NODOC
if [ ! -f /usr/local/lib/roundcubemail/version ]; then if [ ! -f /usr/local/lib/roundcubemail/version ]; then
# not installed yet #NODOC # not installed yet #NODOC
needs_update=1 #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 # checks if the version is what we want
needs_update=1 #NODOC needs_update=1 #NODOC
fi fi
@ -68,10 +68,10 @@ if [ $needs_update == 1 ]; then
mv /usr/local/lib/roundcubemail-$VERSION/ $RCM_DIR mv /usr/local/lib/roundcubemail-$VERSION/ $RCM_DIR
rm -f /tmp/roundcube.tgz 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 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 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 # 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. # Create a configuration file.
# #
# For security, temp and log files are not stored in the default locations # 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. # in normal places.
cat > $RCM_CONFIG <<EOF; cat > $RCM_CONFIG <<EOF;
<?php <?php
@ -158,11 +158,11 @@ cat > ${RCM_PLUGIN_DIR}/carddav/config.inc.php <<EOF;
?> ?>
EOF EOF
# Create writable directories. # Create writeable directories.
mkdir -p /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube 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 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 sudo -u www-data touch /var/log/roundcubemail/errors
# Password changing plugin settings # Password changing plugin settings
@ -184,10 +184,10 @@ usermod -a -G dovecot www-data
# set permissions so that PHP can use users.sqlite # set permissions so that PHP can use users.sqlite
# could use dovecot instead of www-data, but not sure it matters # could use dovecot instead of www-data, but not sure it matters
chown root.www-data $STORAGE_ROOT/mail chown root.www-data "$STORAGE_ROOT/mail"
chmod 775 $STORAGE_ROOT/mail chmod 775 "$STORAGE_ROOT/mail"
chown root.www-data $STORAGE_ROOT/mail/users.sqlite chown root.www-data "$STORAGE_ROOT/mail/users.sqlite"
chmod 664 $STORAGE_ROOT/mail/users.sqlite chmod 664 "$STORAGE_ROOT/mail/users.sqlite"
# Fix Carddav permissions: # Fix Carddav permissions:
chown -f -R root.www-data ${RCM_PLUGIN_DIR}/carddav 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) # Run Roundcube database migration script (database is created if it does not exist)
${RCM_DIR}/bin/updatedb.sh --dir ${RCM_DIR}/SQL --package roundcube ${RCM_DIR}/bin/updatedb.sh --dir ${RCM_DIR}/SQL --package roundcube
chown www-data:www-data $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 chmod 664 "$STORAGE_ROOT/mail/roundcube/roundcube.sqlite"
# Enable PHP modules. # Enable PHP modules.
phpenmod -v php7.0 mcrypt imap phpenmod -v php7.0 mcrypt imap

View File

@ -27,7 +27,7 @@ TARGETHASH=104d44426852429dac8ec2783a4e9ad7752d4682
needs_update=0 #NODOC needs_update=0 #NODOC
if [ ! -f /usr/local/lib/z-push/version ]; then if [ ! -f /usr/local/lib/z-push/version ]; then
needs_update=1 #NODOC 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 # checks if the version
needs_update=1 #NODOC needs_update=1 #NODOC
fi fi

View File

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

View File

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

View File

@ -4,20 +4,20 @@
# instance running here. # instance running here.
# #
# Run this at your own risk. This is for testing & experimentation # 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 source /etc/mailinabox.conf # load global vars
ADMIN=$(./mail.py user admins | head -n 1) ADMIN=$(./mail.py user admins | head -n 1)
test -z "$1" || ADMIN=$1 test -z "$1" || ADMIN=$1
echo I am going to unlock admin features for $ADMIN. 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 "You can provide another user to unlock as the first argument of this script."
echo echo
echo WARNING: you could break mail-in-a-box when fiddling around with Nextcloud\'s admin interface 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 "If in doubt, press CTRL-C to cancel."
echo echo
echo Press enter to continue. echo "Press enter to continue."
read 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."