mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-12 17:07:23 +01:00
Compare commits
3 Commits
v72
...
d0e4671000
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0e4671000 | ||
|
|
d1c63b6517 | ||
|
|
09d1feab80 |
116
CHANGELOG.md
116
CHANGELOG.md
@@ -1,94 +1,6 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Version 72 (June 3, 2025)
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Upgrades
|
|
||||||
|
|
||||||
* Roundcube upgraded to version 1.6.11, fixing a security vulnerability.
|
|
||||||
|
|
||||||
Control Panel
|
|
||||||
|
|
||||||
* A warning during daily tasks related to no TLS certificates being expired is fixed.
|
|
||||||
|
|
||||||
Version 71 (January 4, 2025)
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
(Version 71a was posted on January 6, 2025 and fixes a setup regression.)
|
|
||||||
|
|
||||||
Upgrades
|
|
||||||
|
|
||||||
* Roundcube upgraded to version 1.6.9.
|
|
||||||
* Z-Push upgraded to version 2.7.5.
|
|
||||||
|
|
||||||
Automated Maintenance
|
|
||||||
|
|
||||||
* Daily automated tasks are now run at 1am in the box's timezone and full backups are now restricted to running only on Saturdays and Sundays at that time.
|
|
||||||
* Backups now exclude the owncloud-backup folder so that we're not backing up backups.
|
|
||||||
* Old TLS certificates are now automatically deleted to improve control panel performance.
|
|
||||||
|
|
||||||
Setup
|
|
||||||
|
|
||||||
* Fixed broken setup if SSH was configured to listen on multiple ports.
|
|
||||||
* Ubuntu MOTD advertisements are now disabled.
|
|
||||||
* Fixed missing Roundcube dependency package if NextCloud isn't installed.
|
|
||||||
|
|
||||||
Control Panel
|
|
||||||
|
|
||||||
* Improved status checks for secondary nameservers.
|
|
||||||
* Spamhaus is now queried for the box's IPv6 address also.
|
|
||||||
* DSA and EC private keys are now accepted for TLS certificates.
|
|
||||||
* Timeouts for loading slow control panel pages are reduced.
|
|
||||||
|
|
||||||
And other minor fixes.
|
|
||||||
|
|
||||||
Version 70 (August 15, 2024)
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
* Roundcube is updated to version 1.6.8 fixing security vulnerabilities.
|
|
||||||
|
|
||||||
Version 69 (July 20, 2024)
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Package updates:
|
|
||||||
|
|
||||||
* Nextcloud is updated to 26.0.13.
|
|
||||||
* Z-Push is updated to 2.7.3.
|
|
||||||
|
|
||||||
Other updates:
|
|
||||||
|
|
||||||
* Fixed an error generating the weekly statistics.
|
|
||||||
* Fixed file permissions when setting up Nextcloud.
|
|
||||||
* Added an undocumented option to proxy websockets.
|
|
||||||
* Internal improvements to the code to make it more reliable and readable.
|
|
||||||
|
|
||||||
Version 69a (July 21, 2024) and 69b (July 23, 2024) correct setup failures.
|
|
||||||
|
|
||||||
Version 68 (April 1, 2024)
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Package updates:
|
|
||||||
|
|
||||||
* Roundcube updated to version 1.6.6.
|
|
||||||
* Nextcloud is updated to version 26.0.12.
|
|
||||||
|
|
||||||
Mail:
|
|
||||||
|
|
||||||
* Updated postfix's configuration to guard against SMTP smuggling to the long-term fix (https://www.postfix.org/smtp-smuggling.html).
|
|
||||||
|
|
||||||
Control Panel:
|
|
||||||
|
|
||||||
* Improved reporting of Spamhaus response codes.
|
|
||||||
* Improved detection of SSH port.
|
|
||||||
* Fixed an error if last saved status check results were corrupted.
|
|
||||||
* Other minor fixes.
|
|
||||||
|
|
||||||
Other:
|
|
||||||
|
|
||||||
* fail2ban is updated to see "HTTP/2.0" requests to munin also.
|
|
||||||
* Internal improvements to the code to make it more reliable and readable.
|
|
||||||
|
|
||||||
Version 67 (December 22, 2023)
|
Version 67 (December 22, 2023)
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@@ -115,7 +27,7 @@ Version 64 (September 2, 2023)
|
|||||||
* Fixed backups to work with the latest duplicity package which was not backwards compatible.
|
* Fixed backups to work with the latest duplicity package which was not backwards compatible.
|
||||||
* Fixed setting B2 as a backup target with a slash in the application key.
|
* Fixed setting B2 as a backup target with a slash in the application key.
|
||||||
* Turned off OpenDMARC diagnostic reports sent in response to incoming mail.
|
* Turned off OpenDMARC diagnostic reports sent in response to incoming mail.
|
||||||
* Fixed some crashes when using an unreleased version of Mail-in-a-Box.
|
* Fixed some crashes when using an unrelased version of Mail-in-a-Box.
|
||||||
* Added z-push administration scripts.
|
* Added z-push administration scripts.
|
||||||
|
|
||||||
Version 63 (July 27, 2023)
|
Version 63 (July 27, 2023)
|
||||||
@@ -251,7 +163,7 @@ Other:
|
|||||||
|
|
||||||
* Set systemd journald log retention to 10 days (from no limit) to reduce disk usage.
|
* Set systemd journald log retention to 10 days (from no limit) to reduce disk usage.
|
||||||
* Fixed log processing for submission lines that have a sasl_sender or other extra information.
|
* Fixed log processing for submission lines that have a sasl_sender or other extra information.
|
||||||
* Fix DNS secondary nameserver refresh failure retry period.
|
* Fix DNS secondary nameserver refesh failure retry period.
|
||||||
|
|
||||||
Version 55 (October 18, 2021)
|
Version 55 (October 18, 2021)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@@ -276,7 +188,7 @@ Control panel:
|
|||||||
Other:
|
Other:
|
||||||
|
|
||||||
* Fail2ban's IPv6 support is enabled.
|
* Fail2ban's IPv6 support is enabled.
|
||||||
* The mail log tool now doesn't crash if there are email addresses in log messages with invalid UTF-8 characters.
|
* The mail log tool now doesn't crash if there are email addresess in log messages with invalid UTF-8 characters.
|
||||||
* Additional nsd.conf files can be placed in /etc/nsd.conf.d.
|
* Additional nsd.conf files can be placed in /etc/nsd.conf.d.
|
||||||
|
|
||||||
v0.54 (June 20, 2021)
|
v0.54 (June 20, 2021)
|
||||||
@@ -309,7 +221,7 @@ Setup:
|
|||||||
v0.53a (May 8, 2021)
|
v0.53a (May 8, 2021)
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
The download URL for Z-Push has been revised because the old URL stopped working.
|
The download URL for Z-Push has been revised becaue the old URL stopped working.
|
||||||
|
|
||||||
v0.53 (April 12, 2021)
|
v0.53 (April 12, 2021)
|
||||||
----------------------
|
----------------------
|
||||||
@@ -528,7 +440,7 @@ Changes:
|
|||||||
* Added support for S3-compatible backup services besides Amazon S3.
|
* Added support for S3-compatible backup services besides Amazon S3.
|
||||||
* Fixed the control panel login page to let LastPass save passwords.
|
* Fixed the control panel login page to let LastPass save passwords.
|
||||||
* Fixed an error in the user privileges API.
|
* Fixed an error in the user privileges API.
|
||||||
* Silenced some spurious messages.
|
* Silenced some spurrious messages.
|
||||||
|
|
||||||
Software updates:
|
Software updates:
|
||||||
|
|
||||||
@@ -592,7 +504,7 @@ Setup:
|
|||||||
|
|
||||||
Control Panel:
|
Control Panel:
|
||||||
|
|
||||||
* The users page now documents that passwords should only have ASCII characters to prevent character encoding mismatches between clients and the server.
|
* The users page now documents that passwords should only have ASCII characters to prevent character encoding mismaches between clients and the server.
|
||||||
* The users page no longer shows user mailbox sizes because this was extremely slow for very large mailboxes.
|
* The users page no longer shows user mailbox sizes because this was extremely slow for very large mailboxes.
|
||||||
* The Mail-in-a-Box version is now shown in the system status checks even when the new-version check is disabled.
|
* The Mail-in-a-Box version is now shown in the system status checks even when the new-version check is disabled.
|
||||||
* The alises page now warns that alises should not be used to forward mail off of the box. Mail filters within Roundcube are better for that.
|
* The alises page now warns that alises should not be used to forward mail off of the box. Mail filters within Roundcube are better for that.
|
||||||
@@ -920,7 +832,7 @@ v0.17c (April 1, 2016)
|
|||||||
|
|
||||||
This update addresses some minor security concerns and some installation issues.
|
This update addresses some minor security concerns and some installation issues.
|
||||||
|
|
||||||
ownCloud:
|
ownCoud:
|
||||||
|
|
||||||
* Block web access to the configuration parameters (config.php). There is no immediate impact (see [#776](https://github.com/mail-in-a-box/mailinabox/pull/776)), although advanced users may want to take note.
|
* Block web access to the configuration parameters (config.php). There is no immediate impact (see [#776](https://github.com/mail-in-a-box/mailinabox/pull/776)), although advanced users may want to take note.
|
||||||
|
|
||||||
@@ -936,7 +848,7 @@ Control panel:
|
|||||||
Setup:
|
Setup:
|
||||||
|
|
||||||
* Setup dialogs did not appear correctly when connecting to SSH using Putty on Windows.
|
* Setup dialogs did not appear correctly when connecting to SSH using Putty on Windows.
|
||||||
* We now install Roundcube from our own mirror because Sourceforge's downloads experience frequent intermittent unavailability.
|
* We now install Roundcube from our own mirror because Sourceforge's downloads experience frequent intermittant unavailability.
|
||||||
|
|
||||||
v0.17b (March 1, 2016)
|
v0.17b (March 1, 2016)
|
||||||
----------------------
|
----------------------
|
||||||
@@ -979,7 +891,7 @@ This update primarily adds automatic SSL (now "TLS") certificate provisioning fr
|
|||||||
|
|
||||||
Control Panel:
|
Control Panel:
|
||||||
|
|
||||||
* The SSL certificates (now referred to as "TLS certificates") page now supports provisioning free certificates from Let's Encrypt.
|
* The SSL certificates (now referred to as "TLS ccertificates") page now supports provisioning free certificates from Let's Encrypt.
|
||||||
* Report free memory usage.
|
* Report free memory usage.
|
||||||
* Fix a crash when the git directory is not checked out to a tag.
|
* Fix a crash when the git directory is not checked out to a tag.
|
||||||
* When IPv6 is enabled, check that all domains (besides the system hostname) resolve over IPv6.
|
* When IPv6 is enabled, check that all domains (besides the system hostname) resolve over IPv6.
|
||||||
@@ -1072,7 +984,7 @@ Control panel:
|
|||||||
System:
|
System:
|
||||||
|
|
||||||
* Tweaks to fail2ban settings.
|
* Tweaks to fail2ban settings.
|
||||||
* Fixed a spurious warning while installing munin.
|
* Fixed a spurrious warning while installing munin.
|
||||||
|
|
||||||
v0.13b (August 30, 2015)
|
v0.13b (August 30, 2015)
|
||||||
------------------------
|
------------------------
|
||||||
@@ -1086,7 +998,7 @@ Note: v0.13 (no 'a', August 19, 2015) was pulled immediately due to an ownCloud
|
|||||||
|
|
||||||
Mail:
|
Mail:
|
||||||
|
|
||||||
* Outbound mail headers (the Received: header) are tweaked to possibly improve deliverability.
|
* Outbound mail headers (the Recieved: header) are tweaked to possibly improve deliverability.
|
||||||
* Some MIME messages would hang Roundcube due to a missing package.
|
* Some MIME messages would hang Roundcube due to a missing package.
|
||||||
* The users permitted to send as an alias can now be different from where an alias forwards to.
|
* The users permitted to send as an alias can now be different from where an alias forwards to.
|
||||||
|
|
||||||
@@ -1118,7 +1030,7 @@ v0.12c was posted to work around the current Sourceforge.net outage: pyzor's rem
|
|||||||
v0.12b (July 4, 2015)
|
v0.12b (July 4, 2015)
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
This version corrects a minor regression in v0.12 related to creating aliases targeting multiple addresses.
|
This version corrects a minor regression in v0.12 related to creating aliases targetting multiple addresses.
|
||||||
|
|
||||||
v0.12 (July 3, 2015)
|
v0.12 (July 3, 2015)
|
||||||
--------------------
|
--------------------
|
||||||
@@ -1171,7 +1083,7 @@ Control panel:
|
|||||||
|
|
||||||
System:
|
System:
|
||||||
* The munin system monitoring tool is now installed and accessible at /admin/munin.
|
* The munin system monitoring tool is now installed and accessible at /admin/munin.
|
||||||
* ownCloud updated to version 8.0.4. The ownCloud installation step now is resilient to download problems. The ownCloud configuration file is now stored in STORAGE_ROOT to fix loss of data when moving STORAGE_ROOT to a new machine.
|
* ownCloud updated to version 8.0.4. The ownCloud installation step now is reslient to download problems. The ownCloud configuration file is now stored in STORAGE_ROOT to fix loss of data when moving STORAGE_ROOT to a new machine.
|
||||||
* The setup scripts now run `apt-get update` prior to installing anything to ensure the apt database is in sync with the packages actually available.
|
* The setup scripts now run `apt-get update` prior to installing anything to ensure the apt database is in sync with the packages actually available.
|
||||||
|
|
||||||
|
|
||||||
@@ -1209,7 +1121,7 @@ DNS:
|
|||||||
* Internationalized Domain Names (IDNs) should now work in email. If you had custom DNS or custom web settings for internationalized domains, check that they are still working.
|
* Internationalized Domain Names (IDNs) should now work in email. If you had custom DNS or custom web settings for internationalized domains, check that they are still working.
|
||||||
* It is now possible to set multiple TXT and other types of records on the same domain in the control panel.
|
* It is now possible to set multiple TXT and other types of records on the same domain in the control panel.
|
||||||
* The custom DNS API was completely rewritten to support setting multiple records of the same type on a domain. Any existing client code using the DNS API will have to be rewritten. (Existing code will just get 404s back.)
|
* The custom DNS API was completely rewritten to support setting multiple records of the same type on a domain. Any existing client code using the DNS API will have to be rewritten. (Existing code will just get 404s back.)
|
||||||
* On some systems the `nsd` service failed to start if network interfaces were not ready.
|
* On some systems the `nsd` service failed to start if network inferfaces were not ready.
|
||||||
|
|
||||||
System / Control Panel:
|
System / Control Panel:
|
||||||
|
|
||||||
|
|||||||
@@ -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 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.
|
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.
|
||||||
|
|
||||||
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 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 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 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.
|
||||||
|
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ See the [setup guide](https://mailinabox.email/guide.html) for detailed, user-fr
|
|||||||
|
|
||||||
For experts, start with a completely fresh (really, I mean it) Ubuntu 22.04 LTS 64-bit machine. On the machine...
|
For experts, start with a completely fresh (really, I mean it) Ubuntu 22.04 LTS 64-bit machine. On the machine...
|
||||||
|
|
||||||
Clone this repository and checkout the tag corresponding to the most recent release (which you can find in the tags or releases lists on GitHub):
|
Clone this repository and checkout the tag corresponding to the most recent release:
|
||||||
|
|
||||||
$ git clone https://github.com/mail-in-a-box/mailinabox
|
$ git clone https://github.com/mail-in-a-box/mailinabox
|
||||||
$ cd mailinabox
|
$ cd mailinabox
|
||||||
$ git checkout TAGNAME
|
$ git checkout v67
|
||||||
|
|
||||||
Begin the installation.
|
Begin the installation.
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace inbox {
|
|||||||
|
|
||||||
# dovevot's standard mailboxes configuration file marks two sent folders
|
# dovevot's standard mailboxes configuration file marks two sent folders
|
||||||
# with the \Sent attribute, just in case clients don't agree about which
|
# with the \Sent attribute, just in case clients don't agree about which
|
||||||
# they're using. We'll keep that, plus add Junk as an alternative for Spam.
|
# they're using. We'll keep that, plus add Junk as an alterative for Spam.
|
||||||
# These are not auto-created.
|
# These are not auto-created.
|
||||||
mailbox "Sent Messages" {
|
mailbox "Sent Messages" {
|
||||||
special_use = \Sent
|
special_use = \Sent
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ action = iptables-allports[name=recidive]
|
|||||||
# The last line on the action will sent an email to the configured address. This mail will
|
# The last line on the action will sent an email to the configured address. This mail will
|
||||||
# notify the administrator that someone has been repeatedly triggering one of the other jails.
|
# notify the administrator that someone has been repeatedly triggering one of the other jails.
|
||||||
# By default we don't configure this address and no action is required from the admin anyway.
|
# By default we don't configure this address and no action is required from the admin anyway.
|
||||||
# So the notification is omitted. This will prevent message appearing in the mail.log that mail
|
# So the notification is ommited. This will prevent message appearing in the mail.log that mail
|
||||||
# can't be delivered to fail2ban@$HOSTNAME.
|
# can't be delivered to fail2ban@$HOSTNAME.
|
||||||
|
|
||||||
[postfix-sasl]
|
[postfix-sasl]
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
return 403;
|
return 403;
|
||||||
}
|
}
|
||||||
location ~ /mail/.*\.php {
|
location ~ /mail/.*\.php {
|
||||||
# note: ~ has precedence over a regular location block
|
# note: ~ has precendence over a regular location block
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
fastcgi_split_path_info ^/mail(/.*)()$;
|
fastcgi_split_path_info ^/mail(/.*)()$;
|
||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
location ~ ^(/cloud)((?:/ocs)?/[^/]+\.php)(/.*)?$ {
|
location ~ ^(/cloud)((?:/ocs)?/[^/]+\.php)(/.*)?$ {
|
||||||
# note: ~ has precedence over a regular location block
|
# note: ~ has precendence over a regular location block
|
||||||
# Accept URLs like:
|
# Accept URLs like:
|
||||||
# /cloud/index.php/apps/files/
|
# /cloud/index.php/apps/files/
|
||||||
# /cloud/index.php/apps/files/ajax/scan.php (it's really index.php; see 6fdef379adfdeac86cc2220209bdf4eb9562268d)
|
# /cloud/index.php/apps/files/ajax/scan.php (it's really index.php; see 6fdef379adfdeac86cc2220209bdf4eb9562268d)
|
||||||
|
|||||||
@@ -9,14 +9,13 @@
|
|||||||
|
|
||||||
import os, os.path, re, datetime, sys
|
import os, os.path, re, datetime, sys
|
||||||
import dateutil.parser, dateutil.relativedelta, dateutil.tz
|
import dateutil.parser, dateutil.relativedelta, dateutil.tz
|
||||||
from datetime import date
|
|
||||||
import rtyaml
|
import rtyaml
|
||||||
from exclusiveprocess import Lock
|
from exclusiveprocess import Lock
|
||||||
|
|
||||||
from utils import load_environment, shell, wait_for_service
|
from utils import load_environment, shell, wait_for_service
|
||||||
|
|
||||||
def backup_status(env):
|
def backup_status(env):
|
||||||
# If backups are disabled, return no status.
|
# If backups are dissbled, return no status.
|
||||||
config = get_backup_config(env)
|
config = get_backup_config(env)
|
||||||
if config["target"] == "off":
|
if config["target"] == "off":
|
||||||
return { }
|
return { }
|
||||||
@@ -158,8 +157,6 @@ def should_force_full(config, env):
|
|||||||
# since the last full backup is greater than half the size
|
# since the last full backup is greater than half the size
|
||||||
# of that full backup.
|
# of that full backup.
|
||||||
inc_size = 0
|
inc_size = 0
|
||||||
# Check if day of week is a weekend day
|
|
||||||
weekend = date.today().weekday()>=5
|
|
||||||
for bak in backup_status(env)["backups"]:
|
for bak in backup_status(env)["backups"]:
|
||||||
if not bak["full"]:
|
if not bak["full"]:
|
||||||
# Scan through the incremental backups cumulating
|
# Scan through the incremental backups cumulating
|
||||||
@@ -168,10 +165,8 @@ def should_force_full(config, env):
|
|||||||
else:
|
else:
|
||||||
# ...until we reach the most recent full backup.
|
# ...until we reach the most recent full backup.
|
||||||
# Return if we should to a full backup, which is based
|
# Return if we should to a full backup, which is based
|
||||||
# on whether it is a weekend day, the size of the
|
# on the size of the increments relative to the full
|
||||||
# increments relative to the full backup, as well as
|
# backup, as well as the age of the full backup.
|
||||||
# the age of the full backup.
|
|
||||||
if weekend:
|
|
||||||
if inc_size > .5*bak["size"]:
|
if inc_size > .5*bak["size"]:
|
||||||
return True
|
return True
|
||||||
if dateutil.parser.parse(bak["date"]) + datetime.timedelta(days=config["min_age_in_days"]*10+1) < datetime.datetime.now(dateutil.tz.tzlocal()):
|
if dateutil.parser.parse(bak["date"]) + datetime.timedelta(days=config["min_age_in_days"]*10+1) < datetime.datetime.now(dateutil.tz.tzlocal()):
|
||||||
@@ -325,7 +320,6 @@ def perform_backup(full_backup):
|
|||||||
"--verbosity", "warning", "--no-print-statistics",
|
"--verbosity", "warning", "--no-print-statistics",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--exclude", backup_root,
|
"--exclude", backup_root,
|
||||||
"--exclude", os.path.join(env["STORAGE_ROOT"], "owncloud-backup"),
|
|
||||||
"--volsize", "250",
|
"--volsize", "250",
|
||||||
"--gpg-options", "'--cipher-algo=AES256'",
|
"--gpg-options", "'--cipher-algo=AES256'",
|
||||||
"--allow-source-mismatch",
|
"--allow-source-mismatch",
|
||||||
@@ -405,7 +399,6 @@ def run_duplicity_verification():
|
|||||||
"--compare-data",
|
"--compare-data",
|
||||||
"--archive-dir", backup_cache_dir,
|
"--archive-dir", backup_cache_dir,
|
||||||
"--exclude", backup_root,
|
"--exclude", backup_root,
|
||||||
"--exclude", os.path.join(env["STORAGE_ROOT"], "owncloud-backup"),
|
|
||||||
*get_duplicity_additional_args(env),
|
*get_duplicity_additional_args(env),
|
||||||
get_duplicity_target_url(config),
|
get_duplicity_target_url(config),
|
||||||
env["STORAGE_ROOT"],
|
env["STORAGE_ROOT"],
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import ipaddress
|
|||||||
import rtyaml
|
import rtyaml
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
|
|
||||||
from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains, get_ssh_port
|
from utils import shell, load_env_vars_from_file, safe_domain_name, sort_domains
|
||||||
from ssl_certificates import get_ssl_certificates, check_certificate
|
from ssl_certificates import get_ssl_certificates, check_certificate
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
# From https://stackoverflow.com/questions/3026957/how-to-validate-a-domain-name-using-regex-php/16491074#16491074
|
# From https://stackoverflow.com/questions/3026957/how-to-validate-a-domain-name-using-regex-php/16491074#16491074
|
||||||
# This regular expression matches domain names according to RFCs, it also accepts fqdn with an leading dot,
|
# This regular expression matches domain names according to RFCs, it also accepts fqdn with an leading dot,
|
||||||
# underscores, as well as asterisks which are allowed in domain names but not hostnames (i.e. allowed in
|
# underscores, as well as asteriks which are allowed in domain names but not hostnames (i.e. allowed in
|
||||||
# DNS but not in URLs), which are common in certain record types like for DKIM.
|
# DNS but not in URLs), which are common in certain record types like for DKIM.
|
||||||
DOMAIN_RE = r"^(?!\-)(?:[*][.])?(?:[a-zA-Z\d\-_]{0,62}[a-zA-Z\d_]\.){1,126}(?!\d+)[a-zA-Z\d_]{1,63}(\.?)$"
|
DOMAIN_RE = r"^(?!\-)(?:[*][.])?(?:[a-zA-Z\d\-_]{0,62}[a-zA-Z\d_]\.){1,126}(?!\d+)[a-zA-Z\d_]{1,63}(\.?)$"
|
||||||
|
|
||||||
@@ -443,16 +443,19 @@ def build_sshfp_records():
|
|||||||
|
|
||||||
# Get our local fingerprints by running ssh-keyscan. The output looks
|
# Get our local fingerprints by running ssh-keyscan. The output looks
|
||||||
# like the known_hosts file: hostname, keytype, fingerprint. The order
|
# like the known_hosts file: hostname, keytype, fingerprint. The order
|
||||||
# of the output is arbitrary, so sort it to prevent spurious updates
|
# of the output is arbitrary, so sort it to prevent spurrious updates
|
||||||
# to the zone file (that trigger bumping the serial number). However,
|
# to the zone file (that trigger bumping the serial number). However,
|
||||||
# if SSH has been configured to listen on a nonstandard port, we must
|
# if SSH has been configured to listen on a nonstandard port, we must
|
||||||
# specify that port to sshkeyscan.
|
# specify that port to sshkeyscan.
|
||||||
|
|
||||||
port = get_ssh_port()
|
port = 22
|
||||||
|
with open('/etc/ssh/sshd_config', encoding="utf-8") as f:
|
||||||
# If nothing returned, SSH is probably not installed.
|
for line in f:
|
||||||
if not port:
|
s = line.rstrip().split()
|
||||||
return
|
if len(s) == 2 and s[0] == 'Port':
|
||||||
|
with contextlib.suppress(ValueError):
|
||||||
|
port = int(s[1])
|
||||||
|
break
|
||||||
|
|
||||||
keys = shell("check_output", ["ssh-keyscan", "-4", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"])
|
keys = shell("check_output", ["ssh-keyscan", "-4", "-t", "rsa,dsa,ecdsa,ed25519", "-p", str(port), "localhost"])
|
||||||
keys = sorted(keys.split("\n"))
|
keys = sorted(keys.split("\n"))
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ def scan_mail_log(env):
|
|||||||
|
|
||||||
if collector["other-services"] and VERBOSE and False:
|
if collector["other-services"] and VERBOSE and False:
|
||||||
print_header("Other services")
|
print_header("Other services")
|
||||||
print("The following unknown services were found in the log file.")
|
print("The following unkown services were found in the log file.")
|
||||||
print(" ", *sorted(collector["other-services"]), sep='\n│ ')
|
print(" ", *sorted(collector["other-services"]), sep='\n│ ')
|
||||||
|
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ def print_user_table(users, data=None, sub_data=None, activity=None, latest=None
|
|||||||
data_accum[col] += d[row]
|
data_accum[col] += d[row]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if None not in [latest, earliest]: # noqa PLR6201
|
if None not in {latest, earliest}:
|
||||||
vert_pos = len(line)
|
vert_pos = len(line)
|
||||||
e = earliest[row]
|
e = earliest[row]
|
||||||
l = latest[row]
|
l = latest[row]
|
||||||
@@ -732,7 +732,7 @@ def print_user_table(users, data=None, sub_data=None, activity=None, latest=None
|
|||||||
else:
|
else:
|
||||||
header += l.rjust(max(5, len(l) + 1, col_widths[col]))
|
header += l.rjust(max(5, len(l) + 1, col_widths[col]))
|
||||||
|
|
||||||
if None not in [latest, earliest]: # noqa PLR6201
|
if None not in {latest, earliest}:
|
||||||
header += " │ timespan "
|
header += " │ timespan "
|
||||||
|
|
||||||
lines.insert(0, header.rstrip())
|
lines.insert(0, header.rstrip())
|
||||||
@@ -757,7 +757,7 @@ def print_user_table(users, data=None, sub_data=None, activity=None, latest=None
|
|||||||
footer += temp.format(data_accum[row])
|
footer += temp.format(data_accum[row])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if None not in [latest, earliest]: # noqa PLR6201
|
if None not in {latest, earliest}:
|
||||||
max_l = max(latest)
|
max_l = max(latest)
|
||||||
min_e = min(earliest)
|
min_e = min(earliest)
|
||||||
timespan = relativedelta(max_l, min_e)
|
timespan = relativedelta(max_l, min_e)
|
||||||
|
|||||||
@@ -561,7 +561,7 @@ def kick(env, mail_result=None):
|
|||||||
|
|
||||||
auto_aliases = { }
|
auto_aliases = { }
|
||||||
|
|
||||||
# Map required aliases to the administrator alias (which should be created manually).
|
# Mape required aliases to the administrator alias (which should be created manually).
|
||||||
administrator = get_system_administrator(env)
|
administrator = get_system_administrator(env)
|
||||||
required_aliases = get_required_aliases(env)
|
required_aliases = get_required_aliases(env)
|
||||||
for alias in required_aliases:
|
for alias in required_aliases:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def get_ssl_certificates(env):
|
|||||||
# that the certificates are good for to the best certificate for
|
# that the certificates are good for to the best certificate for
|
||||||
# the domain.
|
# the domain.
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.asymmetric import dsa, rsa, ec
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||||
from cryptography.x509 import Certificate
|
from cryptography.x509 import Certificate
|
||||||
|
|
||||||
# The certificates are all stored here:
|
# The certificates are all stored here:
|
||||||
@@ -59,15 +59,13 @@ def get_ssl_certificates(env):
|
|||||||
# Not a valid PEM format for a PEM type we care about.
|
# Not a valid PEM format for a PEM type we care about.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Is it a private key?
|
||||||
|
if isinstance(pem, RSAPrivateKey):
|
||||||
|
private_keys[pem.public_key().public_numbers()] = { "filename": fn, "key": pem }
|
||||||
|
|
||||||
# Is it a certificate?
|
# Is it a certificate?
|
||||||
if isinstance(pem, Certificate):
|
if isinstance(pem, Certificate):
|
||||||
certificates.append({ "filename": fn, "cert": pem })
|
certificates.append({ "filename": fn, "cert": pem })
|
||||||
# It is a private key
|
|
||||||
elif (isinstance(pem, rsa.RSAPrivateKey)
|
|
||||||
or isinstance(pem, dsa.DSAPrivateKey)
|
|
||||||
or isinstance(pem, ec.EllipticCurvePrivateKey)):
|
|
||||||
private_keys[pem.public_key().public_numbers()] = { "filename": fn, "key": pem }
|
|
||||||
|
|
||||||
|
|
||||||
# Process the certificates.
|
# Process the certificates.
|
||||||
domains = { }
|
domains = { }
|
||||||
@@ -436,7 +434,7 @@ def install_cert(domain, ssl_cert, ssl_chain, env, raw=False):
|
|||||||
cert_status += " " + cert_status_details
|
cert_status += " " + cert_status_details
|
||||||
return cert_status
|
return cert_status
|
||||||
|
|
||||||
# Copy certificate into ssl directory.
|
# Copy certifiate into ssl directory.
|
||||||
install_cert_copy_file(fn, env)
|
install_cert_copy_file(fn, env)
|
||||||
|
|
||||||
# Run post-install steps.
|
# Run post-install steps.
|
||||||
@@ -507,7 +505,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
|
|||||||
# Check that the ssl_certificate & ssl_private_key files are good
|
# Check that the ssl_certificate & ssl_private_key files are good
|
||||||
# for the provided domain.
|
# for the provided domain.
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa, dsa, ec
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||||
from cryptography.x509 import Certificate
|
from cryptography.x509 import Certificate
|
||||||
|
|
||||||
# The ssl_certificate file may contain a chain of certificates. We'll
|
# The ssl_certificate file may contain a chain of certificates. We'll
|
||||||
@@ -541,9 +539,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return (f"The private key file {ssl_private_key} is not a private key file: {e!s}", None)
|
return (f"The private key file {ssl_private_key} is not a private key file: {e!s}", None)
|
||||||
|
|
||||||
if (not isinstance(priv_key, rsa.RSAPrivateKey)
|
if not isinstance(priv_key, RSAPrivateKey):
|
||||||
and not isinstance(priv_key, dsa.DSAPrivateKey)
|
|
||||||
and not isinstance(priv_key, ec.EllipticCurvePrivateKey)):
|
|
||||||
return ("The private key file %s is not a private key file." % ssl_private_key, None)
|
return ("The private key file %s is not a private key file." % ssl_private_key, None)
|
||||||
|
|
||||||
if priv_key.public_key().public_numbers() != cert.public_key().public_numbers():
|
if priv_key.public_key().public_numbers() != cert.public_key().public_numbers():
|
||||||
@@ -643,7 +639,7 @@ def load_pem(pem):
|
|||||||
msg = "File is not a valid PEM-formatted file."
|
msg = "File is not a valid PEM-formatted file."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
pem_type = pem_type.group(1)
|
pem_type = pem_type.group(1)
|
||||||
if pem_type.endswith(b"PRIVATE KEY"):
|
if pem_type in {b"RSA PRIVATE KEY", b"PRIVATE KEY"}:
|
||||||
return serialization.load_pem_private_key(pem, password=None, backend=default_backend())
|
return serialization.load_pem_private_key(pem, password=None, backend=default_backend())
|
||||||
if pem_type == b"CERTIFICATE":
|
if pem_type == b"CERTIFICATE":
|
||||||
return load_pem_x509_certificate(pem, default_backend())
|
return load_pem_x509_certificate(pem, default_backend())
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from web_update import get_web_domains, get_domains_with_a_records
|
|||||||
from ssl_certificates import get_ssl_certificates, get_domain_ssl_files, check_certificate
|
from ssl_certificates import get_ssl_certificates, get_domain_ssl_files, check_certificate
|
||||||
from mailconfig import get_mail_domains, get_mail_aliases
|
from mailconfig import get_mail_domains, get_mail_aliases
|
||||||
|
|
||||||
from utils import shell, sort_domains, load_env_vars_from_file, load_settings, get_ssh_port, get_ssh_config_value
|
from utils import shell, sort_domains, load_env_vars_from_file, load_settings
|
||||||
|
|
||||||
def get_services():
|
def get_services():
|
||||||
return [
|
return [
|
||||||
@@ -65,6 +65,24 @@ def run_checks(rounded_values, env, output, pool, domains_to_check=None):
|
|||||||
run_network_checks(env, output)
|
run_network_checks(env, output)
|
||||||
run_domain_checks(rounded_values, env, output, pool, domains_to_check=domains_to_check)
|
run_domain_checks(rounded_values, env, output, pool, domains_to_check=domains_to_check)
|
||||||
|
|
||||||
|
def get_ssh_port():
|
||||||
|
# Returns ssh port
|
||||||
|
try:
|
||||||
|
output = shell('check_output', ['sshd', '-T'])
|
||||||
|
except FileNotFoundError:
|
||||||
|
# sshd is not installed. That's ok.
|
||||||
|
return None
|
||||||
|
|
||||||
|
returnNext = False
|
||||||
|
for e in output.split():
|
||||||
|
if returnNext:
|
||||||
|
return int(e)
|
||||||
|
if e == "port":
|
||||||
|
returnNext = True
|
||||||
|
|
||||||
|
# Did not find port!
|
||||||
|
return None
|
||||||
|
|
||||||
def run_services_checks(env, output, pool):
|
def run_services_checks(env, output, pool):
|
||||||
# Check that system services are running.
|
# Check that system services are running.
|
||||||
all_running = True
|
all_running = True
|
||||||
@@ -188,15 +206,21 @@ def is_port_allowed(ufw, port):
|
|||||||
return any(re.match(str(port) +"[/ \t].*", item) for item in ufw)
|
return any(re.match(str(port) +"[/ \t].*", item) for item in ufw)
|
||||||
|
|
||||||
def check_ssh_password(env, output):
|
def check_ssh_password(env, output):
|
||||||
config_value = get_ssh_config_value("passwordauthentication")
|
# Check that SSH login with password is disabled. The openssh-server
|
||||||
if config_value:
|
# package may not be installed so check that before trying to access
|
||||||
if config_value == "no":
|
# the configuration file.
|
||||||
output.print_ok("SSH disallows password-based login.")
|
if not os.path.exists("/etc/ssh/sshd_config"):
|
||||||
else:
|
return
|
||||||
|
with open("/etc/ssh/sshd_config", encoding="utf-8") as f:
|
||||||
|
sshd = f.read()
|
||||||
|
if re.search("\nPasswordAuthentication\\s+yes", sshd) \
|
||||||
|
or not re.search("\nPasswordAuthentication\\s+no", sshd):
|
||||||
output.print_error("""The SSH server on this machine permits password-based login. A more secure
|
output.print_error("""The SSH server on this machine permits password-based login. A more secure
|
||||||
way to log in is using a public key. Add your SSH public key to $HOME/.ssh/authorized_keys, check
|
way to log in is using a public key. Add your SSH public key to $HOME/.ssh/authorized_keys, check
|
||||||
that you can log in without a password, set the option 'PasswordAuthentication no' in
|
that you can log in without a password, set the option 'PasswordAuthentication no' in
|
||||||
/etc/ssh/sshd_config, and then restart the openssh via 'sudo service ssh restart'.""")
|
/etc/ssh/sshd_config, and then restart the openssh via 'sudo service ssh restart'.""")
|
||||||
|
else:
|
||||||
|
output.print_ok("SSH disallows password-based login.")
|
||||||
|
|
||||||
def is_reboot_needed_due_to_package_installation():
|
def is_reboot_needed_due_to_package_installation():
|
||||||
return os.path.exists("/var/run/reboot-required")
|
return os.path.exists("/var/run/reboot-required")
|
||||||
@@ -282,45 +306,26 @@ def run_network_checks(env, output):
|
|||||||
# 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.
|
||||||
rev_ip4 = ".".join(reversed(env['PUBLIC_IP'].split('.')))
|
|
||||||
zen = query_dns(rev_ip4+'.zen.spamhaus.org', 'A', nxdomain=None)
|
|
||||||
evaluate_spamhaus_lookup(env['PUBLIC_IP'], 'IPv4', rev_ip4, output, zen)
|
|
||||||
|
|
||||||
if not env['PUBLIC_IPV6']:
|
|
||||||
return
|
|
||||||
|
|
||||||
from ipaddress import IPv6Address
|
|
||||||
|
|
||||||
rev_ip6 = ".".join(reversed(IPv6Address(env['PUBLIC_IPV6']).exploded.split(':')))
|
|
||||||
zen = query_dns(rev_ip6+'.zen.spamhaus.org', 'A', nxdomain=None)
|
|
||||||
evaluate_spamhaus_lookup(env['PUBLIC_IPV6'], 'IPv6', rev_ip6, output, zen)
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate_spamhaus_lookup(lookupaddress, lookuptype, lookupdomain, output, zen):
|
|
||||||
# See https://www.spamhaus.org/news/article/807/using-our-public-mirrors-check-your-return-codes-now. for
|
# See https://www.spamhaus.org/news/article/807/using-our-public-mirrors-check-your-return-codes-now. for
|
||||||
# information on spamhaus return codes
|
# information on spamhaus return codes
|
||||||
|
rev_ip4 = ".".join(reversed(env['PUBLIC_IP'].split('.')))
|
||||||
|
zen = query_dns(rev_ip4+'.zen.spamhaus.org', 'A', nxdomain=None)
|
||||||
if zen is None:
|
if zen is None:
|
||||||
output.print_ok(f"{lookuptype} address is not blacklisted by zen.spamhaus.org.")
|
output.print_ok("IP address is not blacklisted by zen.spamhaus.org.")
|
||||||
elif zen == "[timeout]":
|
elif zen == "[timeout]":
|
||||||
output.print_warning(f"""Connection to zen.spamhaus.org timed out. Could not determine whether this box's
|
output.print_warning("Connection to zen.spamhaus.org timed out. Could not determine whether this box's IP address is blacklisted. Please try again later.")
|
||||||
{lookuptype} address is blacklisted. Please try again later.""")
|
|
||||||
elif zen == "[Not Set]":
|
elif zen == "[Not Set]":
|
||||||
output.print_warning(f"""Could not connect to zen.spamhaus.org. Could not determine whether this box's
|
output.print_warning("Could not connect to zen.spamhaus.org. Could not determine whether this box's IP address is blacklisted. Please try again later.")
|
||||||
{lookuptype} address is blacklisted. Please try again later.""")
|
|
||||||
elif zen == "127.255.255.252":
|
elif zen == "127.255.255.252":
|
||||||
output.print_warning(f"""Incorrect spamhaus query: {lookupdomain + '.zen.spamhaus.org'}. Could not determine whether
|
output.print_warning("Incorrect spamhaus query: %s. Could not determine whether this box's IP address is blacklisted." % (rev_ip4+'.zen.spamhaus.org'))
|
||||||
this box's {lookuptype} address is blacklisted.""")
|
|
||||||
elif zen == "127.255.255.254":
|
elif zen == "127.255.255.254":
|
||||||
output.print_warning(f"""Mail-in-a-Box is configured to use a public DNS server. This is not supported by
|
output.print_warning("Mail-in-a-Box is configured to use a public DNS server. This is not supported by spamhaus. Could not determine whether this box's IP address is blacklisted.")
|
||||||
spamhaus. Could not determine whether this box's {lookuptype} address is blacklisted.""")
|
|
||||||
elif zen == "127.255.255.255":
|
elif zen == "127.255.255.255":
|
||||||
output.print_warning(f"""Too many queries have been performed on the spamhaus server. Could not determine
|
output.print_warning("Too many queries have been performed on the spamhaus server. Could not determine whether this box's IP address is blacklisted.")
|
||||||
whether this box's {lookuptype} address is blacklisted.""")
|
|
||||||
else:
|
else:
|
||||||
output.print_error(f"""The {lookuptype} address of this machine {lookupaddress} is listed in the Spamhaus Block
|
output.print_error("""The IP address of this machine {} is listed in the Spamhaus Block List (code {}),
|
||||||
List (code {zen}), which may prevent recipients from receiving your email. See
|
which may prevent recipients from receiving your email. See http://www.spamhaus.org/query/ip/{}.""".format(env['PUBLIC_IP'], zen, env['PUBLIC_IP']))
|
||||||
http://www.spamhaus.org/query/ip/{lookupaddress}.""")
|
|
||||||
|
|
||||||
|
|
||||||
def run_domain_checks(rounded_time, env, output, pool, domains_to_check=None):
|
def run_domain_checks(rounded_time, env, output, pool, domains_to_check=None):
|
||||||
# Get the list of domains we handle mail for.
|
# Get the list of domains we handle mail for.
|
||||||
@@ -540,8 +545,6 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
|
|||||||
# Check that each custom secondary nameserver resolves the IP address.
|
# Check that each custom secondary nameserver resolves the IP address.
|
||||||
|
|
||||||
if custom_secondary_ns and not probably_external_dns:
|
if custom_secondary_ns and not probably_external_dns:
|
||||||
SOARecord = query_dns(domain, "SOA", at=env['PUBLIC_IP'])# Explicitly ask the local dns server.
|
|
||||||
|
|
||||||
for ns in custom_secondary_ns:
|
for ns in custom_secondary_ns:
|
||||||
# We must first resolve the nameserver to an IP address so we can query it.
|
# We must first resolve the nameserver to an IP address so we can query it.
|
||||||
ns_ips = query_dns(ns, "A")
|
ns_ips = query_dns(ns, "A")
|
||||||
@@ -551,36 +554,15 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
|
|||||||
# Choose the first IP if nameserver returns multiple
|
# Choose the first IP if nameserver returns multiple
|
||||||
ns_ip = ns_ips.split('; ')[0]
|
ns_ip = ns_ips.split('; ')[0]
|
||||||
|
|
||||||
checkSOA = True
|
|
||||||
|
|
||||||
# Now query it to see what it says about this domain.
|
# Now query it to see what it says about this domain.
|
||||||
ip = query_dns(domain, "A", at=ns_ip, nxdomain=None)
|
ip = query_dns(domain, "A", at=ns_ip, nxdomain=None)
|
||||||
if ip == correct_ip:
|
if ip == correct_ip:
|
||||||
output.print_ok(f"Secondary nameserver {ns} resolved the domain correctly.")
|
output.print_ok("Secondary nameserver %s resolved the domain correctly." % ns)
|
||||||
elif ip is None:
|
elif ip is None:
|
||||||
output.print_error(f"Secondary nameserver {ns} is not configured to resolve this domain.")
|
output.print_error("Secondary nameserver %s is not configured to resolve this domain." % ns)
|
||||||
# No need to check SOA record if not configured as nameserver
|
|
||||||
checkSOA = False
|
|
||||||
elif ip == '[timeout]':
|
|
||||||
output.print_error(f"Secondary nameserver {ns} did not resolve this domain, result: {ip}")
|
|
||||||
checkSOA = False
|
|
||||||
else:
|
else:
|
||||||
output.print_error(f"Secondary nameserver {ns} is not configured correctly. (It resolved this domain as {ip}. It should be {correct_ip}.)")
|
output.print_error(f"Secondary nameserver {ns} is not configured correctly. (It resolved this domain as {ip}. It should be {correct_ip}.)")
|
||||||
|
|
||||||
if checkSOA:
|
|
||||||
# Check that secondary DNS server is synchronized with our primary DNS server. Simplified by checking the SOA record which has a version number
|
|
||||||
SOASecondary = query_dns(domain, "SOA", at=ns_ip)
|
|
||||||
|
|
||||||
if SOARecord == SOASecondary:
|
|
||||||
output.print_ok(f"Secondary nameserver {ns} has consistent SOA record.")
|
|
||||||
elif SOARecord == '[Not Set]':
|
|
||||||
output.print_error(f"Secondary nameserver {ns} has no SOA record configured.")
|
|
||||||
elif SOARecord == '[timeout]':
|
|
||||||
output.print_error(f"Secondary nameserver {ns} timed out on checking SOA record.")
|
|
||||||
else:
|
|
||||||
output.print_error(f"""Secondary nameserver {ns} has inconsistent SOA record (primary: {SOARecord} versus secondary: {SOASecondary}).
|
|
||||||
Check that synchronization between secondary and primary DNS servers is properly set-up.""")
|
|
||||||
|
|
||||||
def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domains_with_a_records):
|
def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domains_with_a_records):
|
||||||
# Warn if a custom DNS record is preventing this or the automatic www redirect from
|
# Warn if a custom DNS record is preventing this or the automatic www redirect from
|
||||||
# being served.
|
# being served.
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
filter: invert(100%) hue-rotate(180deg);
|
filter: invert(100%) hue-rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Override Bootstrap theme here to give more contrast. The black turns to white by the filter. */
|
/* Override Boostrap theme here to give more contrast. The black turns to white by the filter. */
|
||||||
.form-control {
|
.form-control {
|
||||||
color: black !important;
|
color: black !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<h4>Automatic configuration</h4>
|
<h4>Automatic configuration</h4>
|
||||||
|
|
||||||
<p>iOS and macOS only: Open <a style="font-weight: bold" href="https://{{hostname}}/mailinabox.mobileconfig">this configuration link</a> on your iOS device or on your Mac desktop to easily set up mail (IMAP/SMTP), Contacts, and Calendar. Your username is your whole email address.</p>
|
<p>iOS and OS X only: Open <a style="font-weight: bold" href="https://{{hostname}}/mailinabox.mobileconfig">this configuration link</a> on your iOS device or on your Mac desktop to easily set up mail (IMAP/SMTP), Contacts, and Calendar. Your username is your whole email address.</p>
|
||||||
|
|
||||||
<h4>Manual configuration</h4>
|
<h4>Manual configuration</h4>
|
||||||
|
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ function init_inputs(target_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return a two-element array of the substring preceding and the substring following
|
// Return a two-element array of the substring preceding and the substring following
|
||||||
// the first occurrence of separator in string. Return [undefined, string] if the
|
// the first occurence of separator in string. Return [undefined, string] if the
|
||||||
// separator does not appear in string.
|
// separator does not appear in string.
|
||||||
const split1_rest = (string, separator) => {
|
const split1_rest = (string, separator) => {
|
||||||
const index = string.indexOf(separator);
|
const index = string.indexOf(separator);
|
||||||
|
|||||||
@@ -179,35 +179,6 @@ def wait_for_service(port, public, env, timeout):
|
|||||||
return False
|
return False
|
||||||
time.sleep(min(timeout/4, 1))
|
time.sleep(min(timeout/4, 1))
|
||||||
|
|
||||||
def get_ssh_port():
|
|
||||||
port_value = get_ssh_config_value("port")
|
|
||||||
|
|
||||||
if port_value:
|
|
||||||
return int(port_value)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_ssh_config_value(parameter_name):
|
|
||||||
# Returns ssh configuration value for the provided parameter
|
|
||||||
import subprocess
|
|
||||||
try:
|
|
||||||
output = shell('check_output', ['sshd', '-T'])
|
|
||||||
except FileNotFoundError:
|
|
||||||
# sshd is not installed. That's ok.
|
|
||||||
return None
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
# error while calling shell command
|
|
||||||
return None
|
|
||||||
|
|
||||||
for line in output.split("\n"):
|
|
||||||
if " " not in line: continue # there's a blank line at the end
|
|
||||||
key, values = line.split(" ", 1)
|
|
||||||
if key == parameter_name:
|
|
||||||
return values # space-delimited if there are multiple values
|
|
||||||
|
|
||||||
# Did not find the parameter!
|
|
||||||
return None
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from web_update import get_web_domains
|
from web_update import get_web_domains
|
||||||
env = load_environment()
|
env = load_environment()
|
||||||
|
|||||||
@@ -166,7 +166,6 @@ def make_domain_config(domain, templates, ssl_certificates, env):
|
|||||||
pass_http_host_header = False
|
pass_http_host_header = False
|
||||||
proxy_redirect_off = False
|
proxy_redirect_off = False
|
||||||
frame_options_header_sameorigin = False
|
frame_options_header_sameorigin = False
|
||||||
web_sockets = False
|
|
||||||
m = re.search("#(.*)$", url)
|
m = re.search("#(.*)$", url)
|
||||||
if m:
|
if m:
|
||||||
for flag in m.group(1).split(","):
|
for flag in m.group(1).split(","):
|
||||||
@@ -176,8 +175,6 @@ def make_domain_config(domain, templates, ssl_certificates, env):
|
|||||||
proxy_redirect_off = True
|
proxy_redirect_off = True
|
||||||
elif flag == "frame-options-sameorigin":
|
elif flag == "frame-options-sameorigin":
|
||||||
frame_options_header_sameorigin = True
|
frame_options_header_sameorigin = True
|
||||||
elif flag == "web-sockets":
|
|
||||||
web_sockets = True
|
|
||||||
url = re.sub("#(.*)$", "", url)
|
url = re.sub("#(.*)$", "", url)
|
||||||
|
|
||||||
nginx_conf_extra += "\tlocation %s {" % path
|
nginx_conf_extra += "\tlocation %s {" % path
|
||||||
@@ -188,10 +185,6 @@ def make_domain_config(domain, templates, ssl_certificates, env):
|
|||||||
nginx_conf_extra += "\n\t\tproxy_set_header Host $http_host;"
|
nginx_conf_extra += "\n\t\tproxy_set_header Host $http_host;"
|
||||||
if frame_options_header_sameorigin:
|
if frame_options_header_sameorigin:
|
||||||
nginx_conf_extra += "\n\t\tproxy_set_header X-Frame-Options SAMEORIGIN;"
|
nginx_conf_extra += "\n\t\tproxy_set_header X-Frame-Options SAMEORIGIN;"
|
||||||
if web_sockets:
|
|
||||||
nginx_conf_extra += "\n\t\tproxy_http_version 1.1;"
|
|
||||||
nginx_conf_extra += "\n\t\tproxy_set_header Upgrade $http_upgrade;"
|
|
||||||
nginx_conf_extra += "\n\t\tproxy_set_header Connection 'Upgrade';"
|
|
||||||
nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"
|
nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"
|
||||||
nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Host $http_host;"
|
nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Host $http_host;"
|
||||||
nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Proto $scheme;"
|
nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Proto $scheme;"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ if [ -z "$TAG" ]; then
|
|||||||
if [ "$UBUNTU_VERSION" == "Ubuntu 22.04 LTS" ]; then
|
if [ "$UBUNTU_VERSION" == "Ubuntu 22.04 LTS" ]; then
|
||||||
# This machine is running Ubuntu 22.04, which is supported by
|
# This machine is running Ubuntu 22.04, which is supported by
|
||||||
# Mail-in-a-Box versions 60 and later.
|
# Mail-in-a-Box versions 60 and later.
|
||||||
TAG=v72
|
TAG=v67
|
||||||
elif [ "$UBUNTU_VERSION" == "Ubuntu 18.04 LTS" ]; then
|
elif [ "$UBUNTU_VERSION" == "Ubuntu 18.04 LTS" ]; then
|
||||||
# This machine is running Ubuntu 18.04, which is supported by
|
# This machine is running Ubuntu 18.04, which is supported by
|
||||||
# Mail-in-a-Box versions 0.40 through 5x.
|
# Mail-in-a-Box versions 0.40 through 5x.
|
||||||
@@ -51,9 +51,9 @@ if [[ $EUID -ne 0 ]]; then
|
|||||||
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 -q -q update
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get -q -q install -y git < /dev/null
|
DEBIAN_FRONTEND=noninteractive apt-get -q -q install -y git < /dev/null
|
||||||
echo
|
echo
|
||||||
@@ -63,25 +63,25 @@ if [ ! -d "$HOME/mailinabox" ]; then
|
|||||||
SOURCE=https://github.com/mail-in-a-box/mailinabox
|
SOURCE=https://github.com/mail-in-a-box/mailinabox
|
||||||
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 \
|
||||||
"$SOURCE" \
|
$SOURCE \
|
||||||
"$HOME/mailinabox" \
|
$HOME/mailinabox \
|
||||||
< /dev/null 2> /dev/null
|
< /dev/null 2> /dev/null
|
||||||
|
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Change directory to it.
|
# Change directory to it.
|
||||||
cd "$HOME/mailinabox" || exit
|
cd $HOME/mailinabox
|
||||||
|
|
||||||
# Update it.
|
# Update it.
|
||||||
if [ "$TAG" != "$(git describe --always)" ]; then
|
if [ "$TAG" != $(git describe --always) ]; 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)?"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -53,12 +53,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" \
|
||||||
@@ -66,7 +66,7 @@ tools/editconf.py /etc/opendmarc.conf -s \
|
|||||||
"FailureReports=false"
|
"FailureReports=false"
|
||||||
|
|
||||||
# SPFIgnoreResults causes the filter to ignore any SPF results in the header
|
# SPFIgnoreResults causes the filter to ignore any SPF results in the header
|
||||||
# of the message. This is useful if you want the filter to perform SPF checks
|
# of the message. This is useful if you want the filter to perfrom SPF checks
|
||||||
# itself, or because you don't trust the arriving header. This added header is
|
# itself, or because you don't trust the arriving header. This added header is
|
||||||
# used by spamassassin to evaluate the mail for spamminess.
|
# used by spamassassin to evaluate the mail for spamminess.
|
||||||
|
|
||||||
|
|||||||
10
setup/dns.sh
10
setup/dns.sh
@@ -101,12 +101,12 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then
|
|||||||
# we're capturing into the `KSK` variable.
|
# we're capturing into the `KSK` variable.
|
||||||
#
|
#
|
||||||
# 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 unnecessary 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.
|
||||||
# (This previously used -b 2048 but it's unclear if this setting makes sense
|
# (This previously used -b 2048 but it's unclear if this setting makes sense
|
||||||
# for non-RSA keys, so it's removed. The RSA-based keys are not recommended
|
# for non-RSA keys, so it's removed. The RSA-based keys are not recommended
|
||||||
# anymore anyway.)
|
# anymore anyway.)
|
||||||
KSK=$(umask 077; cd "$STORAGE_ROOT/dns/dnssec"; ldns-keygen -r /dev/urandom -a $algo -k _domain_);
|
KSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -r /dev/urandom -a $algo -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
|
||||||
@@ -114,7 +114,7 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then
|
|||||||
# disturbing DNS availability.) Omit `-k`.
|
# disturbing DNS availability.) Omit `-k`.
|
||||||
# (This previously used -b 1024 but it's unclear if this setting makes sense
|
# (This previously used -b 1024 but it's unclear if this setting makes sense
|
||||||
# for non-RSA keys, so it's removed.)
|
# for non-RSA keys, so it's removed.)
|
||||||
ZSK=$(umask 077; cd "$STORAGE_ROOT/dns/dnssec"; ldns-keygen -r /dev/urandom -a $algo _domain_);
|
ZSK=$(umask 077; cd $STORAGE_ROOT/dns/dnssec; ldns-keygen -r /dev/urandom -a $algo _domain_);
|
||||||
|
|
||||||
# These generate two sets of files like:
|
# These generate two sets of files like:
|
||||||
#
|
#
|
||||||
@@ -126,7 +126,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
|
||||||
@@ -142,7 +142,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
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/bin/bash
|
|
||||||
# If there aren't any mail users yet, create one.
|
# If there aren't any mail users yet, create one.
|
||||||
if [ -z "$(management/cli.py user)" ]; then
|
if [ -z "$(management/cli.py user)" ]; then
|
||||||
# The output of "management/cli.py user" is a list of mail users. If there
|
# The outut of "management/cli.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.
|
||||||
@@ -11,7 +10,7 @@ if [ -z "$(management/cli.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
|
||||||
@@ -23,7 +22,7 @@ if [ -z "$(management/cli.py user)" ]; then
|
|||||||
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
|
||||||
@@ -48,11 +47,11 @@ if [ -z "$(management/cli.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.
|
||||||
management/cli.py user add "$EMAIL_ADDR" ${EMAIL_PW:+"$EMAIL_PW"}
|
management/cli.py user add $EMAIL_ADDR ${EMAIL_PW:-}
|
||||||
|
|
||||||
# Make it an admin.
|
# Make it an admin.
|
||||||
hide_output management/cli.py user make-admin "$EMAIL_ADDR"
|
hide_output management/cli.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.
|
||||||
management/cli.py alias add "administrator@$PRIMARY_HOSTNAME" "$EMAIL_ADDR" > /dev/null
|
management/cli.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR > /dev/null
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Turn on "strict mode." See http://redsymbol.net/articles/unofficial-bash-strict-mode/.
|
# Turn on "strict mode." See http://redsymbol.net/articles/unofficial-bash-strict-mode/.
|
||||||
# -e: exit if any command unexpectedly fails.
|
# -e: exit if any command unexpectedly fails.
|
||||||
# -u: exit if we have a variable typo.
|
# -u: exit if we have a variable typo.
|
||||||
@@ -17,7 +16,7 @@ function hide_output {
|
|||||||
# Execute command, redirecting stderr/stdout to the temporary file. Since we
|
# Execute command, redirecting stderr/stdout to the temporary file. Since we
|
||||||
# check the return code ourselves, disable 'set -e' temporarily.
|
# check the return code ourselves, disable 'set -e' temporarily.
|
||||||
set +e
|
set +e
|
||||||
"$@" &> "$OUTPUT"
|
"$@" &> $OUTPUT
|
||||||
E=$?
|
E=$?
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -25,15 +24,15 @@ function hide_output {
|
|||||||
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 -----------------------------------------
|
||||||
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 {
|
||||||
@@ -63,9 +62,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +76,7 @@ function get_publicip_from_web_service {
|
|||||||
#
|
#
|
||||||
# Pass '4' or '6' as an argument to this function to specify
|
# Pass '4' or '6' as an argument to this function to specify
|
||||||
# what type of address to get (IPv4, IPv6).
|
# what type of address to get (IPv4, IPv6).
|
||||||
curl -"$1" --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true
|
curl -$1 --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_default_privateip {
|
function get_default_privateip {
|
||||||
@@ -120,19 +119,19 @@ function get_default_privateip {
|
|||||||
if [ "$1" == "6" ]; then target=2001:4860:4860::8888; fi
|
if [ "$1" == "6" ]; then target=2001:4860:4860::8888; fi
|
||||||
|
|
||||||
# Get the route information.
|
# Get the route information.
|
||||||
route=$(ip -"$1" -o route get $target 2>/dev/null | grep -v unreachable)
|
route=$(ip -$1 -o route get $target 2>/dev/null | 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 {
|
||||||
@@ -150,7 +149,7 @@ function ufw_limit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function restart_service {
|
function restart_service {
|
||||||
hide_output service "$1" restart
|
hide_output service $1 restart
|
||||||
}
|
}
|
||||||
|
|
||||||
## Dialog Functions ##
|
## Dialog Functions ##
|
||||||
@@ -179,7 +178,7 @@ function input_menu {
|
|||||||
declare -n result_code=$4_EXITCODE
|
declare -n result_code=$4_EXITCODE
|
||||||
local IFS=^$'\n'
|
local IFS=^$'\n'
|
||||||
set +e
|
set +e
|
||||||
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=$?
|
||||||
set -e
|
set -e
|
||||||
}
|
}
|
||||||
@@ -191,17 +190,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
|
||||||
}
|
}
|
||||||
@@ -217,9 +216,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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="$(($(nproc) * 250))" \
|
default_process_limit=$(echo "$(nproc) * 250" | bc) \
|
||||||
default_vsz_limit="$(($(free -tm | tail -1 | awk '{print $2}') / 3))M" \
|
default_vsz_limit=$(echo "$(free -tm | tail -1 | awk '{print $2}') / 3" | bc)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
|
||||||
|
|
||||||
@@ -152,7 +152,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
|
||||||
|
|
||||||
@@ -201,14 +201,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
|
||||||
|
|||||||
@@ -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 again. Legitimate mail
|
# it is received. Spammers don't usually try agian. 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.
|
||||||
@@ -55,9 +55,9 @@ apt_install postfix postfix-sqlite 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
|
||||||
|
|
||||||
@@ -70,16 +70,10 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
bounce_queue_lifetime=1d
|
bounce_queue_lifetime=1d
|
||||||
|
|
||||||
# Guard against SMTP smuggling
|
# Guard against SMTP smuggling
|
||||||
# This "long-term" fix is recommended at https://www.postfix.org/smtp-smuggling.html.
|
# This short-term workaround is recommended at https://www.postfix.org/smtp-smuggling.html
|
||||||
# This beecame supported in a backported fix in package version 3.6.4-1ubuntu1.3. It is
|
|
||||||
# unnecessary in Postfix 3.9+ where this is the default. The "short-term" workarounds
|
|
||||||
# that we previously had are reverted to postfix defaults (though smtpd_discard_ehlo_keywords
|
|
||||||
# was never included in a released version of Mail-in-a-Box).
|
|
||||||
tools/editconf.py /etc/postfix/main.cf -e \
|
|
||||||
smtpd_data_restrictions= \
|
|
||||||
smtpd_discard_ehlo_keywords=
|
|
||||||
tools/editconf.py /etc/postfix/main.cf \
|
tools/editconf.py /etc/postfix/main.cf \
|
||||||
smtpd_forbid_bare_newline=normalize
|
smtpd_data_restrictions=reject_unauth_pipelining \
|
||||||
|
smtpd_discard_ehlo_keywords="chunking, silent-discard"
|
||||||
|
|
||||||
# ### Outgoing Mail
|
# ### Outgoing Mail
|
||||||
|
|
||||||
@@ -138,9 +132,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 \
|
||||||
tls_medium_cipherlist=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA \
|
tls_medium_cipherlist=ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA \
|
||||||
@@ -172,7 +166,7 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
|
|
||||||
# When connecting to remote SMTP servers, prefer TLS and use DANE if available.
|
# When connecting to remote SMTP servers, prefer TLS and use DANE if available.
|
||||||
#
|
#
|
||||||
# Preferring ("opportunistic") TLS means Postfix will use TLS if the remote end
|
# Prefering ("opportunistic") TLS means Postfix will use TLS if the remote end
|
||||||
# offers it, otherwise it will transmit the message in the clear. Postfix will
|
# offers it, otherwise it will transmit the message in the clear. Postfix will
|
||||||
# accept whatever SSL certificate the remote end provides. Opportunistic TLS
|
# accept whatever SSL certificate the remote end provides. Opportunistic TLS
|
||||||
# protects against passive easvesdropping (but not man-in-the-middle attacks).
|
# protects against passive easvesdropping (but not man-in-the-middle attacks).
|
||||||
@@ -188,7 +182,7 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
# itself but assumes the system's nameserver does and reports DNSSEC status. Thus this also
|
# itself but assumes the system's nameserver does and reports DNSSEC status. Thus this also
|
||||||
# relies on our local DNS server (see system.sh) and `smtp_dns_support_level=dnssec`.
|
# relies on our local DNS server (see system.sh) and `smtp_dns_support_level=dnssec`.
|
||||||
#
|
#
|
||||||
# The `smtp_tls_CAfile` is superfluous, but it eliminates warnings in the logs about untrusted certs,
|
# The `smtp_tls_CAfile` is superflous, but it eliminates warnings in the logs about untrusted certs,
|
||||||
# which we don't care about seeing because Postfix is doing opportunistic TLS anyway. Better to encrypt,
|
# which we don't care about seeing because Postfix is doing opportunistic TLS anyway. Better to encrypt,
|
||||||
# even if we don't know if it's to the right party, than to not encrypt at all. Instead we'll
|
# even if we don't know if it's to the right party, than to not encrypt at all. Instead we'll
|
||||||
# now see notices about trusted certs. The CA file is provided by the package `ca-certificates`.
|
# now see notices about trusted certs. The CA file is provided by the package `ca-certificates`.
|
||||||
@@ -230,7 +224,7 @@ tools/editconf.py /etc/postfix/main.cf -e lmtp_destination_recipient_limit=
|
|||||||
# * `reject_unlisted_recipient`: Although Postfix will reject mail to unknown recipients, it's nicer to reject such mail ahead of greylisting rather than after.
|
# * `reject_unlisted_recipient`: Although Postfix will reject mail to unknown recipients, it's nicer to reject such mail ahead of greylisting rather than after.
|
||||||
# * `check_policy_service`: Apply greylisting using postgrey.
|
# * `check_policy_service`: Apply greylisting using postgrey.
|
||||||
#
|
#
|
||||||
# Note the spamhaus rbl return codes are taken into account as advised here: https://docs.spamhaus.com/datasets/docs/source/40-real-world-usage/PublicMirrors/MTAs/020-Postfix.html
|
# Note the spamhaus rbl return codes are taken into account as adviced here: https://docs.spamhaus.com/datasets/docs/source/40-real-world-usage/PublicMirrors/MTAs/020-Postfix.html
|
||||||
# Notes: #NODOC
|
# Notes: #NODOC
|
||||||
# permit_dnswl_client can pass through mail from whitelisted IP addresses, which would be good to put before greylisting #NODOC
|
# permit_dnswl_client can pass through mail from whitelisted IP addresses, which would be good to put before greylisting #NODOC
|
||||||
# so these IPs get mail delivered quickly. But when an IP is not listed in the permit_dnswl_client list (i.e. it is not #NODOC
|
# so these IPs get mail delivered quickly. But when an IP is not listed in the permit_dnswl_client list (i.e. it is not #NODOC
|
||||||
@@ -247,7 +241,7 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
# other MTA have their own intervals. To fix the problem of receiving
|
# other MTA have their own intervals. To fix the problem of receiving
|
||||||
# e-mails really latter, delay of greylisting has been set to
|
# e-mails really latter, delay of greylisting has been set to
|
||||||
# 180 seconds (default is 300 seconds). We will move the postgrey database
|
# 180 seconds (default is 300 seconds). We will move the postgrey database
|
||||||
# under $STORAGE_ROOT. This prevents a "warming up" that would have occurred
|
# under $STORAGE_ROOT. This prevents a "warming up" that would have occured
|
||||||
# previously with a migrated or reinstalled OS. We will specify this new path
|
# previously with a migrated or reinstalled OS. We will specify this new path
|
||||||
# with the --dbdir=... option. Arguments within POSTGREY_OPTS can not have spaces,
|
# with the --dbdir=... option. Arguments within POSTGREY_OPTS can not have spaces,
|
||||||
# including dbdir. This is due to the way the init script sources the
|
# including dbdir. This is due to the way the init script sources the
|
||||||
@@ -260,17 +254,17 @@ tools/editconf.py /etc/default/postgrey \
|
|||||||
|
|
||||||
|
|
||||||
# If the $STORAGE_ROOT/mail/postgrey is empty, copy the postgrey database over from the old location
|
# If the $STORAGE_ROOT/mail/postgrey is empty, copy the postgrey database over from the old location
|
||||||
if [ ! -d "$STORAGE_ROOT/mail/postgrey/db" ]; then
|
if [ ! -d $STORAGE_ROOT/mail/postgrey/db ]; then
|
||||||
# Stop the service
|
# Stop the service
|
||||||
service postgrey stop
|
service postgrey stop
|
||||||
# Ensure the new paths for postgrey db exists
|
# Ensure the new paths for postgrey db exists
|
||||||
mkdir -p "$STORAGE_ROOT/mail/postgrey/db"
|
mkdir -p $STORAGE_ROOT/mail/postgrey/db
|
||||||
# Move over database files
|
# Move over database files
|
||||||
mv /var/lib/postgrey/* "$STORAGE_ROOT/mail/postgrey/db/" || true
|
mv /var/lib/postgrey/* $STORAGE_ROOT/mail/postgrey/db/ || true
|
||||||
fi
|
fi
|
||||||
# Ensure permissions are set
|
# Ensure permissions are set
|
||||||
chown -R postgrey:postgrey "$STORAGE_ROOT/mail/postgrey/"
|
chown -R postgrey:postgrey $STORAGE_ROOT/mail/postgrey/
|
||||||
chmod 700 "$STORAGE_ROOT/mail/postgrey/"{,db}
|
chmod 700 $STORAGE_ROOT/mail/postgrey/{,db}
|
||||||
|
|
||||||
# We are going to setup a newer whitelist for postgrey, the version included in the distribution is old
|
# We are going to setup a newer whitelist for postgrey, the version included in the distribution is old
|
||||||
cat > /etc/cron.daily/mailinabox-postgrey-whitelist << EOF;
|
cat > /etc/cron.daily/mailinabox-postgrey-whitelist << EOF;
|
||||||
|
|||||||
@@ -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 querying an Sqlite3 database of mail users.
|
# validation by quering 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,12 +18,12 @@ 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;
|
||||||
echo "CREATE TABLE mfa (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type TEXT NOT NULL, secret TEXT NOT NULL, mru_token TEXT, label TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);" | sqlite3 "$db_path";
|
echo "CREATE TABLE mfa (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type TEXT NOT NULL, secret TEXT NOT NULL, mru_token TEXT, label TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);" | sqlite3 $db_path;
|
||||||
echo "CREATE TABLE auto_aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);" | sqlite3 "$db_path";
|
echo "CREATE TABLE auto_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
|
||||||
|
|||||||
@@ -52,9 +52,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
|
||||||
|
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ tr -cd '[:xdigit:]' < /dev/urandom | head -c 32 > /var/lib/mailinabox/api.key
|
|||||||
chmod 640 /var/lib/mailinabox/api.key
|
chmod 640 /var/lib/mailinabox/api.key
|
||||||
|
|
||||||
source $venv/bin/activate
|
source $venv/bin/activate
|
||||||
export PYTHONPATH=$PWD/management
|
export PYTHONPATH=$(pwd)/management
|
||||||
exec gunicorn -b localhost:10222 -w 1 --timeout 630 wsgi:app
|
exec gunicorn -b localhost:10222 -w 1 --timeout 630 wsgi:app
|
||||||
EOF
|
EOF
|
||||||
chmod +x $inst_dir/start
|
chmod +x $inst_dir/start
|
||||||
@@ -116,7 +116,7 @@ minute=$((RANDOM % 60)) # avoid overloading mailinabox.email
|
|||||||
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.
|
||||||
$minute 1 * * * root (cd $PWD && management/daily_tasks.sh)
|
$minute 3 * * * root (cd $(pwd) && management/daily_tasks.sh)
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Start the management server.
|
# Start the management server.
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ def migration_12(env):
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
# Delete all sessions, requiring users to login again to recreate carddav_*
|
# Delete all sessions, requring users to login again to recreate carddav_*
|
||||||
# databases
|
# databases
|
||||||
conn = sqlite3.connect(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite"))
|
conn = sqlite3.connect(os.path.join(env["STORAGE_ROOT"], "mail/roundcube/roundcube.sqlite"))
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
|
|||||||
@@ -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 ! grep -qFx up "/sys/class/net/$IF/operstate" 2>/dev/null; then
|
if ! grep -qFx up /sys/class/net/$IF/operstate 2>/dev/null; then
|
||||||
rm "$f";
|
rm $f;
|
||||||
fi;
|
fi;
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ done
|
|||||||
mkdir -p /var/lib/munin-node/plugin-state/
|
mkdir -p /var/lib/munin-node/plugin-state/
|
||||||
|
|
||||||
# Create a systemd service for munin.
|
# Create a systemd service for munin.
|
||||||
ln -sf "$PWD/management/munin_start.sh" /usr/local/lib/mailinabox/munin_start.sh
|
ln -sf $(pwd)/management/munin_start.sh /usr/local/lib/mailinabox/munin_start.sh
|
||||||
chmod 0744 /usr/local/lib/mailinabox/munin_start.sh
|
chmod 0744 /usr/local/lib/mailinabox/munin_start.sh
|
||||||
cp --remove-destination conf/munin.service /lib/systemd/system/munin.service # target was previously a symlink so remove first
|
cp --remove-destination conf/munin.service /lib/systemd/system/munin.service # target was previously a symlink so remove first
|
||||||
hide_output systemctl link -f /lib/systemd/system/munin.service
|
hide_output systemctl link -f /lib/systemd/system/munin.service
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Install the 'host', 'sed', and and 'nc' tools. This script is run before
|
# Install the 'host', 'sed', and and 'nc' tools. This script is run before
|
||||||
# the rest of the system setup so we may not yet have things installed.
|
# the rest of the system setup so we may not yet have things installed.
|
||||||
apt_get_quiet install bind9-host sed netcat-openbsd
|
apt_get_quiet install bind9-host sed netcat-openbsd
|
||||||
@@ -7,7 +6,7 @@ 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
|
||||||
echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the"
|
echo "The hostname you chose '$PRIMARY_HOSTNAME' is listed in the"
|
||||||
echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/"
|
echo "Spamhaus Domain Block List. See http://www.spamhaus.org/dbl/"
|
||||||
@@ -23,8 +22,8 @@ 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
|
||||||
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."
|
||||||
echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP."
|
echo "See http://www.spamhaus.org/query/ip/$PUBLIC_IP."
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ echo "Installing Nextcloud (contacts/calendar)..."
|
|||||||
# we automatically install intermediate versions as needed.
|
# we automatically install intermediate versions as needed.
|
||||||
# * The hash is the SHA1 hash of the ZIP package, which you can find by just running this script and
|
# * The hash is the SHA1 hash of the ZIP package, which you can find by just running this script and
|
||||||
# copying it from the error message when it doesn't match what is below.
|
# copying it from the error message when it doesn't match what is below.
|
||||||
nextcloud_ver=26.0.13
|
nextcloud_ver=26.0.12
|
||||||
nextcloud_hash=d5c10b650e5396d5045131c6d22c02a90572527c
|
nextcloud_hash=b55e9f51171c0a9b9ab3686cf5c8ad1a4292ca15
|
||||||
|
|
||||||
# Nextcloud apps
|
# Nextcloud apps
|
||||||
# --------------
|
# --------------
|
||||||
@@ -40,12 +40,12 @@ contacts_ver=5.5.3
|
|||||||
contacts_hash=799550f38e46764d90fa32ca1a6535dccd8316e5
|
contacts_hash=799550f38e46764d90fa32ca1a6535dccd8316e5
|
||||||
|
|
||||||
# Always ensure the versions are supported, see https://apps.nextcloud.com/apps/calendar
|
# Always ensure the versions are supported, see https://apps.nextcloud.com/apps/calendar
|
||||||
calendar_ver=4.7.6
|
calendar_ver=4.6.6
|
||||||
calendar_hash=a995bca4effeecb2cab25f3bbeac9bfe05fee766
|
calendar_hash=e34a71669a52d997e319d64a984dcd041389eb22
|
||||||
|
|
||||||
# Always ensure the versions are supported, see https://apps.nextcloud.com/apps/user_external
|
# Always ensure the versions are supported, see https://apps.nextcloud.com/apps/user_external
|
||||||
user_external_ver=3.3.0
|
user_external_ver=3.2.0
|
||||||
user_external_hash=280d24eb2a6cb56b4590af8847f925c28d8d853e
|
user_external_hash=a494073dcdecbbbc79a9c77f72524ac9994d2eec
|
||||||
|
|
||||||
# Developer advice (test plan)
|
# Developer advice (test plan)
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
@@ -65,13 +65,13 @@ user_external_hash=280d24eb2a6cb56b4590af8847f925c28d8d853e
|
|||||||
# Clear prior packages and install dependencies from apt.
|
# Clear prior packages and install dependencies from apt.
|
||||||
apt-get purge -qq -y owncloud* # we used to use the package manager
|
apt-get purge -qq -y owncloud* # we used to use the package manager
|
||||||
|
|
||||||
apt_install curl php"${PHP_VER}" php"${PHP_VER}"-fpm \
|
apt_install curl php${PHP_VER} php${PHP_VER}-fpm \
|
||||||
php"${PHP_VER}"-cli php"${PHP_VER}"-sqlite3 php"${PHP_VER}"-gd php"${PHP_VER}"-imap php"${PHP_VER}"-curl \
|
php${PHP_VER}-cli php${PHP_VER}-sqlite3 php${PHP_VER}-gd php${PHP_VER}-imap php${PHP_VER}-curl \
|
||||||
php"${PHP_VER}"-dev php"${PHP_VER}"-gd php"${PHP_VER}"-xml php"${PHP_VER}"-mbstring php"${PHP_VER}"-zip php"${PHP_VER}"-apcu \
|
php${PHP_VER}-dev php${PHP_VER}-gd php${PHP_VER}-xml php${PHP_VER}-mbstring php${PHP_VER}-zip php${PHP_VER}-apcu \
|
||||||
php"${PHP_VER}"-intl php"${PHP_VER}"-imagick php"${PHP_VER}"-gmp php"${PHP_VER}"-bcmath
|
php${PHP_VER}-intl php${PHP_VER}-imagick php${PHP_VER}-gmp php${PHP_VER}-bcmath
|
||||||
|
|
||||||
# Enable APC before Nextcloud tools are run.
|
# Enable APC before Nextcloud tools are run.
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/mods-available/apcu.ini -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/mods-available/apcu.ini -c ';' \
|
||||||
apc.enabled=1 \
|
apc.enabled=1 \
|
||||||
apc.enable_cli=1
|
apc.enable_cli=1
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ InstallNextcloud() {
|
|||||||
echo
|
echo
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
# Remove the current owncloud/Nextcloud
|
# Remove the current owncloud/Nextcloud
|
||||||
rm -rf /usr/local/lib/owncloud
|
rm -rf /usr/local/lib/owncloud
|
||||||
@@ -105,18 +105,18 @@ InstallNextcloud() {
|
|||||||
# 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-releases/contacts/archive/refs/tags/v$version_contacts.tar.gz" "$hash_contacts" /tmp/contacts.tgz
|
wget_verify https://github.com/nextcloud-releases/contacts/archive/refs/tags/v$version_contacts.tar.gz $hash_contacts /tmp/contacts.tgz
|
||||||
tar xf /tmp/contacts.tgz -C /usr/local/lib/owncloud/apps/
|
tar xf /tmp/contacts.tgz -C /usr/local/lib/owncloud/apps/
|
||||||
rm /tmp/contacts.tgz
|
rm /tmp/contacts.tgz
|
||||||
|
|
||||||
wget_verify "https://github.com/nextcloud-releases/calendar/archive/refs/tags/v$version_calendar.tar.gz" "$hash_calendar" /tmp/calendar.tgz
|
wget_verify https://github.com/nextcloud-releases/calendar/archive/refs/tags/v$version_calendar.tar.gz $hash_calendar /tmp/calendar.tgz
|
||||||
tar xf /tmp/calendar.tgz -C /usr/local/lib/owncloud/apps/
|
tar xf /tmp/calendar.tgz -C /usr/local/lib/owncloud/apps/
|
||||||
rm /tmp/calendar.tgz
|
rm /tmp/calendar.tgz
|
||||||
|
|
||||||
# Starting with Nextcloud 15, the app user_external is no longer included in Nextcloud core,
|
# Starting with Nextcloud 15, the app user_external is no longer included in Nextcloud core,
|
||||||
# we will install from their github repository.
|
# we will install from their github repository.
|
||||||
if [ -n "$version_user_external" ]; then
|
if [ -n "$version_user_external" ]; then
|
||||||
wget_verify "https://github.com/nextcloud-releases/user_external/releases/download/v$version_user_external/user_external-v$version_user_external.tar.gz" "$hash_user_external" /tmp/user_external.tgz
|
wget_verify https://github.com/nextcloud-releases/user_external/releases/download/v$version_user_external/user_external-v$version_user_external.tar.gz $hash_user_external /tmp/user_external.tgz
|
||||||
tar -xf /tmp/user_external.tgz -C /usr/local/lib/owncloud/apps/
|
tar -xf /tmp/user_external.tgz -C /usr/local/lib/owncloud/apps/
|
||||||
rm /tmp/user_external.tgz
|
rm /tmp/user_external.tgz
|
||||||
fi
|
fi
|
||||||
@@ -126,35 +126,33 @@ 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 || /bin/true
|
chown -f -R www-data:www-data $STORAGE_ROOT/owncloud /usr/local/lib/owncloud || /bin/true
|
||||||
|
|
||||||
# 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"$PHP_VER" /usr/local/lib/owncloud/occ upgrade
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ upgrade
|
||||||
E=$?
|
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then
|
||||||
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"$PHP_VER" /usr/local/lib/owncloud/occ upgrade
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ upgrade
|
||||||
E=$?
|
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi
|
||||||
if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ maintenance:mode --off
|
||||||
sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ maintenance:mode --off
|
|
||||||
echo "...which seemed to work."
|
echo "...which seemed to work."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add missing indices. NextCloud didn't include this in the normal upgrade because it might take some time.
|
# Add missing indices. NextCloud didn't include this in the normal upgrade because it might take some time.
|
||||||
sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ db:add-missing-indices
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-indices
|
||||||
sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ db:add-missing-primary-keys
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:add-missing-primary-keys
|
||||||
|
|
||||||
# Run conversion to BigInt identifiers, this process may take some time on large tables.
|
# Run conversion to BigInt identifiers, this process may take some time on large tables.
|
||||||
sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ db:convert-filecache-bigint --no-interaction
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +164,7 @@ InstallNextcloud() {
|
|||||||
|
|
||||||
# If config.php exists, get version number, otherwise CURRENT_NEXTCLOUD_VER is empty.
|
# If config.php exists, get version number, otherwise CURRENT_NEXTCLOUD_VER is empty.
|
||||||
if [ -f "$STORAGE_ROOT/owncloud/config.php" ]; then
|
if [ -f "$STORAGE_ROOT/owncloud/config.php" ]; then
|
||||||
CURRENT_NEXTCLOUD_VER=$(php"$PHP_VER" -r "include(\"$STORAGE_ROOT/owncloud/config.php\"); echo(\$CONFIG['version']);")
|
CURRENT_NEXTCLOUD_VER=$(php$PHP_VER -r "include(\"$STORAGE_ROOT/owncloud/config.php\"); echo(\$CONFIG['version']);")
|
||||||
else
|
else
|
||||||
CURRENT_NEXTCLOUD_VER=""
|
CURRENT_NEXTCLOUD_VER=""
|
||||||
fi
|
fi
|
||||||
@@ -176,7 +174,7 @@ fi
|
|||||||
if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextcloud_ver ]]; then
|
if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextcloud_ver ]]; then
|
||||||
|
|
||||||
# Stop php-fpm if running. If they are not running (which happens on a previously failed install), dont bail.
|
# Stop php-fpm if running. If they are not running (which happens on a previously failed install), dont bail.
|
||||||
service php"$PHP_VER"-fpm stop &> /dev/null || /bin/true
|
service php$PHP_VER-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
|
||||||
@@ -186,21 +184,21 @@ if [ ! -d /usr/local/lib/owncloud/ ] || [[ ! ${CURRENT_NEXTCLOUD_VER} =~ ^$nextc
|
|||||||
echo "Upgrading Nextcloud --- backing up existing installation, configuration, and database to directory to $BACKUP_DIRECTORY..."
|
echo "Upgrading Nextcloud --- 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 "$STORAGE_ROOT/owncloud/owncloud.db" ]; then
|
if [ -e $STORAGE_ROOT/owncloud/owncloud.db ]; then
|
||||||
cp "$STORAGE_ROOT/owncloud/owncloud.db" "$BACKUP_DIRECTORY"
|
cp $STORAGE_ROOT/owncloud/owncloud.db $BACKUP_DIRECTORY
|
||||||
fi
|
fi
|
||||||
if [ -e "$STORAGE_ROOT/owncloud/config.php" ]; then
|
if [ -e $STORAGE_ROOT/owncloud/config.php ]; then
|
||||||
cp "$STORAGE_ROOT/owncloud/config.php" "$BACKUP_DIRECTORY"
|
cp $STORAGE_ROOT/owncloud/config.php $BACKUP_DIRECTORY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If ownCloud or Nextcloud was previously installed....
|
# If ownCloud or Nextcloud was previously installed....
|
||||||
if [ -n "${CURRENT_NEXTCLOUD_VER}" ]; then
|
if [ ! -z ${CURRENT_NEXTCLOUD_VER} ]; then
|
||||||
# Database migrations from ownCloud are no longer possible because ownCloud cannot be run under
|
# Database migrations from ownCloud are no longer possible because ownCloud cannot be run under
|
||||||
# PHP 7.
|
# PHP 7.
|
||||||
|
|
||||||
if [ -e "$STORAGE_ROOT/owncloud/config.php" ]; then
|
if [ -e $STORAGE_ROOT/owncloud/config.php ]; then
|
||||||
# Remove the read-onlyness of the config, which is needed for migrations, especially for v24
|
# Remove the read-onlyness of the config, which is needed for migrations, especially for v24
|
||||||
sed -i -e '/config_is_read_only/d' "$STORAGE_ROOT/owncloud/config.php"
|
sed -i -e '/config_is_read_only/d' $STORAGE_ROOT/owncloud/config.php
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^[89] ]]; then
|
if [[ ${CURRENT_NEXTCLOUD_VER} =~ ^[89] ]]; then
|
||||||
@@ -248,13 +246,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',
|
||||||
@@ -274,6 +272,15 @@ if [ ! -f "$STORAGE_ROOT/owncloud/owncloud.db" ]; then
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
'memcache.local' => '\OC\Memcache\APCu',
|
'memcache.local' => '\OC\Memcache\APCu',
|
||||||
|
'mail_smtpmode' => 'sendmail',
|
||||||
|
'mail_smtpsecure' => '',
|
||||||
|
'mail_smtpauthtype' => 'LOGIN',
|
||||||
|
'mail_smtpauth' => false,
|
||||||
|
'mail_smtphost' => '',
|
||||||
|
'mail_smtpport' => '',
|
||||||
|
'mail_smtpname' => '',
|
||||||
|
'mail_smtppassword' => '',
|
||||||
|
'mail_from_address' => 'owncloud',
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
EOF
|
EOF
|
||||||
@@ -298,12 +305,12 @@ 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
|
||||||
# settings and deletes the autoconfig.php file.
|
# settings and deletes the autoconfig.php file.
|
||||||
(cd /usr/local/lib/owncloud || exit; sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/index.php;)
|
(cd /usr/local/lib/owncloud; sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/index.php;)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update config.php.
|
# Update config.php.
|
||||||
@@ -319,7 +326,7 @@ fi
|
|||||||
# 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"$PHP_VER" <<EOF > "$CONFIG_TEMP" && mv "$CONFIG_TEMP" "$STORAGE_ROOT/owncloud/config.php";
|
php$PHP_VER <<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");
|
||||||
|
|
||||||
@@ -329,10 +336,13 @@ include("$STORAGE_ROOT/owncloud/config.php");
|
|||||||
|
|
||||||
\$CONFIG['memcache.local'] = '\OC\Memcache\APCu';
|
\$CONFIG['memcache.local'] = '\OC\Memcache\APCu';
|
||||||
\$CONFIG['overwrite.cli.url'] = 'https://${PRIMARY_HOSTNAME}/cloud';
|
\$CONFIG['overwrite.cli.url'] = 'https://${PRIMARY_HOSTNAME}/cloud';
|
||||||
|
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches our master administrator address
|
||||||
|
|
||||||
\$CONFIG['logtimezone'] = '$TIMEZONE';
|
\$CONFIG['logtimezone'] = '$TIMEZONE';
|
||||||
\$CONFIG['logdateformat'] = 'Y-m-d H:i:s';
|
\$CONFIG['logdateformat'] = 'Y-m-d H:i:s';
|
||||||
|
|
||||||
|
\$CONFIG['mail_domain'] = '$PRIMARY_HOSTNAME';
|
||||||
|
|
||||||
\$CONFIG['user_backends'] = array(
|
\$CONFIG['user_backends'] = array(
|
||||||
array(
|
array(
|
||||||
'class' => '\OCA\UserExternal\IMAP',
|
'class' => '\OCA\UserExternal\IMAP',
|
||||||
@@ -342,47 +352,36 @@ include("$STORAGE_ROOT/owncloud/config.php");
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
\$CONFIG['mail_domain'] = '$PRIMARY_HOSTNAME';
|
|
||||||
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches the required administrator alias on mail_domain/$PRIMARY_HOSTNAME
|
|
||||||
\$CONFIG['mail_smtpmode'] = 'sendmail';
|
|
||||||
\$CONFIG['mail_smtpauth'] = true; # if smtpmode is smtp
|
|
||||||
\$CONFIG['mail_smtphost'] = '127.0.0.1'; # if smtpmode is smtp
|
|
||||||
\$CONFIG['mail_smtpport'] = '587'; # if smtpmode is smtp
|
|
||||||
\$CONFIG['mail_smtpsecure'] = ''; # if smtpmode is smtp, must be empty string
|
|
||||||
\$CONFIG['mail_smtpname'] = ''; # if smtpmode is smtp, set this to a mail user
|
|
||||||
\$CONFIG['mail_smtppassword'] = ''; # if smtpmode is smtp, set this to the user's password
|
|
||||||
|
|
||||||
echo "<?php\n\\\$CONFIG = ";
|
echo "<?php\n\\\$CONFIG = ";
|
||||||
var_export(\$CONFIG);
|
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.
|
||||||
# user_external is what allows Nextcloud to use IMAP for login. The contacts
|
# user_external is what allows Nextcloud to use IMAP for login. The contacts
|
||||||
# and calendar apps are the extensions we really care about here.
|
# and calendar apps are the extensions we really care about here.
|
||||||
hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:disable firstrunwizard
|
hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:disable firstrunwizard
|
||||||
hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:enable user_external
|
hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:enable user_external
|
||||||
hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:enable contacts
|
hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:enable contacts
|
||||||
hide_output sudo -u www-data php"$PHP_VER" /usr/local/lib/owncloud/console.php app:enable calendar
|
hide_output sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/console.php app:enable calendar
|
||||||
|
|
||||||
# When upgrading, run the upgrade script again now that apps are enabled. It seems like
|
# When upgrading, run the upgrade script again now that apps are enabled. It seems like
|
||||||
# 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"$PHP_VER" /usr/local/lib/owncloud/occ upgrade
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ upgrade
|
||||||
E=$?
|
if [ \( $? -ne 0 \) -a \( $? -ne 3 \) ]; then exit 1; fi
|
||||||
if [ $E -ne 0 ] && [ $E -ne 3 ]; then exit 1; fi
|
|
||||||
|
|
||||||
# Disable default apps that we don't support
|
# Disable default apps that we don't support
|
||||||
sudo -u www-data \
|
sudo -u www-data \
|
||||||
php"$PHP_VER" /usr/local/lib/owncloud/occ app:disable photos dashboard activity \
|
php$PHP_VER /usr/local/lib/owncloud/occ app:disable photos dashboard activity \
|
||||||
| (grep -v "No such app enabled" || /bin/true)
|
| (grep -v "No such app enabled" || /bin/true)
|
||||||
|
|
||||||
# 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)
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \
|
||||||
upload_max_filesize=16G \
|
upload_max_filesize=16G \
|
||||||
post_max_size=16G \
|
post_max_size=16G \
|
||||||
output_buffering=16384 \
|
output_buffering=16384 \
|
||||||
@@ -391,7 +390,7 @@ tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \
|
|||||||
short_open_tag=On
|
short_open_tag=On
|
||||||
|
|
||||||
# Set Nextcloud recommended opcache settings
|
# Set Nextcloud recommended opcache settings
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/cli/conf.d/10-opcache.ini -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/cli/conf.d/10-opcache.ini -c ';' \
|
||||||
opcache.enable=1 \
|
opcache.enable=1 \
|
||||||
opcache.enable_cli=1 \
|
opcache.enable_cli=1 \
|
||||||
opcache.interned_strings_buffer=8 \
|
opcache.interned_strings_buffer=8 \
|
||||||
@@ -405,7 +404,7 @@ tools/editconf.py /etc/php/"$PHP_VER"/cli/conf.d/10-opcache.ini -c ';' \
|
|||||||
# This version was probably in use in Mail-in-a-Box v0.41 (February 26, 2019) and earlier.
|
# This version was probably in use in Mail-in-a-Box v0.41 (February 26, 2019) and earlier.
|
||||||
# We moved to v0.6.3 in 193763f8. Ignore errors - maybe there are duplicated users with the
|
# We moved to v0.6.3 in 193763f8. Ignore errors - maybe there are duplicated users with the
|
||||||
# correct backend already.
|
# correct backend already.
|
||||||
sqlite3 "$STORAGE_ROOT/owncloud/owncloud.db" "UPDATE oc_users_external SET backend='127.0.0.1';" || /bin/true
|
sqlite3 $STORAGE_ROOT/owncloud/owncloud.db "UPDATE oc_users_external SET backend='127.0.0.1';" || /bin/true
|
||||||
|
|
||||||
# Set up a general cron job for Nextcloud.
|
# Set up a general cron job for Nextcloud.
|
||||||
# Also add another job for Calendar updates, per advice in the Nextcloud docs
|
# Also add another job for Calendar updates, per advice in the Nextcloud docs
|
||||||
@@ -420,11 +419,11 @@ chmod +x /etc/cron.d/mailinabox-nextcloud
|
|||||||
|
|
||||||
# We also need to change the sending mode from background-job to occ.
|
# We also need to change the sending mode from background-job to occ.
|
||||||
# Or else the reminders will just be sent as soon as possible when the background jobs run.
|
# Or else the reminders will just be sent as soon as possible when the background jobs run.
|
||||||
hide_output sudo -u www-data php"$PHP_VER" -f /usr/local/lib/owncloud/occ config:app:set dav sendEventRemindersMode --value occ
|
hide_output sudo -u www-data php$PHP_VER -f /usr/local/lib/owncloud/occ config:app:set dav sendEventRemindersMode --value occ
|
||||||
|
|
||||||
# Now set the config to read-only.
|
# Now set the config to read-only.
|
||||||
# Do this only at the very bottom when no further occ commands are needed.
|
# Do this only at the very bottom when no further occ commands are needed.
|
||||||
sed -i'' "s/'config_is_read_only'\s*=>\s*false/'config_is_read_only' => true/" "$STORAGE_ROOT/owncloud/config.php"
|
sed -i'' "s/'config_is_read_only'\s*=>\s*false/'config_is_read_only' => true/" $STORAGE_ROOT/owncloud/config.php
|
||||||
|
|
||||||
# Rotate the nextcloud.log file
|
# Rotate the nextcloud.log file
|
||||||
cat > /etc/logrotate.d/nextcloud <<EOF
|
cat > /etc/logrotate.d/nextcloud <<EOF
|
||||||
@@ -449,4 +448,4 @@ EOF
|
|||||||
# ```
|
# ```
|
||||||
|
|
||||||
# Enable PHP modules and restart PHP.
|
# Enable PHP modules and restart PHP.
|
||||||
restart_service php"$PHP_VER"-fpm
|
restart_service php$PHP_VER-fpm
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/bin/bash
|
|
||||||
if [ -z "${NONINTERACTIVE:-}" ]; then
|
if [ -z "${NONINTERACTIVE:-}" ]; then
|
||||||
# Install 'dialog' so we can ask the user questions. The original motivation for
|
# Install 'dialog' so we can ask the user questions. The original motivation for
|
||||||
# this was being able to ask the user for input even if stdin has been redirected,
|
# this was being able to ask the user for input even if stdin has been redirected,
|
||||||
@@ -8,7 +7,7 @@ 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
|
apt-get -q -q update
|
||||||
apt_get_quiet install dialog python3 python3-pip || exit 1
|
apt_get_quiet install dialog python3 python3-pip || exit 1
|
||||||
fi
|
fi
|
||||||
@@ -32,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=$(get_default_hostname | sed -e 's/^box\.//')
|
DEFAULT_DOMAIN_GUESS=$(echo $(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.
|
||||||
@@ -56,7 +55,7 @@ you really want.
|
|||||||
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
|
||||||
@@ -66,7 +65,7 @@ you really want.
|
|||||||
|
|
||||||
# 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" \
|
||||||
@@ -75,7 +74,7 @@ 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
|
||||||
|
|
||||||
if [ -z "$PRIMARY_HOSTNAME" ]; then
|
if [ -z "$PRIMARY_HOSTNAME" ]; then
|
||||||
@@ -93,7 +92,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:-}" && -n "$GUESSED_IP" ]]; then
|
if [[ -z "${DEFAULT_PUBLIC_IP:-}" && ! -z "$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.
|
||||||
@@ -110,7 +109,7 @@ 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
|
||||||
@@ -126,7 +125,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:-}" && -n "$GUESSED_IP" ]]; then
|
if [[ -z "${DEFAULT_PUBLIC_IPV6:-}" && ! -z "$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
|
||||||
@@ -142,10 +141,10 @@ 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 [ ! -n "$PUBLIC_IPV6_EXITCODE" ]; then
|
if [ ! $PUBLIC_IPV6_EXITCODE ]; then
|
||||||
# user hit ESC/cancel
|
# user hit ESC/cancel
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
@@ -163,7 +162,7 @@ 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 interface"
|
echo "I could not determine the IP or IPv6 address of the network inteface"
|
||||||
echo "for connecting to the Internet. Setup must stop."
|
echo "for connecting to the Internet. Setup must stop."
|
||||||
echo
|
echo
|
||||||
hostname -I
|
hostname -I
|
||||||
@@ -198,7 +197,7 @@ fi
|
|||||||
echo
|
echo
|
||||||
echo "Primary Hostname: $PRIMARY_HOSTNAME"
|
echo "Primary Hostname: $PRIMARY_HOSTNAME"
|
||||||
echo "Public IP Address: $PUBLIC_IP"
|
echo "Public IP Address: $PUBLIC_IP"
|
||||||
if [ -n "$PUBLIC_IPV6" ]; then
|
if [ ! -z "$PUBLIC_IPV6" ]; then
|
||||||
echo "Public IPv6 Address: $PUBLIC_IPV6"
|
echo "Public IPv6 Address: $PUBLIC_IPV6"
|
||||||
fi
|
fi
|
||||||
if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then
|
if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then
|
||||||
@@ -208,6 +207,6 @@ if [ "$PRIVATE_IPV6" != "$PUBLIC_IPV6" ]; then
|
|||||||
echo "Private IPv6 Address: $PRIVATE_IPV6"
|
echo "Private IPv6 Address: $PRIVATE_IPV6"
|
||||||
fi
|
fi
|
||||||
if [ -f /usr/bin/git ] && [ -d .git ]; then
|
if [ -f /usr/bin/git ] && [ -d .git ]; then
|
||||||
echo "Mail-in-a-Box Version: $(git describe --always)"
|
echo "Mail-in-a-Box Version: " $(git describe --always)
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ tools/editconf.py /etc/default/spampd \
|
|||||||
|
|
||||||
# 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 opening a message with embedded malware.
|
# from accidentally openening 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
|
||||||
@@ -135,11 +135,11 @@ EOF
|
|||||||
# 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
|
||||||
@@ -184,8 +184,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/
|
||||||
|
|||||||
33
setup/ssl.sh
33
setup/ssl.sh
@@ -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,48 +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
|
||||||
|
|
||||||
# Cleanup expired SSL certificates from $STORAGE_ROOT/ssl daily
|
|
||||||
cat > /etc/cron.daily/mailinabox-ssl-cleanup << EOF;
|
|
||||||
#!/bin/bash
|
|
||||||
# Mail-in-a-Box
|
|
||||||
# Cleanup expired SSL certificates
|
|
||||||
$(pwd)/tools/ssl_cleanup
|
|
||||||
EOF
|
|
||||||
chmod +x /etc/cron.daily/mailinabox-ssl-cleanup
|
|
||||||
|
|||||||
@@ -46,7 +46,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
|
||||||
@@ -75,17 +75,17 @@ 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
|
||||||
f=$STORAGE_ROOT
|
f=$STORAGE_ROOT
|
||||||
while [[ $f != / ]]; do chmod a+rx "$f"; f=$(dirname "$f"); done;
|
while [[ $f != / ]]; do chmod a+rx "$f"; f=$(dirname "$f"); done;
|
||||||
if [ ! -f "$STORAGE_ROOT/mailinabox.version" ]; then
|
if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then
|
||||||
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
|
||||||
|
|
||||||
# Save the global options in /etc/mailinabox.conf so that standalone
|
# Save the global options in /etc/mailinabox.conf so that standalone
|
||||||
@@ -122,7 +122,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
|
||||||
|
|
||||||
@@ -142,41 +142,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 --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=//i"
|
| sed "s/SHA256 Fingerprint=//i"
|
||||||
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=//i"
|
| sed "s/SHA256 Fingerprint=//i"
|
||||||
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
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#!/bin/bash
|
|
||||||
source /etc/mailinabox.conf
|
source /etc/mailinabox.conf
|
||||||
source setup/functions.sh # load our functions
|
source setup/functions.sh # load our functions
|
||||||
|
|
||||||
@@ -12,8 +11,8 @@ 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
|
echo $PRIMARY_HOSTNAME > /etc/hostname
|
||||||
hostname "$PRIMARY_HOSTNAME"
|
hostname $PRIMARY_HOSTNAME
|
||||||
|
|
||||||
# ### Fix permissions
|
# ### Fix permissions
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ chmod g-w /etc /etc/default /usr
|
|||||||
# - Check if the user intents to activate swap on next boot by checking fstab entries.
|
# - Check if the user intents to activate swap on next boot by checking fstab entries.
|
||||||
# - Check if a swapfile already exists
|
# - Check if a swapfile already exists
|
||||||
# - Check if the root file system is not btrfs, might be an incompatible version with
|
# - Check if the root file system is not btrfs, might be an incompatible version with
|
||||||
# swapfiles. User should handle it them selves.
|
# swapfiles. User should hanle it them selves.
|
||||||
# - Check the memory requirements
|
# - Check the memory requirements
|
||||||
# - Check available diskspace
|
# - Check available diskspace
|
||||||
|
|
||||||
@@ -54,14 +53,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 chunks
|
# Allocate and activate the swap file. Allocate in 1KB chuncks
|
||||||
# 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
|
||||||
@@ -83,15 +82,6 @@ fi
|
|||||||
# (See https://discourse.mailinabox.email/t/journalctl-reclaim-space-on-small-mailinabox/6728/11.)
|
# (See https://discourse.mailinabox.email/t/journalctl-reclaim-space-on-small-mailinabox/6728/11.)
|
||||||
tools/editconf.py /etc/systemd/journald.conf MaxRetentionSec=10day
|
tools/editconf.py /etc/systemd/journald.conf MaxRetentionSec=10day
|
||||||
|
|
||||||
# ### Improve server privacy
|
|
||||||
|
|
||||||
# Disable MOTD adverts to prevent revealing server information in MOTD request headers
|
|
||||||
# See https://ma.ttias.be/what-exactly-being-sent-ubuntu-motd/
|
|
||||||
if [ -f /etc/default/motd-news ]; then
|
|
||||||
tools/editconf.py /etc/default/motd-news ENABLED=0
|
|
||||||
rm -f /var/cache/motd-news
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ### Add PPAs.
|
# ### Add PPAs.
|
||||||
|
|
||||||
# We install some non-standard Ubuntu packages maintained by other
|
# We install some non-standard Ubuntu packages maintained by other
|
||||||
@@ -120,7 +110,7 @@ hide_output add-apt-repository --y ppa:ondrej/php
|
|||||||
# 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 upgrade
|
||||||
|
|
||||||
@@ -145,7 +135,7 @@ apt_get_quiet autoremove
|
|||||||
# * bc: allows us to do math to compute sane defaults
|
# * bc: allows us to do math to compute sane defaults
|
||||||
# * openssh-client: provides ssh-keygen
|
# * openssh-client: provides ssh-keygen
|
||||||
|
|
||||||
echo "Installing system packages..."
|
echo Installing system packages...
|
||||||
apt_install python3 python3-dev python3-pip python3-setuptools \
|
apt_install python3 python3-dev python3-pip python3-setuptools \
|
||||||
netcat-openbsd wget curl git sudo coreutils bc file \
|
netcat-openbsd wget curl git sudo coreutils bc file \
|
||||||
pollinate openssh-client unzip \
|
pollinate openssh-client unzip \
|
||||||
@@ -174,7 +164,7 @@ fi
|
|||||||
# 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 ] || [ -n "${FIRST_TIME_SETUP:-}" ]; then
|
if [ ! -f /etc/timezone ] || [ ! -z ${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.
|
||||||
@@ -227,7 +217,7 @@ fi
|
|||||||
# issue any warnings if no entropy is actually available. (http://www.2uo.de/myths-about-urandom/)
|
# issue any warnings if no entropy is actually available. (http://www.2uo.de/myths-about-urandom/)
|
||||||
# Entropy might not be readily available because this machine has no user input
|
# Entropy might not be readily available because this machine has no user input
|
||||||
# devices (common on servers!) and either no hard disk or not enough IO has
|
# devices (common on servers!) and either no hard disk or not enough IO has
|
||||||
# occurred yet --- although haveged tries to mitigate this. So there's a good chance
|
# ocurred yet --- although haveged tries to mitigate this. So there's a good chance
|
||||||
# that accessing /dev/urandom will not be drawing from any hardware entropy and under
|
# that accessing /dev/urandom will not be drawing from any hardware entropy and under
|
||||||
# a perfect-storm circumstance where the other seeds are meaningless, /dev/urandom
|
# a perfect-storm circumstance where the other seeds are meaningless, /dev/urandom
|
||||||
# may not be seeded at all.
|
# may not be seeded at all.
|
||||||
@@ -236,7 +226,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
|
||||||
@@ -279,14 +269,14 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then
|
|||||||
# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC
|
# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC
|
||||||
# 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 //" | tr '\n' ' ') #NODOC
|
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC
|
||||||
if [ -n "$SSH_PORT" ]; then
|
if [ ! -z "$SSH_PORT" ]; then
|
||||||
for port in $SSH_PORT; do
|
if [ "$SSH_PORT" != "22" ]; then
|
||||||
if [ "$port" != "22" ]; then
|
|
||||||
echo "Opening alternate SSH port $port." #NODOC
|
echo Opening alternate SSH port $SSH_PORT. #NODOC
|
||||||
ufw_limit "$port" #NODOC
|
ufw_limit $SSH_PORT #NODOC
|
||||||
|
|
||||||
fi
|
fi
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ufw --force enable;
|
ufw --force enable;
|
||||||
|
|||||||
38
setup/web.sh
38
setup/web.sh
@@ -6,9 +6,9 @@ source setup/functions.sh # load our functions
|
|||||||
source /etc/mailinabox.conf # load global vars
|
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 dependencies.
|
# 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
|
||||||
@@ -19,7 +19,7 @@ fi
|
|||||||
|
|
||||||
echo "Installing Nginx (web server)..."
|
echo "Installing Nginx (web server)..."
|
||||||
|
|
||||||
apt_install nginx php"${PHP_VER}"-cli php"${PHP_VER}"-fpm idn2
|
apt_install nginx php${PHP_VER}-cli php${PHP_VER}-fpm idn2
|
||||||
|
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
@@ -46,15 +46,15 @@ tools/editconf.py /etc/nginx/nginx.conf -s \
|
|||||||
ssl_protocols="TLSv1.2 TLSv1.3;"
|
ssl_protocols="TLSv1.2 TLSv1.3;"
|
||||||
|
|
||||||
# Tell PHP not to expose its version number in the X-Powered-By header.
|
# Tell PHP not to expose its version number in the X-Powered-By header.
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \
|
||||||
expose_php=Off
|
expose_php=Off
|
||||||
|
|
||||||
# Set PHPs default charset to UTF-8, since we use it. See #367.
|
# Set PHPs default charset to UTF-8, since we use it. See #367.
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/php.ini -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/php.ini -c ';' \
|
||||||
default_charset="UTF-8"
|
default_charset="UTF-8"
|
||||||
|
|
||||||
# Configure the path environment for php-fpm
|
# Configure the path environment for php-fpm
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \
|
||||||
env[PATH]=/usr/local/bin:/usr/bin:/bin \
|
env[PATH]=/usr/local/bin:/usr/bin:/bin \
|
||||||
|
|
||||||
# Configure php-fpm based on the amount of memory the machine has
|
# Configure php-fpm based on the amount of memory the machine has
|
||||||
@@ -62,32 +62,32 @@ tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \
|
|||||||
# Some synchronisation issues can occur when many people access the site at once.
|
# Some synchronisation issues can occur when many people access the site at once.
|
||||||
# The pm=ondemand setting is used for memory constrained machines < 2GB, this is copied over from PR: 1216
|
# The pm=ondemand setting is used for memory constrained machines < 2GB, this is copied over from PR: 1216
|
||||||
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}' || /bin/true)
|
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}' || /bin/true)
|
||||||
if [ "$TOTAL_PHYSICAL_MEM" -lt 1000000 ]
|
if [ $TOTAL_PHYSICAL_MEM -lt 1000000 ]
|
||||||
then
|
then
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \
|
||||||
pm=ondemand \
|
pm=ondemand \
|
||||||
pm.max_children=8 \
|
pm.max_children=8 \
|
||||||
pm.start_servers=2 \
|
pm.start_servers=2 \
|
||||||
pm.min_spare_servers=1 \
|
pm.min_spare_servers=1 \
|
||||||
pm.max_spare_servers=3
|
pm.max_spare_servers=3
|
||||||
elif [ "$TOTAL_PHYSICAL_MEM" -lt 2000000 ]
|
elif [ $TOTAL_PHYSICAL_MEM -lt 2000000 ]
|
||||||
then
|
then
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \
|
||||||
pm=ondemand \
|
pm=ondemand \
|
||||||
pm.max_children=16 \
|
pm.max_children=16 \
|
||||||
pm.start_servers=4 \
|
pm.start_servers=4 \
|
||||||
pm.min_spare_servers=1 \
|
pm.min_spare_servers=1 \
|
||||||
pm.max_spare_servers=6
|
pm.max_spare_servers=6
|
||||||
elif [ "$TOTAL_PHYSICAL_MEM" -lt 3000000 ]
|
elif [ $TOTAL_PHYSICAL_MEM -lt 3000000 ]
|
||||||
then
|
then
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \
|
||||||
pm=dynamic \
|
pm=dynamic \
|
||||||
pm.max_children=60 \
|
pm.max_children=60 \
|
||||||
pm.start_servers=6 \
|
pm.start_servers=6 \
|
||||||
pm.min_spare_servers=3 \
|
pm.min_spare_servers=3 \
|
||||||
pm.max_spare_servers=9
|
pm.max_spare_servers=9
|
||||||
else
|
else
|
||||||
tools/editconf.py /etc/php/"$PHP_VER"/fpm/pool.d/www.conf -c ';' \
|
tools/editconf.py /etc/php/$PHP_VER/fpm/pool.d/www.conf -c ';' \
|
||||||
pm=dynamic \
|
pm=dynamic \
|
||||||
pm.max_children=120 \
|
pm.max_children=120 \
|
||||||
pm.start_servers=12 \
|
pm.start_servers=12 \
|
||||||
@@ -138,16 +138,16 @@ cat conf/mta-sts.txt \
|
|||||||
chmod a+r /var/lib/mailinabox/mta-sts.txt
|
chmod a+r /var/lib/mailinabox/mta-sts.txt
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
# Start services.
|
# Start services.
|
||||||
restart_service nginx
|
restart_service nginx
|
||||||
restart_service php"$PHP_VER"-fpm
|
restart_service php$PHP_VER-fpm
|
||||||
|
|
||||||
# Open ports.
|
# Open ports.
|
||||||
ufw_allow http
|
ufw_allow http
|
||||||
|
|||||||
32
setup/webmail.sh
Normal file → Executable file
32
setup/webmail.sh
Normal file → Executable file
@@ -22,8 +22,8 @@ source /etc/mailinabox.conf # load global vars
|
|||||||
echo "Installing Roundcube (webmail)..."
|
echo "Installing Roundcube (webmail)..."
|
||||||
apt_install \
|
apt_install \
|
||||||
dbconfig-common \
|
dbconfig-common \
|
||||||
php"${PHP_VER}"-cli php"${PHP_VER}"-sqlite3 php"${PHP_VER}"-intl php"${PHP_VER}"-common php"${PHP_VER}"-curl php"${PHP_VER}"-imap \
|
php${PHP_VER}-cli php${PHP_VER}-sqlite3 php${PHP_VER}-intl php${PHP_VER}-common php${PHP_VER}-curl php${PHP_VER}-imap \
|
||||||
php"${PHP_VER}"-gd php"${PHP_VER}"-pspell php"${PHP_VER}"-mbstring php"${PHP_VER}"-xml libjs-jquery libjs-jquery-mousewheel libmagic1 \
|
php${PHP_VER}-gd php${PHP_VER}-pspell php${PHP_VER}-mbstring libjs-jquery libjs-jquery-mousewheel libmagic1 \
|
||||||
sqlite3
|
sqlite3
|
||||||
|
|
||||||
# Install Roundcube from source if it is not already present or if it is out of date.
|
# Install Roundcube from source if it is not already present or if it is out of date.
|
||||||
@@ -36,8 +36,8 @@ apt_install \
|
|||||||
# https://github.com/mstilkerich/rcmcarddav/releases
|
# https://github.com/mstilkerich/rcmcarddav/releases
|
||||||
# The easiest way to get the package hashes is to run this script and get the hash from
|
# The easiest way to get the package hashes is to run this script and get the hash from
|
||||||
# the error message.
|
# the error message.
|
||||||
VERSION=1.6.11
|
VERSION=1.6.6
|
||||||
HASH=d72da06b5f65142dab8b574f7676e0220541a3d4
|
HASH=7705d2736890c49e7ae3ac75e3ae00ba56187056
|
||||||
PERSISTENT_LOGIN_VERSION=bde7b6840c7d91de627ea14e81cf4133cbb3c07a # version 5.3
|
PERSISTENT_LOGIN_VERSION=bde7b6840c7d91de627ea14e81cf4133cbb3c07a # version 5.3
|
||||||
HTML5_NOTIFIER_VERSION=68d9ca194212e15b3c7225eb6085dbcf02fd13d7 # version 0.6.4+
|
HTML5_NOTIFIER_VERSION=68d9ca194212e15b3c7225eb6085dbcf02fd13d7 # version 0.6.4+
|
||||||
CARDDAV_VERSION=4.4.3
|
CARDDAV_VERSION=4.4.3
|
||||||
@@ -170,8 +170,8 @@ cat > ${RCM_PLUGIN_DIR}/carddav/config.inc.php <<EOF;
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Create writable directories.
|
# Create writable 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.log
|
sudo -u www-data touch /var/log/roundcubemail/errors.log
|
||||||
@@ -194,10 +194,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
|
||||||
@@ -205,9 +205,9 @@ chown -f -R root:www-data ${RCM_PLUGIN_DIR}/carddav
|
|||||||
chmod -R 774 ${RCM_PLUGIN_DIR}/carddav
|
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)
|
||||||
php"$PHP_VER" ${RCM_DIR}/bin/updatedb.sh --dir ${RCM_DIR}/SQL --package roundcube
|
php$PHP_VER ${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
|
||||||
|
|
||||||
# Patch the Roundcube code to eliminate an issue that causes postfix to reject our sqlite
|
# Patch the Roundcube code to eliminate an issue that causes postfix to reject our sqlite
|
||||||
# user database (see https://github.com/mail-in-a-box/mailinabox/issues/2185)
|
# user database (see https://github.com/mail-in-a-box/mailinabox/issues/2185)
|
||||||
@@ -217,8 +217,8 @@ sed -i.miabold 's/^[^#]\+.\+PRAGMA journal_mode = WAL.\+$/#&/' \
|
|||||||
# Because Roundcube wants to set the PRAGMA we just deleted from the source, we apply it here
|
# Because Roundcube wants to set the PRAGMA we just deleted from the source, we apply it here
|
||||||
# to the roundcube database (see https://github.com/roundcube/roundcubemail/issues/8035)
|
# to the roundcube database (see https://github.com/roundcube/roundcubemail/issues/8035)
|
||||||
# Database should exist, created by migration script
|
# Database should exist, created by migration script
|
||||||
hide_output sqlite3 "$STORAGE_ROOT/mail/roundcube/roundcube.sqlite" 'PRAGMA journal_mode=WAL;'
|
sqlite3 $STORAGE_ROOT/mail/roundcube/roundcube.sqlite 'PRAGMA journal_mode=WAL;' | 2>&1
|
||||||
|
|
||||||
# Enable PHP modules.
|
# Enable PHP modules.
|
||||||
phpenmod -v "$PHP_VER" imap
|
phpenmod -v $PHP_VER imap
|
||||||
restart_service php"$PHP_VER"-fpm
|
restart_service php$PHP_VER-fpm
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ source /etc/mailinabox.conf # load global vars
|
|||||||
|
|
||||||
echo "Installing Z-Push (Exchange/ActiveSync server)..."
|
echo "Installing Z-Push (Exchange/ActiveSync server)..."
|
||||||
apt_install \
|
apt_install \
|
||||||
php"${PHP_VER}"-soap php"${PHP_VER}"-imap libawl-php php"$PHP_VER"-xml php"${PHP_VER}"-intl
|
php${PHP_VER}-soap php${PHP_VER}-imap libawl-php php$PHP_VER-xml
|
||||||
|
|
||||||
phpenmod -v "$PHP_VER" imap
|
phpenmod -v $PHP_VER imap
|
||||||
|
|
||||||
# Copy Z-Push into place.
|
# Copy Z-Push into place.
|
||||||
VERSION=2.7.5
|
VERSION=2.7.1
|
||||||
TARGETHASH=f0b0b06e255f3496173ab9d28a4f2d985184720e
|
TARGETHASH=f15c566b1ad50de24f3f08f505f0c3d8155c2d0d
|
||||||
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
|
||||||
@@ -44,10 +44,10 @@ if [ $needs_update == 1 ]; then
|
|||||||
# Create admin and top scripts with PHP_VER
|
# Create admin and top scripts with PHP_VER
|
||||||
rm -f /usr/sbin/z-push-{admin,top}
|
rm -f /usr/sbin/z-push-{admin,top}
|
||||||
echo '#!/bin/bash' > /usr/sbin/z-push-admin
|
echo '#!/bin/bash' > /usr/sbin/z-push-admin
|
||||||
echo php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php '"$@"' >> /usr/sbin/z-push-admin
|
echo php$PHP_VER /usr/local/lib/z-push/z-push-admin.php '"$@"' >> /usr/sbin/z-push-admin
|
||||||
chmod 755 /usr/sbin/z-push-admin
|
chmod 755 /usr/sbin/z-push-admin
|
||||||
echo '#!/bin/bash' > /usr/sbin/z-push-top
|
echo '#!/bin/bash' > /usr/sbin/z-push-top
|
||||||
echo php"$PHP_VER" /usr/local/lib/z-push/z-push-top.php '"$@"' >> /usr/sbin/z-push-top
|
echo php$PHP_VER /usr/local/lib/z-push/z-push-top.php '"$@"' >> /usr/sbin/z-push-top
|
||||||
chmod 755 /usr/sbin/z-push-top
|
chmod 755 /usr/sbin/z-push-top
|
||||||
|
|
||||||
echo $VERSION > /usr/local/lib/z-push/version
|
echo $VERSION > /usr/local/lib/z-push/version
|
||||||
@@ -57,6 +57,8 @@ fi
|
|||||||
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" /usr/local/lib/z-push/config.php
|
sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" /usr/local/lib/z-push/config.php
|
||||||
sed -i "s/define('BACKEND_PROVIDER', .*/define('BACKEND_PROVIDER', 'BackendCombined');/" /usr/local/lib/z-push/config.php
|
sed -i "s/define('BACKEND_PROVIDER', .*/define('BACKEND_PROVIDER', 'BackendCombined');/" /usr/local/lib/z-push/config.php
|
||||||
sed -i "s/define('USE_FULLEMAIL_FOR_LOGIN', .*/define('USE_FULLEMAIL_FOR_LOGIN', true);/" /usr/local/lib/z-push/config.php
|
sed -i "s/define('USE_FULLEMAIL_FOR_LOGIN', .*/define('USE_FULLEMAIL_FOR_LOGIN', true);/" /usr/local/lib/z-push/config.php
|
||||||
|
sed -i "s/define('LOG_MEMORY_PROFILER', .*/define('LOG_MEMORY_PROFILER', false);/" /usr/local/lib/z-push/config.php
|
||||||
|
sed -i "s/define('BUG68532FIXED', .*/define('BUG68532FIXED', false);/" /usr/local/lib/z-push/config.php
|
||||||
sed -i "s/define('LOGLEVEL', .*/define('LOGLEVEL', LOGLEVEL_ERROR);/" /usr/local/lib/z-push/config.php
|
sed -i "s/define('LOGLEVEL', .*/define('LOGLEVEL', LOGLEVEL_ERROR);/" /usr/local/lib/z-push/config.php
|
||||||
|
|
||||||
# Configure BACKEND
|
# Configure BACKEND
|
||||||
@@ -106,10 +108,8 @@ EOF
|
|||||||
|
|
||||||
# Restart service.
|
# Restart service.
|
||||||
|
|
||||||
restart_service php"$PHP_VER"-fpm
|
restart_service php$PHP_VER-fpm
|
||||||
|
|
||||||
# Fix states after upgrade
|
# Fix states after upgrade
|
||||||
|
|
||||||
if [ $needs_update == 1 ]; then
|
hide_output php$PHP_VER /usr/local/lib/z-push/z-push-admin.php -a fixstates
|
||||||
hide_output php"$PHP_VER" /usr/local/lib/z-push/z-push-admin.php -a fixstates
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ def test2(tests, server, description):
|
|||||||
#print(server, ":", qname, rtype, "?", response)
|
#print(server, ":", qname, rtype, "?", response)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# show problem
|
# show prolem
|
||||||
if first:
|
if first:
|
||||||
print("Incorrect DNS Response from", description)
|
print("Incorrect DNS Response from", description)
|
||||||
print()
|
print()
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#!/bin/bash
|
|
||||||
# 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
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ POSTDATA=dummy
|
|||||||
if [ "$1" == "--force" ]; then
|
if [ "$1" == "--force" ]; then
|
||||||
POSTDATA=force=1
|
POSTDATA=force=1
|
||||||
fi
|
fi
|
||||||
curl -s -d $POSTDATA --user "$(</var/lib/mailinabox/api.key):" http://127.0.0.1:10222/dns/update
|
curl -s -d $POSTDATA --user $(</var/lib/mailinabox/api.key): http://127.0.0.1:10222/dns/update
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import sys, re
|
|||||||
|
|
||||||
# sanity check
|
# sanity check
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
print("usage: python3 editconf.py /etc/file.conf [-e] [-s] [-w] [-c <CHARACTER>] [-t] NAME=VAL [NAME=VAL ...]")
|
print("usage: python3 editconf.py /etc/file.conf [-s] [-w] [-c <CHARACTER>] [-t] NAME=VAL [NAME=VAL ...]")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# parse command line arguments
|
# parse command line arguments
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ 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
|
||||||
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 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -36,14 +36,14 @@ 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$PHP_VER" /usr/local/lib/owncloud/occ maintenance:mode --off
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ maintenance:mode --off
|
||||||
|
|
||||||
service php8.0-fpm start
|
service php8.0-fpm start
|
||||||
echo "Done"
|
echo "Done"
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ 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$PHP_VER" /usr/local/lib/owncloud/occ group:adduser admin "$ADMIN" && echo "Done."
|
sudo -u www-data php$PHP_VER /usr/local/lib/owncloud/occ group:adduser admin $ADMIN && echo Done.
|
||||||
|
|||||||
@@ -227,10 +227,12 @@ class EditConf(Grammar):
|
|||||||
options = []
|
options = []
|
||||||
eq = "="
|
eq = "="
|
||||||
if self[3] and "-s" in self[3].string: eq = " "
|
if self[3] and "-s" in self[3].string: eq = " "
|
||||||
|
try:
|
||||||
for opt in re.split("\s+", self[4].string):
|
for opt in re.split("\s+", self[4].string):
|
||||||
k, v = opt.split("=", 1)
|
k, v = opt.split("=", 1) # try except , else err: 1 arg missing
|
||||||
v = re.sub(r"\n+", "", fixup_tokens(v)) # not sure why newlines are getting doubled
|
v = re.sub(r"\n+", "", fixup_tokens(v)) # not sure why newlines are getting doubled
|
||||||
options.append("%s%s%s" % (k, eq, v))
|
options.append("%s%s%s" % (k, eq, v))
|
||||||
|
except: pass
|
||||||
return "<div class='write-to'><div class='filename'>" + self[1].string + " <span>(change settings)</span></div><pre>" + "\n".join(cgi.escape(s) for s in options) + "</pre></div>\n"
|
return "<div class='write-to'><div class='filename'>" + self[1].string + " <span>(change settings)</span></div><pre>" + "\n".join(cgi.escape(s) for s in options) + "</pre></div>\n"
|
||||||
|
|
||||||
class CaptureOutput(Grammar):
|
class CaptureOutput(Grammar):
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Cleanup SSL certificates which expired more than 7 days ago from $STORAGE_ROOT/ssl and move them to $STORAGE_ROOT/ssl.expired
|
|
||||||
|
|
||||||
source /etc/mailinabox.conf
|
|
||||||
shopt -s extglob nullglob
|
|
||||||
|
|
||||||
retain_after="$(date --date="7 days ago" +%Y%m%d)"
|
|
||||||
|
|
||||||
mkdir -p $STORAGE_ROOT/ssl.expired
|
|
||||||
for file in $STORAGE_ROOT/ssl/*-+([0-9])-+([0-9a-f]).pem; do
|
|
||||||
pem="$(basename "$file")"
|
|
||||||
not_valid_after="$(cut -d- -f1 <<< "${pem: -21}")"
|
|
||||||
|
|
||||||
if [ "$not_valid_after" -lt "$retain_after" ]; then
|
|
||||||
mv "$file" "$STORAGE_ROOT/ssl.expired/${pem}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
curl -s -d POSTDATA --user "$(</var/lib/mailinabox/api.key):" http://127.0.0.1:10222/web/update
|
curl -s -d POSTDATA --user $(</var/lib/mailinabox/api.key): http://127.0.0.1:10222/web/update
|
||||||
|
|||||||
Reference in New Issue
Block a user