From 1eb77c332bca0a74a37f792460903a1fb660befc Mon Sep 17 00:00:00 2001 From: Toilal Date: Sat, 4 Apr 2015 17:50:16 +0200 Subject: [PATCH] Dockerize using phusion/baseimage and runit services --- Dockerfile | 44 ++++-- containers/docker/init.sh | 79 ---------- containers/docker/my_init.d/10-mailinabox.sh | 51 +++++++ .../docker/patch/setup/functions_docker.sh | 22 +++ containers/docker/run | 81 ++++++---- containers/docker/runit/bind9/run | 52 +++++++ containers/docker/runit/dovecot.sh | 3 - containers/docker/runit/dovecot/run | 3 + containers/docker/runit/fail2ban/run | 93 +++++++++++ containers/docker/runit/mailinabox/run | 14 ++ containers/docker/runit/memcached/run | 18 +++ containers/docker/runit/nginx/run | 24 +++ containers/docker/runit/nsd/run | 30 ++++ containers/docker/runit/opendkim/run | 71 +++++++++ containers/docker/runit/opendmarc/run | 70 +++++++++ containers/docker/runit/php5-fpm/run | 42 +++++ containers/docker/runit/postfix/run | 144 ++++++++++++++++++ containers/docker/runit/postgrey/run | 29 ++++ containers/docker/runit/rsyslogd.sh | 3 - containers/docker/runit/spampd/run | 98 ++++++++++++ containers/docker/tools/disable_services.sh | 14 ++ containers/docker/tools/lsb_compat.sh | 20 +++ containers/docker/tools/runit_logs.sh | 26 ++++ setup/dns.sh | 1 + setup/functions.sh | 20 +-- setup/mail-postfix.sh | 1 + setup/management.sh | 10 +- setup/owncloud.sh | 3 +- setup/questions.sh | 1 + setup/start.sh | 8 - 30 files changed, 915 insertions(+), 160 deletions(-) delete mode 100755 containers/docker/init.sh create mode 100755 containers/docker/my_init.d/10-mailinabox.sh create mode 100755 containers/docker/patch/setup/functions_docker.sh create mode 100755 containers/docker/runit/bind9/run delete mode 100755 containers/docker/runit/dovecot.sh create mode 100755 containers/docker/runit/dovecot/run create mode 100755 containers/docker/runit/fail2ban/run create mode 100755 containers/docker/runit/mailinabox/run create mode 100755 containers/docker/runit/memcached/run create mode 100755 containers/docker/runit/nginx/run create mode 100755 containers/docker/runit/nsd/run create mode 100755 containers/docker/runit/opendkim/run create mode 100755 containers/docker/runit/opendmarc/run create mode 100755 containers/docker/runit/php5-fpm/run create mode 100755 containers/docker/runit/postfix/run create mode 100755 containers/docker/runit/postgrey/run delete mode 100755 containers/docker/runit/rsyslogd.sh create mode 100755 containers/docker/runit/spampd/run create mode 100755 containers/docker/tools/disable_services.sh create mode 100755 containers/docker/tools/lsb_compat.sh create mode 100755 containers/docker/tools/runit_logs.sh diff --git a/Dockerfile b/Dockerfile index 3663649c..93f02977 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,14 @@ 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 -VOLUME /data +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 @@ -35,20 +41,28 @@ 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) -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y rsyslog -RUN rm -f /tmp/mailinabox_apt_package_list.txt -RUN apt-get clean - -# Create the user-data user, so the start script doesn't have to. -RUN useradd -m user-data # Now add Mail-in-a-Box to the system. ADD . /usr/local/mailinabox -# We can't know things like the IP address where the container will eventually -# be deployed until the container is started. We also don't want to create any -# private keys during the creation of the image --- that should wait until the -# container is started too. So our whole setup process is deferred until the -# container is started. -RUN mkdir -p /etc/my_init.d -RUN ln -s /usr/local/mailinabox/containers/docker/init.sh /etc/my_init.d/20-mailinabox.sh +# 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/init.sh b/containers/docker/init.sh deleted file mode 100755 index 8f7e377e..00000000 --- a/containers/docker/init.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/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 - export PUBLIC_IP=auto - export PUBLIC_IPV6=auto - export PRIMARY_HOSTNAME=auto - export CSR_COUNTRY=US - export NONINTERACTIVE=1 -fi - -# The phusion/baseimage base image we use for a working Ubuntu -# replaces the normal Upstart system service management with -# a ligher-weight service management system called runit that -# requires a different configuration. We need to create service -# run files that do not daemonize. - -# For most of the services, there is a common pattern we can use: -# execute the init.d script that the Ubuntu package installs, and -# then poll for the termination of the daemon. -function make_runit_service { - INITD_NAME=$1 - WAIT_ON_PROCESS_NAME=$2 - mkdir -p /etc/service/$INITD_NAME - cat > /etc/service/$INITD_NAME/run < /dev/null; then +if [ -z "$SKIP_BUILD" ]; then tput setaf 2 - echo - echo "Creating a new container for your data (mailinabox-userdata)..." + echo "Building/updating base image (mailinabox)..." tput setaf 7 - docker run -d \ - --name mailinabox-userdata \ - -v /home/user-data \ - scratch /bin/does-not-exist-but-thats-ok + 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 mailinabox-userdata for your data." + echo "Using existing container ${CONTAINER_DATA_NAME} for your data." tput setaf 7 fi # End a running container. - -if docker ps -a | grep mailinabox-services > /dev/null; then +if docker inspect ${CONTAINER_NAME} > /dev/null; then tput setaf 2 echo - echo "Destroying mailinabox-services container..." + echo "Destroying ${CONTAINER_NAME} container..." tput setaf 7 - docker rm -f mailinabox-services + docker rm -f ${CONTAINER_NAME} fi # Start container. - tput setaf 2 echo -echo "Starting new container (mailinabox-services)..." +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 \ - --privileged \ -v /dev/urandom:/dev/random \ - -p 25 -p 53/udp -p 53/tcp -p 80 -p 443 -p 587 -p 993 \ - --name mailinabox-services \ - --volumes-from mailinabox-userdata \ - -e "SKIP_NETWORK_CHECKS=$SKIP_NETWORK_CHECKS" \ + -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.sh b/containers/docker/runit/dovecot.sh deleted file mode 100755 index 5e5c1474..00000000 --- a/containers/docker/runit/dovecot.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -/usr/sbin/dovecot -F -c /etc/dovecot/dovecot.conf &> /var/log/dovecot.log 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/rsyslogd.sh b/containers/docker/runit/rsyslogd.sh deleted file mode 100755 index 497d38ad..00000000 --- a/containers/docker/runit/rsyslogd.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -rsyslogd -n 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 < /etc/cron.daily/mailinabox-backup << EOF; diff --git a/setup/owncloud.sh b/setup/owncloud.sh index 57f66767..00923b0c 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/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 10c9afa2..e1b0d613 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -63,14 +63,6 @@ source setup/owncloud.sh source setup/zpush.sh source setup/management.sh -# In Docker, sysvinit services are started automatically. Runit services -# aren't started until after this setup script finishes. But we need -# Dovecot (which is Upstart-only) running in order to create the first -# mail user. So start dovecot now. -if [ ! -z "$IS_DOCKER" ]; then - /usr/sbin/dovecot -c /etc/dovecot/dovecot.conf -fi - # Ping the management daemon to write the DNS and nginx configuration files. until nc -z -w 4 localhost 10222 do