diff --git a/Dockerfile b/Dockerfile index 520c6f1d..8f315389 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,17 +5,30 @@ # To build the image: # sudo docker.io build -t box . -# Run your container the first time with an interactive console so you can -# create your first mail account. -# sudo docker.io run -i -t box +# Run your container. +# -i -t: creates an interactive console so you can poke around (CTRL+D will terminate the container) +# -p ...: Maps container ports to host ports so that the host begins acting as a Mail-in-a-Box. +# sudo docker.io run -i -t -p 22 -p 25:25 -p 53:53/udp -p 443:443 -p 587:587 -p 993:993 box -# Then run it in the background and expose all of the ports so that the *host* acts as a Mail-in-a-Box: -# (the SSH port is only available locally, but other ports are exposed publicly and must be available -# otherwise the container won't start) -# sudo docker.io run -d -p 22 -p 25:25 -p 53:53/udp -p 443:443 -p 587:587 -p 993:993 box +########################################### -FROM ubuntu:14.04 +# 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. They +# provide a better image, but their latest is for an earlier Ubuntu +# version. When they get to Ubuntu 14.04 we'll want to use: +# +# FROM phusion/baseimage: +# +# Until then, use an upgraded image provided by @pjz, based on his +# PR: https://github.com/phusion/baseimage-docker/pull/64 + +FROM pjzz/phusion-baseimage:0.9.10 + # based originally on ubuntu:14.04 + +# Dockerfile metadata. MAINTAINER Joshua Tauberer (http://razor.occams.info) +EXPOSE 22 25 53 443 587 993 # We can't know these values ahead of time, so set them to something # obviously local. The start.sh script will need to be run again once @@ -26,10 +39,11 @@ ENV PUBLIC_IP 192.168.200.1 # Docker-specific Mail-in-a-Box configuration. ENV DISABLE_FIREWALL 1 +ENV NO_RESTART_SERVICES 1 # Our install will fail if SSH is installed and allows password-based authentication. -RUN DEBIAN_FRONTEND=noninteractive apt-get install -qq -y openssh-server -RUN sed -i /etc/ssh/sshd_config -e "s/^#PasswordAuthentication yes/PasswordAuthentication no/g" +# The base image already installs openssh-server. Just edit its configuration. +RUN sed -i -e "s/^#*\s*PasswordAuthentication \(yes\|no\)/PasswordAuthentication no/g" /etc/ssh/sshd_config # Add this repo into the image so we have the configuration scripts. ADD scripts /usr/local/mailinabox/scripts @@ -37,9 +51,12 @@ ADD conf /usr/local/mailinabox/conf ADD tools /usr/local/mailinabox/tools # Start the configuration. -RUN cd /usr/local/mailinabox; scripts/start.sh +RUN cd /usr/local/mailinabox && scripts/start.sh -# How the instance is launched. +# Configure services for docker. ADD containers/docker /usr/local/mailinabox/containers/docker -CMD bash /usr/local/mailinabox/containers/docker/start_services.sh -EXPOSE 22 25 53 443 587 993 +RUN /usr/local/mailinabox/containers/docker/setup_services.sh +RUN ln -s /usr/local/mailinabox/containers/docker/container_start.sh /etc/my_init.d/99-mailinabox.sh + +# Start bash so we can poke around. +CMD ["/sbin/my_init", "--", "bash"] diff --git a/containers/docker/container_start.sh b/containers/docker/container_start.sh new file mode 100755 index 00000000..ce46e0a3 --- /dev/null +++ b/containers/docker/container_start.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# The PUBLIC_HOSTNAME and PUBLIC_IP is not known at the time the docker +# image is built. On the first run of the container, re-run the start +# script with actual values. That will also ask the user for their first +# email user account. +if grep "^PUBLIC_IP=192.168.200.1" /etc/mailinabox.conf > /dev/null; then + echo "Configuring container on first run..." + + # Get the public IP address of the host machine. + export PUBLIC_IP=`curl -s icanhazip.com` + echo Your IP address is $PUBLIC_IP. + + # Get the reverse DNS of that IP address. + export PUBLIC_HOSTNAME=`host $PUBLIC_IP | sed -e "s/.* //" | sed -e "s/\.$//"` + echo Your hostname is $PUBLIC_HOSTNAME. + + # Start configuration again. Hide the terminal. The system services + # have not been started yet, so we can't ask the user to create an + # account yet. + cd /usr/local/mailinabox + scripts/start.sh < /dev/null +fi + diff --git a/containers/docker/setup_services.sh b/containers/docker/setup_services.sh new file mode 100755 index 00000000..650b54c0 --- /dev/null +++ b/containers/docker/setup_services.sh @@ -0,0 +1,58 @@ +#!/bin/bash +echo "Setting up Mail-in-a-Box services..." + +SERVICES="nsd postfix dovecot opendkim nginx php-fastcgi" + +for service in $SERVICES; do + mkdir -p /etc/service/$service +done + +cat </etc/service/nsd/run +#!/bin/sh +exec /usr/sbin/nsd -d +EORUN + +cat </etc/service/postfix/run +#!/bin/sh +# from http://smarden.org/runit/runscripts.html#postfix +exec 1>&2 + +daemon_directory=/usr/lib/postfix \ + command_directory=/usr/sbin \ + config_directory=/etc/postfix \ + queue_directory=/var/spool/postfix \ + mail_owner=postfix \ + setgid_group=postdrop \ + /etc/postfix/postfix-script check || exit 1 + +exec /usr/lib/postfix/master +EORUN + +cat </etc/service/dovecot/run +#!/bin/sh +exec dovecot -F +EORUN + +cat </etc/service/opendkim/run +#!/bin/sh +exec opendkim -f -x /etc/opendkim.conf -u opendkim -P /var/run/opendkim/opendkim.pid +EORUN + +echo "daemon off;" >> /etc/nginx/nginx.conf +cat </etc/service/nginx/run +#!/bin/sh +exec nginx +EORUN + +cat </etc/service/php-fastcgi/run +#!/bin/bash +export PHP_FCGI_CHILDREN=4 PHP_FCGI_MAX_REQUESTS=1000 +exec /usr/bin/php-cgi -q -b /tmp/php-fastcgi.www-data.sock -c /etc/php5/cgi/php.ini +EORUN + +for service in $SERVICES; do + chmod a+x /etc/service/$service/run +done + +echo "Your Mail-in-a-Box services are configured." + diff --git a/containers/docker/start_services.sh b/containers/docker/start_services.sh deleted file mode 100644 index 08bafe46..00000000 --- a/containers/docker/start_services.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# The PUBLIC_HOSTNAME and PUBLIC_IP is not known at the time the docker -# image is built. On the first run of the container, re-run the start -# script with actual values. That will also ask the user for their first -# email user account. -if grep "^PUBLIC_IP=192.168.200.1" /etc/mailinabox.conf > /dev/null; then - echo "Configuring container on first run..." - - # Get the public IP address of the host machine. - export PUBLIC_IP=`curl -s icanhazip.com` - echo Your IP address is $PUBLIC_IP. - - # Get the reverse DNS of that IP address. - export PUBLIC_HOSTNAME=`host $PUBLIC_IP | sed -e "s/.* //" | sed -e "s/\.$//"` - echo Your hostname is $PUBLIC_HOSTNAME. - - # Start configuration again. - cd /usr/local/mailinabox - scripts/start.sh -fi - -echo "Starting Mail-in-a-Box services..." - -service nsd start -service postfix start -dovecot # it's integration with Upstart doesn't work in docker -service opendkim start -service nginx start -service php-fastcgi start - -if [ -t 0 ] -then - # This is an interactive shell. You get a command prompt within - # the container. - # - # You get here by running 'docker run -i -t'. - - echo "Welcome to your Mail-in-a-Box." - bash - -else - # This is a non-interactive shell. It loops forever to prevent - # the docker container from stopping. - # - # You get here by omitting '-t' from the docker run arguments. - - echo "Your Mail-in-a-Box is running..." - while true; do sleep 10; done -fi diff --git a/scripts/start.sh b/scripts/start.sh index e963b071..71267c92 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -52,6 +52,17 @@ PUBLIC_HOSTNAME=$PUBLIC_HOSTNAME PUBLIC_IP=$PUBLIC_IP EOF +# For docker, we don't want any of our scripts to start daemons. +# Mask the 'service' program by defining a function of the same name +# so that whenever we try to restart a service we just silently do +# nothing. +if [ "$NO_RESTART_SERVICES" == "1" ]; then + function service { + # we could output some status, but it's not important + echo skipping service $@ > /dev/null; + } +fi + # Start service configuration. . scripts/system.sh . scripts/dns.sh diff --git a/scripts/system.sh b/scripts/system.sh index cd44d0df..d1fc5966 100755 --- a/scripts/system.sh +++ b/scripts/system.sh @@ -2,8 +2,8 @@ source scripts/functions.sh # load our functions # Base system configuration. -apt-get -q -q update -apt-get -q -y upgrade +apt-get -qq update +apt-get -qq -y upgrade # Install openssh-server to ensure that the end result is consistent across all Mail-in-a-Boxes. apt_install openssh-server