diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..93f02977 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,68 @@ +# Mail-in-a-Box Dockerfile +########################### +# +# This file lets Mail-in-a-Box run inside of Docker (https://docker.io), +# a virtualization/containerization manager. +# +# Run: +# $ containers/docker/run.sh +# to build the image, launch a storage container, and launch a Mail-in-a-Box +# container. +# +########################################### + +# We need a better starting image than docker's ubuntu image because that +# base image doesn't provide enough to run most Ubuntu services. See +# http://phusion.github.io/baseimage-docker/ for an explanation. + +FROM phusion/baseimage:0.9.16 + +# Dockerfile metadata. +MAINTAINER Joshua Tauberer (http://razor.occams.info) +EXPOSE 25 53/udp 53/tcp 80 443 587 993 4190 +VOLUME /home/user-data + +# Use baseimage init system +CMD ["/sbin/my_init"] + +# Create the user-data user, so the start script doesn't have to. +RUN useradd -m user-data + +# Docker has a beautiful way to cache images after each step. The next few +# steps of installing system packages are very intensive, so we take care +# of them early and let docker cache the image after that, before doing +# any Mail-in-a-Box specific system configuration. That makes rebuilds +# of the image extremely fast. + +# Update system packages. +RUN apt-get update +RUN DEBIAN_FRONTEND=noninteractive apt-get upgrade -y + +# Install packages needed by Mail-in-a-Box. +ADD containers/docker/apt_package_list.txt /tmp/mailinabox_apt_package_list.txt +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y $(cat /tmp/mailinabox_apt_package_list.txt) + +# Now add Mail-in-a-Box to the system. +ADD . /usr/local/mailinabox + +# Patch setup/functions.sh +RUN cp /usr/local/mailinabox/setup/functions.sh /usr/local/mailinabox/setup/functions.orig.sh +RUN echo "# Docker patches" >> /usr/local/mailinabox/setup/functions.sh && \ + echo "source containers/docker/patch/setup/functions_docker.sh" >> /usr/local/mailinabox/setup/functions.sh +# Skip apt-get install +RUN sed 's/PACKAGES=$@/PACKAGES=""/g' -i /usr/local/mailinabox/setup/functions.sh + +# Install runit services +ADD containers/docker/runit/ /etc/service/ + +# LSB Compatibility +RUN /usr/local/mailinabox/containers/docker/tools/lsb_compat.sh + +# Configure service logs +RUN /usr/local/mailinabox/containers/docker/tools/runit_logs.sh + +# Disable services +RUN /usr/local/mailinabox/containers/docker/tools/disable_services.sh + +# Add my_init scripts +ADD containers/docker/my_init.d/* /etc/my_init.d/ diff --git a/containers/docker/apt_package_list.txt b/containers/docker/apt_package_list.txt new file mode 100644 index 00000000..96bfdb56 --- /dev/null +++ b/containers/docker/apt_package_list.txt @@ -0,0 +1,82 @@ +bc +bind9 +ca-certificates +coreutils +cron +curl +dbconfig-common +dovecot-antispam +dovecot-core +dovecot-imapd +dovecot-lmtpd +dovecot-managesieved +dovecot-sieve +dovecot-sqlite +duplicity +fail2ban +git +haveged +ldnsutils +libapr1 +libawl-php +libcurl4-openssl-dev +libjs-jquery +libjs-jquery-mousewheel +libmagic1 +libtool +libyaml-dev +links +memcached +nginx +nsd +ntp +opendkim +opendkim-tools +opendmarc +openssh-client +openssl +php-apc +php-auth +php-crypt-gpg +php-mail-mime +php-net-sieve +php-net-smtp +php-net-socket +php-pear +php-soap +php-xml-parser +php5 +php5-cli +php5-common +php5-curl +php5-dev +php5-fpm +php5-gd +php5-imap +php5-intl +php5-json +php5-mcrypt +php5-memcache +php5-pspell +php5-sqlite +php5-xsl +postfix +postfix-pcre +postgrey +python3 +python3-dateutil +python3-dev +python3-dnspython +python3-flask +python3-pip +pyzor +razor +resolvconf +spampd +sqlite3 +sudo +tinymce +ufw +unattended-upgrades +unzip +wget diff --git a/containers/docker/my_init.d/10-mailinabox.sh b/containers/docker/my_init.d/10-mailinabox.sh new file mode 100755 index 00000000..bb3cf840 --- /dev/null +++ b/containers/docker/my_init.d/10-mailinabox.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# This script is used within containers to turn it into a Mail-in-a-Box. +# It is referenced by the Dockerfile. You should not run it directly. +######################################################################## + +# Local configuration details were not known at the time the Docker +# image was created, so all setup is defered until the container +# is started. That's when this script runs. + +# If we're not in an interactive shell, set defaults. +if [ ! -t 0 ]; then + echo '*** Non interactive shell detected...' + export PUBLIC_IP=auto + export PUBLIC_IPV6=auto + export PRIMARY_HOSTNAME=auto + export CSR_COUNTRY=US + export NONINTERACTIVE=1 +fi + +if ([ -z "$FORCE_INSTALL" ] && [ -f /var/lib/mailinabox/api.key ]); then + # Mailinabox is already installed and we don't want to reinstall + export SKIP_INSTALL=1 +fi + +# If we are skipping install, reload from /etc/mailinabox.conf if exists +if ([ -f /var/lib/mailinabox/api.key ] && [ ! -z "$SKIP_INSTALL" ]); then + echo '*** Loading variables from "/etc/mailinabox.conf"...' + + source /etc/mailinabox.conf + unset PRIVATE_IP + unset PRIVATE_IPV6 + export SKIP_NETWORK_CHECKS=1 + export NONINTERACTIVE=1 +fi + +export DISABLE_FIREWALL=1 +cd /usr/local/mailinabox + +if [ -z "$SKIP_INSTALL" ]; then + # Disable all services. + /usr/local/mailinabox/containers/docker/tools/disable_services.sh + + echo "*** Starting mailinabox installation..." + # Run in background to avoid blocking runit initialization while installing. + source setup/start.sh & +else + echo "*** Configuring mailinabox..." + # Run in foreground for services to be started after configuration. + source setup/configure.sh +fi \ No newline at end of file diff --git a/containers/docker/patch/setup/functions_docker.sh b/containers/docker/patch/setup/functions_docker.sh new file mode 100755 index 00000000..8f0d2947 --- /dev/null +++ b/containers/docker/patch/setup/functions_docker.sh @@ -0,0 +1,22 @@ +#!/bin/bash +function save_function() { + local ORIG_FUNC=$(declare -f $1) + local NEWNAME_FUNC="$2${ORIG_FUNC#$1}" + eval "$NEWNAME_FUNC" +} + +function enable_service { + if [ -f /etc/service/$1/down ]; then + # Runit service already exists, but is disabled with a down file. Remove it. + rm /etc/service/$1/down + fi +} + +save_function restart_service restart_service_orig +function restart_service { + # Make sure service is enabled + enable_service $1 + + # Call original method + restart_service_orig $1 +} \ No newline at end of file diff --git a/containers/docker/run b/containers/docker/run new file mode 100755 index 00000000..dad4d3c9 --- /dev/null +++ b/containers/docker/run @@ -0,0 +1,102 @@ +#!/bin/bash +# Use this script to launch Mail-in-a-Box within a docker container. +# ================================================================== +# +# Run this script from the base directory of the Mail-in-a-Box +# repository (i.e. run as 'containers/docker/run'). +# +# A base image is created first. The base image installs Ubuntu +# packages and pulls in the Mail-in-a-Box source code. This is +# defined in Dockerfile at the root of this repository. +# +# A mailinabox-data container is created next. This container +# contains nothing but a shared volume for storing user data. +# It is segregated from the rest of the live system to make backups +# easier. +# +# The mailinabox container is started last. It is the +# real thing: it runs the mailinabox image. This container will +# initialize itself and will initialize the mailinabox-data +# volume if the volume is new. + +# Build or rebuild the image. +# Rebuilds are very fast. + +HOST_HTTP_PORT=${HOST_HTTP_PORT:-80} +HOST_HTTPS_PORT=${HOST_HTTPS_PORT:-443} +CONTAINER_NAME=${CONTAINER_NAME:-mailinabox} +CONTAINER_DATA_NAME=${CONTAINER_DATA_NAME:-${CONTAINER_NAME}-data} + +if [ -z "$SKIP_BUILD" ]; then + tput setaf 2 + echo "Building/updating base image (mailinabox)..." + tput setaf 7 + + docker build -q -t mailinabox . +fi; + +if ! docker inspect ${CONTAINER_DATA_NAME} > /dev/null; then + tput setaf 2 + echo + echo "Creating a new container for your data (${CONTAINER_DATA_NAME})..." + tput setaf 7 + + docker create \ + --name ${CONTAINER_DATA_NAME} \ + $([ -z "$HOST_USERDATA_VOLUME" ] && echo "-v $HOST_USERDATA_VOLUME:/home/user-data" || echo "-v /home/user-data") \ + phusion/baseimage:0.9.16 +else + tput setaf 2 + echo + echo "Using existing container ${CONTAINER_DATA_NAME} for your data." + tput setaf 7 +fi + +# End a running container. +if docker inspect ${CONTAINER_NAME} > /dev/null; then + tput setaf 2 + echo + echo "Destroying ${CONTAINER_NAME} container..." + tput setaf 7 + + docker rm -f ${CONTAINER_NAME} +fi + +# Start container. +tput setaf 2 +echo +echo "Starting new container (${CONTAINER_NAME})..." +tput setaf 7 + +# Run the services container +# detached if NONINTERACTIVE is set, +# interactively if NONINTERACTIVE is not set, +# Notes: +# * Passing through SKIP_NETWORK_CHECKS makes it easier to do testing +# on a residential network. +# * --privileged flag cause an issue with bind9/named failing to start in this case +# see docker/docker#7318 +docker run \ + -v /dev/urandom:/dev/random \ + -p 25:25 \ + $([ -z "$NODNS" ] && echo "-p 53:53/udp -p 53:53/tcp") \ + -p $HOST_HTTP_PORT:80 \ + -p $HOST_HTTPS_PORT:443 \ + -p 587:587 \ + -p 993:993 \ + -p 4190:4190 \ + --name ${CONTAINER_NAME} \ + --volumes-from ${CONTAINER_DATA_NAME} \ + --restart always \ + $([ ! -z "$NONINTERACTIVE" ] && echo "-d") \ + -it \ + mailinabox + +if [ -z "$NONINTERACTIVE" ]; then + tput setaf 2 + echo + echo "Restarting container ${CONTAINER_NAME}..." + tput setaf 7 + + docker restart ${CONTAINER_NAME} +fi \ No newline at end of file diff --git a/containers/docker/runit/bind9/run b/containers/docker/runit/bind9/run new file mode 100755 index 00000000..0a03e3b8 --- /dev/null +++ b/containers/docker/runit/bind9/run @@ -0,0 +1,52 @@ +#!/bin/bash + +PATH=/sbin:/bin:/usr/sbin:/usr/bin + +# for a chrooted server: "-u bind -t /var/lib/named" +# Don't modify this line, change or create /etc/default/bind9. +OPTIONS="" +RESOLVCONF=no + +test -f /etc/default/bind9 && . /etc/default/bind9 + +test -x /usr/sbin/rndc || exit 0 + +. /lib/lsb/init-functions + +check_network() { + if [ -x /usr/bin/uname ] && [ "X$(/usr/bin/uname -o)" = XSolaris ]; then + IFCONFIG_OPTS="-au" + else + IFCONFIG_OPTS="" + fi + if [ -z "$(/sbin/ifconfig $IFCONFIG_OPTS)" ]; then + #log_action_msg "No networks configured." + return 1 + fi + return 0 +} + +log_daemon_msg "Starting domain name service..." "bind9" + +modprobe capability >/dev/null 2>&1 || true + +# dirs under /var/run can go away on reboots. +mkdir -p /var/run/named +chmod 775 /var/run/named +chown root:bind /var/run/named >/dev/null 2>&1 || true + +if [ ! -x /usr/sbin/named ]; then + log_action_msg "named binary missing - not starting" + log_end_msg 1 +fi + +if ! check_network; then + log_action_msg "no networks configured" + log_end_msg 1 +fi + +if [ "X$RESOLVCONF" != "Xno" ] && [ -x /sbin/resolvconf ] ; then + echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.named +fi + +exec /usr/sbin/named -f $OPTIONS diff --git a/containers/docker/runit/dovecot/run b/containers/docker/runit/dovecot/run new file mode 100755 index 00000000..bd3939fc --- /dev/null +++ b/containers/docker/runit/dovecot/run @@ -0,0 +1,3 @@ +#!/bin/bash + +exec /usr/sbin/dovecot -F -c /etc/dovecot/dovecot.conf diff --git a/containers/docker/runit/fail2ban/run b/containers/docker/runit/fail2ban/run new file mode 100755 index 00000000..ce22c625 --- /dev/null +++ b/containers/docker/runit/fail2ban/run @@ -0,0 +1,93 @@ +#!/bin/bash + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +DESC="authentication failure monitor" +NAME=fail2ban + +# fail2ban-client is not a daemon itself but starts a daemon and +# loads its with configuration +#DAEMON=/usr/bin/$NAME-client +DAEMON=/usr/bin/$NAME-server +SCRIPTNAME=/etc/init.d/$NAME + +# Ad-hoc way to parse out socket file name +SOCKFILE=`grep -h '^[^#]*socket *=' /etc/$NAME/$NAME.conf /etc/$NAME/$NAME.local 2>/dev/null \ + | tail -n 1 | sed -e 's/.*socket *= *//g' -e 's/ *$//g'` +[ -z "$SOCKFILE" ] && SOCKFILE='/tmp/fail2ban.sock' + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Run as root by default. +FAIL2BAN_USER=root + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME +DAEMON_ARGS="-f $FAIL2BAN_OPTS" + +# Load the VERBOSE setting and other rcS variables +[ -f /etc/default/rcS ] && . /etc/default/rcS + +# Predefine what can be missing from lsb source later on -- necessary to run +# on sarge. Just present it in a bit more compact way from what was shipped +log_daemon_msg () { + [ -z "$1" ] && return 1 + echo -n "$1:" + [ -z "$2" ] || echo -n " $2" +} + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +# Actually has to (>=2.0-7) present in sarge. log_daemon_msg is predefined +# so we must be ok +. /lib/lsb/init-functions + +# +# Shortcut function for abnormal init script interruption +# +report_bug() +{ + echo $* + echo "Please submit a bug report to Debian BTS (reportbug fail2ban)" + exit 1 +} + +# +# Helper function to check if socket is present, which is often left after +# abnormal exit of fail2ban and needs to be removed +# +check_socket() +{ + # Return + # 0 if socket is present and readable + # 1 if socket file is not present + # 2 if socket file is present but not readable + # 3 if socket file is present but is not a socket + [ -e "$SOCKFILE" ] || return 1 + [ -r "$SOCKFILE" ] || return 2 + [ -S "$SOCKFILE" ] || return 3 + return 0 +} + +if [ -e "$SOCKFILE" ]; then + log_failure_msg "Socket file $SOCKFILE is present" + [ "$1" = "force-start" ] \ + && log_success_msg "Starting anyway as requested" \ + || return 2 + DAEMON_ARGS="$DAEMON_ARGS -x" +fi + +# Assure that /var/run/fail2ban exists +[ -d /var/run/fail2ban ] || mkdir -p /var/run/fail2ban + +if [ "$FAIL2BAN_USER" != "root" ]; then + # Make the socket directory, IP lists and fail2ban log + # files writable by fail2ban + chown "$FAIL2BAN_USER" /var/run/fail2ban + # Create the logfile if it doesn't exist + touch /var/log/fail2ban.log + chown "$FAIL2BAN_USER" /var/log/fail2ban.log + find /proc/net/xt_recent -name 'fail2ban-*' -exec chown "$FAIL2BAN_USER" {} \; +fi + +exec /sbin/setuser $FAIL2BAN_USER $DAEMON $DAEMON_ARGS diff --git a/containers/docker/runit/mailinabox/run b/containers/docker/runit/mailinabox/run new file mode 100755 index 00000000..abeb4331 --- /dev/null +++ b/containers/docker/runit/mailinabox/run @@ -0,0 +1,14 @@ +#!/bin/bash + +NAME=mailinabox +DAEMON=/usr/local/bin/mailinabox-daemon + +export LANGUAGE=en_US.UTF-8 +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 +export LC_TYPE=en_US.UTF-8 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +exec $DAEMON 2>&1 \ No newline at end of file diff --git a/containers/docker/runit/memcached/run b/containers/docker/runit/memcached/run new file mode 100755 index 00000000..51728127 --- /dev/null +++ b/containers/docker/runit/memcached/run @@ -0,0 +1,18 @@ +#!/bin/sh + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/bin/memcached + +test -x $DAEMON || exit 0 +set -e + +# Edit /etc/default/memcached to change this. +ENABLE_MEMCACHED=no +test -r /etc/default/memcached && . /etc/default/memcached + +echo -n "Starting $DESC: " +if [ $ENABLE_MEMCACHED = yes ]; then + exec /sbin/setuser memcache $DAEMON +else + exit 1 +fi \ No newline at end of file diff --git a/containers/docker/runit/nginx/run b/containers/docker/runit/nginx/run new file mode 100755 index 00000000..5c4f483c --- /dev/null +++ b/containers/docker/runit/nginx/run @@ -0,0 +1,24 @@ +#!/bin/bash + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/nginx +NAME=nginx +DESC=nginx + +# Include nginx defaults if available +if [ -r /etc/default/nginx ]; then + . /etc/default/nginx +fi + +test -x $DAEMON || exit 0 + +. /lib/init/vars.sh +. /lib/lsb/init-functions + +# Check if the ULIMIT is set in /etc/default/nginx +if [ -n "$ULIMIT" ]; then + # Set the ulimits + ulimit $ULIMIT +fi + +exec $DAEMON $DAEMON_OPTS -g "daemon off;" diff --git a/containers/docker/runit/nsd/run b/containers/docker/runit/nsd/run new file mode 100755 index 00000000..720a812e --- /dev/null +++ b/containers/docker/runit/nsd/run @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +PATH=/sbin:/usr/sbin:/bin:/usr/bin +NAME=nsd # Introduce the short server's name here +DAEMON=/usr/sbin/$NAME # Introduce the server's location here +CONFFILE=/etc/nsd/nsd.conf +DAEMON_ARGS="-d -c $CONFFILE" + +NSDC=/usr/sbin/nsd-control + +# Exit if the package is not installed +[ -x $DAEMON ] || exit 0 + +PIDFILE=$(nsd-checkconf -o pidfile $CONFFILE) + +prepare_environment() { + mkdir -p "$(dirname "$(/usr/sbin/nsd-checkconf -o pidfile $CONFFILE)")" + chown "$(/usr/sbin/nsd-checkconf -o username $CONFFILE)" "$(dirname "$(/usr/sbin/nsd-checkconf -o pidfile $CONFFILE)")" + mkdir -p "$(dirname "$(/usr/sbin/nsd-checkconf -o database $CONFFILE)")" + chown "$(/usr/sbin/nsd-checkconf -o username $CONFFILE)" "$(dirname "$(/usr/sbin/nsd-checkconf -o database $CONFFILE)")" +} + +prepare_environment + +# Check if daemon is running +nc -z -w 4 localhost 10222 +/usr/local/mailinabox/tools/dns_update + +exec $DAEMON $DAEMON_ARGS diff --git a/containers/docker/runit/opendkim/run b/containers/docker/runit/opendkim/run new file mode 100755 index 00000000..9a43d941 --- /dev/null +++ b/containers/docker/runit/opendkim/run @@ -0,0 +1,71 @@ +#!/bin/bash + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/opendkim +NAME=opendkim +DESC="OpenDKIM" +RUNDIR=/var/run/$NAME +USER=opendkim +GROUP=opendkim +SOCKET=local:$RUNDIR/$NAME.sock +PIDFILE=$RUNDIR/$NAME.pid +CONFFILE=/etc/$NAME.conf + +test -x $DAEMON || exit 0 +test -f $CONFFILE || exit 0 + +# Check if mailinabox configuration files are there +test -f /etc/opendkim/SigningTable || exit 0 + +# Include LSB provided init functions +. /lib/lsb/init-functions + +# Include opendkim defaults if available +if [ -f /etc/default/opendkim ] ; then + . /etc/default/opendkim +fi + +if [ -f /etc/opendkim.conf ]; then + CONFIG_SOCKET=`awk '$1 == "Socket" { print $2 }' /etc/opendkim.conf` +fi + +# This can be set via Socket option in config file, so it's not required +if [ -n "$SOCKET" -a -z "$CONFIG_SOCKET" ]; then + DAEMON_OPTS="-p $SOCKET $DAEMON_OPTS" +fi + +DAEMON_OPTS="-f -x $CONFFILE -u $USER -P $PIDFILE $DAEMON_OPTS" + + +# Create the run directory if it doesn't exist +if [ ! -d "$RUNDIR" ]; then + install -o "$USER" -g "$GROUP" -m 755 -d "$RUNDIR" || return 2 + [ -x /sbin/restorecon ] && /sbin/restorecon "$RUNDIR" +fi + +# Clean up stale sockets +if [ -f "$PIDFILE" ]; then + pid=`cat $PIDFILE` + if ! ps -C "$DAEMON" -s "$pid" >/dev/null; then + rm "$PIDFILE" + TMPSOCKET="" + if [ -n "$SOCKET" ]; then + TMPSOCKET="$SOCKET" + elif [ -n "$CONFIG_SOCKET" ]; then + TMPSOCKET="$CONFIG_SOCKET" + fi + if [ -n "$TMPSOCKET" ]; then + # UNIX sockets may be specified with or without the + # local: prefix; handle both + t=`echo $SOCKET | cut -d: -f1` + s=`echo $SOCKET | cut -d: -f2` + if [ -e "$s" -a -S "$s" ]; then + if [ "$t" = "$s" -o "$t" = "local" ]; then + rm "$s" + fi + fi + fi + fi +fi + +exec $DAEMON $DAEMON_OPTS \ No newline at end of file diff --git a/containers/docker/runit/opendmarc/run b/containers/docker/runit/opendmarc/run new file mode 100755 index 00000000..03a2c09b --- /dev/null +++ b/containers/docker/runit/opendmarc/run @@ -0,0 +1,70 @@ +#!/bin/sh + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/opendmarc +NAME=opendmarc +DESC="OpenDMARC" +RUNDIR=/var/run/$NAME +USER=opendmarc +GROUP=opendmarc +SOCKET=local:$RUNDIR/$NAME.sock +PIDFILE=$RUNDIR/$NAME.pid +CONFFILE=/etc/$NAME.conf + +test -x $DAEMON || exit 0 +test -f $CONFFILE || exit 0 + +# Check if mailinabox configuration files are there +test -f /etc/opendkim/SigningTable || exit 0 + +# Include LSB provided init functions +. /lib/lsb/init-functions + +# Include opendkim defaults if available +if [ -f /etc/default/opendmarc ] ; then + . /etc/default/opendmarc +fi + +if [ -f /etc/opendmarc.conf ]; then + CONFIG_SOCKET=`awk '$1 == "Socket" { print $2 }' /etc/opendmarc.conf` +fi + +# This can be set via Socket option in config file, so it's not required +if [ -n "$SOCKET" -a -z "$CONFIG_SOCKET" ]; then + DAEMON_OPTS="-p $SOCKET $DAEMON_OPTS" +fi + +DAEMON_OPTS="-f -c $CONFFILE -u $USER -P $PIDFILE $DAEMON_OPTS" + + +# Create the run directory if it doesn't exist +if [ ! -d "$RUNDIR" ]; then + install -o "$USER" -g "$GROUP" -m 755 -d "$RUNDIR" || return 2 + [ -x /sbin/restorecon ] && /sbin/restorecon "$RUNDIR" +fi +# Clean up stale sockets +if [ -f "$PIDFILE" ]; then + pid=`cat $PIDFILE` + if ! ps -C "$DAEMON" -s "$pid" >/dev/null; then + rm "$PIDFILE" + TMPSOCKET="" + if [ -n "$SOCKET" ]; then + TMPSOCKET="$SOCKET" + elif [ -n "$CONFIG_SOCKET" ]; then + TMPSOCKET="$CONFIG_SOCKET" + fi + if [ -n "$TMPSOCKET" ]; then + # UNIX sockets may be specified with or without the + # local: prefix; handle both + t=`echo $SOCKET | cut -d: -f1` + s=`echo $SOCKET | cut -d: -f2` + if [ -e "$s" -a -S "$s" ]; then + if [ "$t" = "$s" -o "$t" = "local" ]; then + rm "$s" + fi + fi + fi + fi +fi + +exec $DAEMON $DAEMON_OPTS diff --git a/containers/docker/runit/php5-fpm/run b/containers/docker/runit/php5-fpm/run new file mode 100755 index 00000000..480740dd --- /dev/null +++ b/containers/docker/runit/php5-fpm/run @@ -0,0 +1,42 @@ +#!/bin/bash + +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="PHP5 FastCGI Process Manager" +NAME=php5-fpm +DAEMON=/usr/sbin/$NAME +DAEMON_ARGS="-F --fpm-config /etc/php5/fpm/php-fpm.conf" +PIDFILE=/var/run/php5-fpm.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# Don't run if we are running upstart +if init_is_upstart; then + exit 1 +fi + +# +# Function to check the correctness of the config file +# +do_check() +{ + /usr/lib/php5/php5-fpm-checkconf || return 1 + return 0 +} + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +do_check +exec $DAEMON $DAEMON_ARGS \ No newline at end of file diff --git a/containers/docker/runit/postfix/run b/containers/docker/runit/postfix/run new file mode 100755 index 00000000..c06d0f47 --- /dev/null +++ b/containers/docker/runit/postfix/run @@ -0,0 +1,144 @@ +#!/bin/bash + +exec 1>&2 + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DAEMON=/usr/sbin/postfix +NAME=Postfix +TZ= +unset TZ + +# Defaults - don't touch, edit /etc/default/postfix +SYNC_CHROOT="y" + +test -f /etc/default/postfix && . /etc/default/postfix + +test -x $DAEMON && test -f /etc/postfix/main.cf || exit 0 + +. /lib/lsb/init-functions + +configure_instance() { + POSTCONF="postconf" + + # if you set myorigin to 'ubuntu.com' or 'debian.org', it's wrong, and annoys the admins of + # those domains. See also sender_canonical_maps. + + MYORIGIN=$($POSTCONF -h myorigin | tr 'A-Z' 'a-z') + if [ "X${MYORIGIN#/}" != "X${MYORIGIN}" ]; then + MYORIGIN=$(tr 'A-Z' 'a-z' < $MYORIGIN) + fi + if [ "X$MYORIGIN" = Xubuntu.com ] || [ "X$MYORIGIN" = Xdebian.org ]; then + log_failure_msg "Invalid \$myorigin ($MYORIGIN), refusing to start" + log_end_msg 1 + exit 1 + fi + + config_dir=$($POSTCONF -h config_directory) + # see if anything is running chrooted. + NEED_CHROOT=$(awk '/^[0-9a-z]/ && ($5 ~ "[-yY]") { print "y"; exit}' ${config_dir}/master.cf) + + if [ -n "$NEED_CHROOT" ] && [ -n "$SYNC_CHROOT" ]; then + # Make sure that the chroot environment is set up correctly. + oldumask=$(umask) + umask 022 + queue_dir=$($POSTCONF -h queue_directory) + cd "$queue_dir" + + # copy the CA path if specified + ca_path=$($POSTCONF -h smtp_tls_CApath) + case "$ca_path" in + '') :;; # no ca_path + $queue_dir/*) :;; # skip stuff already in chroot, (and to make vim syntax happy: */) + *) + if test -d "$ca_path"; then + dest_dir="$queue_dir/${ca_path#/}" + # strip any/all trailing / + while [ "${dest_dir%/}" != "${dest_dir}" ]; do + dest_dir="${dest_dir%/}" + done + new=0 + if test -d "$dest_dir"; then + # write to a new directory ... + dest_dir="${dest_dir}.NEW" + new=1 + fi + mkdir --parent ${dest_dir} + # handle files in subdirectories + (cd "$ca_path" && find . -name '*.pem' -print0 | cpio -0pdL --quiet "$dest_dir") 2>/dev/null || + (log_failure_msg failure copying certificates; exit 1) + c_rehash "$dest_dir" >/dev/null 2>&1 + if [ "$new" = 1 ]; then + # and replace the old directory + rm -rf "${dest_dir%.NEW}" + mv "$dest_dir" "${dest_dir%.NEW}" + fi + fi + ;; + esac + + # if there is a CA file, copy it + ca_file=$($POSTCONF -h smtp_tls_CAfile) + case "$ca_file" in + $queue_dir/*) :;; # skip stuff already in chroot + '') # no ca_file + # or copy the bundle to preserve functionality + ca_bundle=/etc/ssl/certs/ca-certificates.crt + if [ -f $ca_bundle ]; then + mkdir --parent "$queue_dir/${ca_bundle%/*}" + cp -L "$ca_bundle" "$queue_dir/${ca_bundle%/*}" + fi + ;; + *) + if test -f "$ca_file"; then + dest_dir="$queue_dir/${ca_path#/}" + mkdir --parent "$dest_dir" + cp -L "$ca_file" "$dest_dir" + fi + ;; + esac + + # if we're using unix:passwd.byname, then we need to add etc/passwd. + local_maps=$($POSTCONF -h local_recipient_maps) + if [ "X$local_maps" != "X${local_maps#*unix:passwd.byname}" ]; then + if [ "X$local_maps" = "X${local_maps#*proxy:unix:passwd.byname}" ]; then + sed 's/^\([^:]*\):[^:]*/\1:x/' /etc/passwd > etc/passwd + chmod a+r etc/passwd + fi + fi + + FILES="etc/localtime etc/services etc/resolv.conf etc/hosts \ + etc/nsswitch.conf etc/nss_mdns.config" + for file in $FILES; do + [ -d ${file%/*} ] || mkdir -p ${file%/*} + if [ -f /${file} ]; then rm -f ${file} && cp /${file} ${file}; fi + if [ -f ${file} ]; then chmod a+rX ${file}; fi + done + # ldaps needs this. debian bug 572841 + (echo /dev/random; echo /dev/urandom) | cpio -pdL --quiet . 2>/dev/null || true + rm -f usr/lib/zoneinfo/localtime + mkdir -p usr/lib/zoneinfo + ln -sf /etc/localtime usr/lib/zoneinfo/localtime + + LIBLIST=$(for name in gcc_s nss resolv; do + for f in /lib/*/lib${name}*.so* /lib/lib${name}*.so*; do + if [ -f "$f" ]; then echo ${f#/}; fi; + done; + done) + + if [ -n "$LIBLIST" ]; then + for f in "$LIBLIST"; do + rm -f "$f" + done + tar cf - -C / $LIBLIST 2>/dev/null |tar xf - + fi + umask $oldumask + fi +} +configure_instance + +command_directory=`postconf -h command_directory` +daemon_directory=`$command_directory/postconf -h daemon_directory` +# make consistency check +$command_directory/postfix check +# run Postfix +exec $daemon_directory/master \ No newline at end of file diff --git a/containers/docker/runit/postgrey/run b/containers/docker/runit/postgrey/run new file mode 100755 index 00000000..b1d352dd --- /dev/null +++ b/containers/docker/runit/postgrey/run @@ -0,0 +1,29 @@ +#!/bin/bash + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/postgrey +NAME=postgrey +DESC="postfix greylisting daemon" + +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Gracefully exit if the package has been removed. +test -x $DAEMON || exit 0 + +. /lib/lsb/init-functions + +# Read config file if it is present. +if [ -r /etc/default/$NAME ] +then + . /etc/default/$NAME +fi + +POSTGREY_OPTS="--pidfile=$PIDFILE $POSTGREY_OPTS" +if [ -z "$POSTGREY_TEXT" ]; then + POSTGREY_TEXT_OPT="" +else + POSTGREY_TEXT_OPT="--greylist-text=$POSTGREY_TEXT" +fi + +exec $DAEMON $POSTGREY_OPTS "$POSTGREY_TEXT_OPT" \ No newline at end of file diff --git a/containers/docker/runit/spampd/run b/containers/docker/runit/spampd/run new file mode 100755 index 00000000..545eb2d8 --- /dev/null +++ b/containers/docker/runit/spampd/run @@ -0,0 +1,98 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin +DESC='spam checking proxy daemon' +NAME='spampd' +PROGRAM=/usr/sbin/spampd +#EXECUTABLE=`head -n 1 $PROGRAM | sed -e 's,^#![ ]*/,/,;s,[ ].*$,,'` +EXECUTABLE=/usr/bin/perl +PIDFILE=/var/run/spampd.pid + +if [ -f $PIDFILE ]; then + # If can't delete pidfile, this means process is running ... + rm $PIDFILE || exit 0 +fi + +. /lib/lsb/init-functions + +# set some important defaults (overridable via /etc/default/spampd) +USERID=spampd +GRPID=spampd + +if [ -f /etc/default/$NAME ]; then + . /etc/default/$NAME +fi + +istrue () { + ANS=$(echo $1 | tr A-Z a-z) + [ "$ANS" = 'yes' -o "$ANS" = 'true' -o "$ANS" = 'enable' -o "$ANS" = '1' ] +} + +# +# find out wether to start spampd or not +# +istrue ${STARTSPAMPD} && STARTSPAMPD='true' + +# +# Check wether the program is actually there +# +# return 5 as demanded by LSB 2.1 when program isn't installed. +[ -x $PROGRAM ] || exit 5 + +# +# Calculate final commandline +# +S_TAGALL='' +S_AWL='' +S_LOCALONLY='' + +istrue "$TAGALL" \ +&& S_TAGALL='--tagall' + +istrue "$AUTOWHITELIST" \ +&& S_AWL='--auto-whitelist' + +istrue "$LOCALONLY" \ +&& S_LOCALONLY='--L' + +istrue "$LOGINET" \ +&& LOGTARGET="inet" \ +|| LOGTARGET="unix" + +ARGS="${S_LOCALONLY} ${S_AWL} ${S_TAGALL} " + +[ -n "${LISTENPORT}" ] && ARGS="${ARGS} --port=${LISTENPORT}" + +[ -n "${LISTENHOST}" ] && ARGS="${ARGS} --host=${LISTENHOST}" + +[ -n "${DESTPORT}" ] && ARGS="${ARGS} --relayport=${DESTPORT}" + +[ -n "${DESTHOST}" ] && ARGS="${ARGS} --relayhost=${DESTHOST}" + +[ -n "${PIDFILE}" ] && ARGS="${ARGS} --pid=${PIDFILE}" + +[ -n "${CHILDREN}" ] && ARGS="${ARGS} --children=${CHILDREN}" + +[ -n "${USERID}" ] && ARGS="${ARGS} --user=${USERID}" + +[ -n "${GRPID}" ] && ARGS="${ARGS} --group=${GRPID}" + +[ -n "${LOGTARGET}" ] && ARGS="${ARGS} --logsock=${LOGTARGET}" + +[ -n "${ADDOPTS}" ] && ARGS="${ARGS} ${ADDOPTS}" + +# Don't daemonize +ARGS="${ARGS} --nodetach" + +if ! istrue "${STARTSPAMPD}"; then + log_warning_msg "Starting $DESC: $NAME (disabled in /etc/default/$NAME)." + # LSB 2.1: 6 mean unconfigured. This seems appropriate here. + exit 6 +fi +log_daemon_msg "Starting $DESC" "$NAME" +# if spampd is not installed, return 5 as demanded by LSB 2.1 +if [ ! -x $EXECUTABLE ]; then + log_error_msg "failed! - executable not found" + exit 5 +fi +# start daemon +exec $PROGRAM $ARGS \ No newline at end of file diff --git a/containers/docker/tools/disable_services.sh b/containers/docker/tools/disable_services.sh new file mode 100755 index 00000000..757302d8 --- /dev/null +++ b/containers/docker/tools/disable_services.sh @@ -0,0 +1,14 @@ +SERVICES=/etc/service/* + +for f in $SERVICES +do + service=$(basename "$f") + if [ "$service" = "syslog-ng" ]; then continue; fi; + if [ "$service" = "syslog-forwarder" ]; then continue; fi; + if [ "$service" = "ssh" ]; then continue; fi; + if [ "$service" = "cron" ]; then continue; fi; + if ([ -d /etc/service/$service ] && [ ! -f /etc/service/$service/down ]); then + echo "Creating down file for '$service'" + touch /etc/service/$service/down + fi +done \ No newline at end of file diff --git a/containers/docker/tools/lsb_compat.sh b/containers/docker/tools/lsb_compat.sh new file mode 100755 index 00000000..92042c17 --- /dev/null +++ b/containers/docker/tools/lsb_compat.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# This removes /etc/init.d service if service exists in runit. +# It also creates a symlink from /usr/bin/sv to /etc/init.d/$service +# to support SysV syntax: service $service or /etc/init.d/$service + +SERVICES=/etc/service/* + +for f in $SERVICES +do + service=$(basename "$f") + if [ -d /etc/service/$service ]; then + echo "LSB Compatibility for '$service'" + if [ -f /etc/init.d/$service ]; then + mv /etc/init.d/$service /etc/init.d/$service.lsb + chmod -x /etc/init.d/$service.lsb + fi + ln -s /usr/bin/sv /etc/init.d/$service + fi +done \ No newline at end of file diff --git a/containers/docker/tools/runit_logs.sh b/containers/docker/tools/runit_logs.sh new file mode 100755 index 00000000..3981c94d --- /dev/null +++ b/containers/docker/tools/runit_logs.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# This adds a log/run file on each runit service directory. +# This file make services stdout/stderr output to svlogd log +# directory located in /var/log/runit/$service. + +SERVICES=/etc/service/* + +for f in $SERVICES +do + service=$(basename "$f") + if [ -d /etc/service/$service ]; then + echo "Creating log/run for '$service'" + mkdir -p /etc/service/$service/log + cat > /etc/service/$service/log/run </dev/null 2>&1; then + useradd -m $STORAGE_USER +fi + +# Create the STORAGE_ROOT if it not exists +if [ ! -d $STORAGE_ROOT ]; then + mkdir -p $STORAGE_ROOT +fi + +# Create mailinabox.version file if not exists +if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then + echo $(setup/migrate.py --current) > $STORAGE_ROOT/mailinabox.version + chown $STORAGE_USER:$STORAGE_USER $STORAGE_ROOT/mailinabox.version +fi + + +# Save the global options in /etc/mailinabox.conf so that standalone +# tools know where to look for data. +cat > /etc/mailinabox.conf << EOF; +STORAGE_USER=$STORAGE_USER +STORAGE_ROOT=$STORAGE_ROOT +PRIMARY_HOSTNAME=$PRIMARY_HOSTNAME +PUBLIC_IP=$PUBLIC_IP +PUBLIC_IPV6=$PUBLIC_IPV6 +PRIVATE_IP=$PRIVATE_IP +PRIVATE_IPV6=$PRIVATE_IPV6 +CSR_COUNTRY=$CSR_COUNTRY +EOF diff --git a/setup/dns.sh b/setup/dns.sh index ff6abfe2..dc376a2d 100755 --- a/setup/dns.sh +++ b/setup/dns.sh @@ -113,3 +113,4 @@ chmod +x /etc/cron.daily/mailinabox-dnssec ufw_allow domain +restart_service nsd \ No newline at end of file diff --git a/setup/functions.sh b/setup/functions.sh index d402a888..bcfb5d67 100644 --- a/setup/functions.sh +++ b/setup/functions.sh @@ -70,12 +70,20 @@ function get_default_hostname { # Guess the machine's hostname. It should be a fully qualified # domain name suitable for DNS. None of these calls may provide # the right value, but it's the best guess we can make. - set -- $(hostname --fqdn 2>/dev/null || - hostname --all-fqdns 2>/dev/null || - hostname 2>/dev/null) + set -- $( + get_hostname_from_reversedns || + hostname --fqdn 2>/dev/null || + hostname --all-fqdns 2>/dev/null || + hostname 2>/dev/null) printf '%s\n' "$1" # return this value } +function get_hostname_from_reversedns { + # Do a reverse DNS lookup on our public IPv4 address. The output of + # `host` is complex -- use sed to get the FDQN. + host $(get_publicip_from_web_service 4) | sed "s/.*pointer \(.*\)\./\1/" +} + function get_publicip_from_web_service { # This seems to be the most reliable way to determine the # machine's public IP address: asking a very nice web API diff --git a/setup/mail-postfix.sh b/setup/mail-postfix.sh index f3cfc065..2bdcb4b0 100755 --- a/setup/mail-postfix.sh +++ b/setup/mail-postfix.sh @@ -182,3 +182,4 @@ ufw_allow submission # Restart services restart_service postfix +restart_service postgrey diff --git a/setup/management.sh b/setup/management.sh index 476abd79..66a52f5c 100755 --- a/setup/management.sh +++ b/setup/management.sh @@ -16,10 +16,12 @@ rm -f /usr/local/bin/mailinabox-daemon ln -s `pwd`/management/daemon.py /usr/local/bin/mailinabox-daemon # Create an init script to start the management daemon and keep it -# running after a reboot. -rm -f /etc/init.d/mailinabox -ln -s $(pwd)/conf/management-initscript /etc/init.d/mailinabox -hide_output update-rc.d mailinabox defaults +# running after a reboot, if not runit service exists. +if [ ! -d /etc/service/mailinabox ]; then + rm -f /etc/init.d/mailinabox + ln -s $(pwd)/conf/management-initscript /etc/init.d/mailinabox + hide_output update-rc.d mailinabox defaults +fi # Perform a daily backup. cat > /etc/cron.daily/mailinabox-backup << EOF; @@ -41,8 +43,5 @@ EOF chmod +x /etc/cron.daily/mailinabox-statuschecks -# Start it. Remove the api key file first so that start.sh -# can wait for it to be created to know that the management -# server is ready. -rm -f /var/lib/mailinabox/api.key +# Start it. restart_service mailinabox diff --git a/setup/owncloud.sh b/setup/owncloud.sh index 2a48b73b..4ebb84a2 100755 --- a/setup/owncloud.sh +++ b/setup/owncloud.sh @@ -75,7 +75,7 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then 'instanceid' => '$instanceid', - 'trusted_domains' => + 'trusted_domains' => array ( 0 => '$PRIMARY_HOSTNAME', ), @@ -172,4 +172,5 @@ chmod +x /etc/cron.hourly/mailinabox-owncloud # Enable PHP modules and restart PHP. php5enmod imap +restart_service memcached restart_service php5-fpm diff --git a/setup/preflight.sh b/setup/preflight.sh index 90d36cc1..637eef2e 100644 --- a/setup/preflight.sh +++ b/setup/preflight.sh @@ -4,7 +4,7 @@ if [[ $EUID -ne 0 ]]; then echo echo "sudo $0" echo - exit + exit 1 fi # Check that we are running on Ubuntu 14.04 LTS (or 14.04.xx). @@ -14,7 +14,7 @@ if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" != "U lsb_release -d | sed 's/.*:\s*//' echo echo "We can't write scripts that run on every possible setup, sorry." - exit + exit 1 fi # Check that we have enough memory. @@ -30,6 +30,6 @@ if [ ! -d /vagrant ]; then echo "Your Mail-in-a-Box needs more memory (RAM) to function properly." echo "Please provision a machine with at least 768 MB, 1 GB recommended." echo "This machine has $TOTAL_PHYSICAL_MEM MB memory." - exit + exit 1 fi fi diff --git a/setup/questions.sh b/setup/questions.sh index 08531fe8..0b10a895 100644 --- a/setup/questions.sh +++ b/setup/questions.sh @@ -4,6 +4,7 @@ if [ -z "$NONINTERACTIVE" ]; then # e.g. if we piped a bootstrapping install script to bash to get started. In that # case, the nifty '[ -t 0 ]' test won't work. But with Vagrant we must suppress so we # use a shell flag instead. Really supress any output from installing dialog. + apt_get_quiet update apt_get_quiet install dialog message_box "Mail-in-a-Box Installation" \ "Hello and thanks for deploying a Mail-in-a-Box! diff --git a/setup/start.sh b/setup/start.sh index ca461656..91ec0e11 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -14,8 +14,8 @@ source setup/preflight.sh # the management daemon startup script. if [ -z `locale -a | grep en_US.utf8` ]; then - # Generate locale if not exists - hide_output locale-gen en_US.UTF-8 + # Generate locale if not exists + hide_output locale-gen en_US.UTF-8 fi export LANGUAGE=en_US.UTF-8 @@ -45,93 +45,10 @@ source setup/start.sh EOF chmod +x /usr/local/bin/mailinabox -# Ask the user for the PRIMARY_HOSTNAME, PUBLIC_IP, PUBLIC_IPV6, and CSR_COUNTRY -# if values have not already been set in environment variables. When running -# non-interactively, be sure to set values for all! -source setup/questions.sh +# Start configuration +source setup/configure.sh -# Automatic configuration, e.g. as used in our Vagrant configuration. -if [ "$PUBLIC_IP" = "auto" ]; then - # Use a public API to get our public IP address, or fall back to local network configuration. - PUBLIC_IP=$(get_publicip_from_web_service 4 || get_default_privateip 4) -fi -if [ "$PUBLIC_IPV6" = "auto" ]; then - # Use a public API to get our public IPv6 address, or fall back to local network configuration. - PUBLIC_IPV6=$(get_publicip_from_web_service 6 || get_default_privateip 6) -fi -if [ "$PRIMARY_HOSTNAME" = "auto-easy" ]; then - # Generate a probably-unique subdomain under our justtesting.email domain. - PRIMARY_HOSTNAME=`echo $PUBLIC_IP | sha1sum | cut -c1-5`.justtesting.email -fi - -# Show the configuration, since the user may have not entered it manually. -echo -echo "Primary Hostname: $PRIMARY_HOSTNAME" -echo "Public IP Address: $PUBLIC_IP" -if [ ! -z "$PUBLIC_IPV6" ]; then - echo "Public IPv6 Address: $PUBLIC_IPV6" -fi -if [ "$PRIVATE_IP" != "$PUBLIC_IP" ]; then - echo "Private IP Address: $PRIVATE_IP" -fi -if [ "$PRIVATE_IPV6" != "$PUBLIC_IPV6" ]; then - echo "Private IPv6 Address: $PRIVATE_IPV6" -fi -if [ -f .git ]; then - echo "Mail-in-a-Box Version: " $(git describe) -fi -echo - -# Run some network checks to make sure setup on this machine makes sense. -if [ -z "$SKIP_NETWORK_CHECKS" ]; then - . setup/network-checks.sh -fi - -# For the first time (if the config file (/etc/mailinabox.conf) not exists): -# Create the user named "user-data" and store all persistent user -# data (mailboxes, etc.) in that user's home directory. -# -# If the config file exists: -# Apply the existing configuration options for STORAGE_USER/ROOT -if [ -z "$STORAGE_USER" ]; then - STORAGE_USER=$([[ -z "$DEFAULT_STORAGE_USER" ]] && echo "user-data" || echo "$DEFAULT_STORAGE_USER") -fi - -if [ -z "$STORAGE_ROOT" ]; then - STORAGE_ROOT=$([[ -z "$DEFAULT_STORAGE_ROOT" ]] && echo "/home/$STORAGE_USER" || echo "$DEFAULT_STORAGE_ROOT") -fi - -# Create the STORAGE_USER if it not exists -if ! id -u $STORAGE_USER >/dev/null 2>&1; then - useradd -m $STORAGE_USER -fi - -# Create the STORAGE_ROOT if it not exists -if [ ! -d $STORAGE_ROOT ]; then - mkdir -p $STORAGE_ROOT -fi - -# Create mailinabox.version file if not exists -if [ ! -f $STORAGE_ROOT/mailinabox.version ]; then - echo $(setup/migrate.py --current) > $STORAGE_ROOT/mailinabox.version - chown $STORAGE_USER.$STORAGE_USER $STORAGE_ROOT/mailinabox.version -fi - - -# Save the global options in /etc/mailinabox.conf so that standalone -# tools know where to look for data. -cat > /etc/mailinabox.conf << EOF; -STORAGE_USER=$STORAGE_USER -STORAGE_ROOT=$STORAGE_ROOT -PRIMARY_HOSTNAME=$PRIMARY_HOSTNAME -PUBLIC_IP=$PUBLIC_IP -PUBLIC_IPV6=$PUBLIC_IPV6 -PRIVATE_IP=$PRIVATE_IP -PRIVATE_IPV6=$PRIVATE_IPV6 -CSR_COUNTRY=$CSR_COUNTRY -EOF - -# Start service configuration. +# Start service installation. source setup/system.sh source setup/ssl.sh source setup/dns.sh @@ -147,7 +64,8 @@ source setup/zpush.sh source setup/management.sh # Ping the management daemon to write the DNS and nginx configuration files. -while [ ! -f /var/lib/mailinabox/api.key ]; do +until nc -z -w 4 localhost 10222 +do echo Waiting for the Mail-in-a-Box management daemon to start... sleep 2 done @@ -179,7 +97,7 @@ else echo fi openssl x509 -in $STORAGE_ROOT/ssl/ssl_certificate.pem -noout -fingerprint \ - | sed "s/SHA1 Fingerprint=//" + | sed "s/SHA1 Fingerprint=//" echo echo Then you can confirm the security exception and continue. -echo +echo \ No newline at end of file diff --git a/setup/webmail.sh b/setup/webmail.sh index 47308030..946bfbbc 100755 --- a/setup/webmail.sh +++ b/setup/webmail.sh @@ -121,7 +121,7 @@ EOF # Create writable directories. mkdir -p /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube -chown -R www-data.www-data /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube +chown -R www-data:www-data /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube # Password changing plugin settings # The config comes empty by default, so we need the settings @@ -142,9 +142,9 @@ usermod -a -G dovecot www-data # set permissions so that PHP can use users.sqlite # could use dovecot instead of www-data, but not sure it matters -chown root.www-data $STORAGE_ROOT/mail +chown root:www-data $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 # Enable PHP modules. diff --git a/setup/zpush.sh b/setup/zpush.sh index 3b5d0bdf..37436071 100755 --- a/setup/zpush.sh +++ b/setup/zpush.sh @@ -16,7 +16,7 @@ source /etc/mailinabox.conf # load global vars # Prereqs. apt_install \ - php-soap php5-imap libawl-php php5-xsl + php-soap php5-imap libawl-php php5-xsl git php5enmod imap @@ -26,7 +26,7 @@ needs_update=0 #NODOC if [ ! -f /usr/local/lib/z-push/version ]; then needs_update=1 #NODOC elif [[ $TARGETHASH != `cat /usr/local/lib/z-push/version` ]]; then - # checks if the version + # checks if the version needs_update=1 #NODOC fi if [ $needs_update == 1 ]; then diff --git a/tools/list_all_packages.py b/tools/list_all_packages.py new file mode 100644 index 00000000..9b2eab70 --- /dev/null +++ b/tools/list_all_packages.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 + +import os.path, glob, re + +packages = set() + +def add(line): + global packages + if line.endswith("\\"): line = line[:-1] + packages |= set(p for p in line.split(" ") if p not in("", "apt_install")) + +for fn in glob.glob(os.path.join(os.path.dirname(__file__), "../setup/*.sh")): + with open(fn) as f: + in_apt_install = False + for line in f: + line = line.strip() + if line.startswith("apt_install "): + in_apt_install = True + if in_apt_install: + add(line) + in_apt_install = in_apt_install and line.endswith("\\") + +print("\n".join(sorted(packages)))