Compare commits

..

1 Commits

Author SHA1 Message Date
Michael Meidlinger 9d7a892355
Merge 32cfd1ed52 into 18b8f9ab4b 2024-03-10 12:51:12 -07:00
33 changed files with 309 additions and 350 deletions

View File

@ -1,31 +1,6 @@
CHANGELOG CHANGELOG
========= =========
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)
------------------------------ ------------------------------

View File

@ -60,7 +60,7 @@ Clone this repository and checkout the tag corresponding to the most recent rele
$ 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 v68 $ git checkout v67
Begin the installation. Begin the installation.

View File

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

View File

@ -9,7 +9,7 @@ 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
@ -448,11 +448,14 @@ def build_sshfp_records():
# 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"))

View File

@ -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")

View File

@ -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>

View File

@ -179,34 +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
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()

View File

@ -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=v68 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

View File

@ -10,12 +10,12 @@ source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars source /etc/mailinabox.conf # load global vars
# Install DKIM... # Install DKIM...
echo "Installing OpenDKIM/OpenDMARC..." echo Installing OpenDKIM/OpenDMARC...
apt_install opendkim opendkim-tools opendmarc apt_install opendkim opendkim-tools opendmarc
# Make sure configuration directories exist. # Make sure configuration directories exist.
mkdir -p /etc/opendkim; mkdir -p /etc/opendkim;
mkdir -p "$STORAGE_ROOT/mail/dkim" mkdir -p $STORAGE_ROOT/mail/dkim
# Used in InternalHosts and ExternalIgnoreList configuration directives. # Used in InternalHosts and ExternalIgnoreList configuration directives.
# Not quite sure why. # Not quite sure why.
@ -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" \

View File

@ -106,7 +106,7 @@ if [ ! -f "$STORAGE_ROOT/dns/dnssec/$algo.conf" ]; then
# (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

View File

@ -1,4 +1,3 @@
#!/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 outut 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
@ -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:-}" 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

View File

@ -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
} }

View File

@ -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

View File

@ -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 \
@ -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;

View File

@ -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

View File

@ -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 3 * * * 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.

View File

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

View File

@ -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."

View File

@ -65,13 +65,13 @@ user_external_hash=a494073dcdecbbbc79a9c77f72524ac9994d2eec
# 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',
@ -307,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.
@ -328,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");
@ -359,32 +357,31 @@ 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 \
@ -393,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 \
@ -407,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
@ -422,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
@ -451,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

View File

@ -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
@ -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

View File

@ -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/

View File

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

View File

@ -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

View File

@ -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
@ -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 chuncks # 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
@ -111,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
@ -136,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 \
@ -165,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 +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
@ -271,11 +270,11 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then
# settings, find the port it is supposedly running on, and open that port #NODOC # settings, find the port it is supposedly running on, and open that port #NODOC
# too. #NODOC # too. #NODOC
SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC SSH_PORT=$(sshd -T 2>/dev/null | grep "^port " | sed "s/port //") #NODOC
if [ -n "$SSH_PORT" ]; then if [ ! -z "$SSH_PORT" ]; then
if [ "$SSH_PORT" != "22" ]; then if [ "$SSH_PORT" != "22" ]; then
echo "Opening alternate SSH port $SSH_PORT." #NODOC echo Opening alternate SSH port $SSH_PORT. #NODOC
ufw_limit "$SSH_PORT" #NODOC ufw_limit $SSH_PORT #NODOC
fi fi
fi fi

View File

@ -8,7 +8,7 @@ source /etc/mailinabox.conf # load global vars
# Some Ubuntu images start off with Apache. Remove it since we # Some Ubuntu images start off with Apache. Remove it since we
# will use nginx. Use autoremove to remove any Apache depenencies. # will use nginx. Use autoremove to remove any Apache depenencies.
if [ -f /usr/sbin/apache2 ]; then if [ -f /usr/sbin/apache2 ]; then
echo "Removing apache..." echo Removing apache...
hide_output apt-get -y purge apache2 apache2-* hide_output apt-get -y purge apache2 apache2-*
hide_output apt-get -y --purge autoremove hide_output apt-get -y --purge autoremove
fi fi
@ -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

28
setup/webmail.sh Normal file → Executable file
View 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 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.
@ -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

View File

@ -17,9 +17,9 @@ 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}-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.1 VERSION=2.7.1
@ -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
@ -108,8 +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
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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.

View File

@ -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