Skip to content
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=mail gid=mail home=/mail/mailboxes/%d/%n
}
driver = mysql
connect = host=##DB_HOST## dbname=servermail user=##DB_USER## password=##DB_PASS##
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
USE servermail;
CREATE TABLE `virtual_domains` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `virtual_users` (
`id` INT NOT NULL AUTO_INCREMENT,
`domain_id` INT NOT NULL,
`password` VARCHAR(106) NOT NULL,
`email` VARCHAR(120) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `virtual_aliases` (
`id` INT NOT NULL AUTO_INCREMENT,
`domain_id` INT NOT NULL,
`source` varchar(100) NOT NULL,
`destination` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#!/bin/bash -eux
export DB_PORT=3306
export DB_HOST=db
export DB_USER=admin
echo $HOSTNAME
sed -i "s/##DB_HOST##/$DB_HOST/" /etc/dovecot/dovecot-sql.conf.ext
sed -i "s/##DB_USER##/$DB_USER/" /etc/dovecot/dovecot-sql.conf.ext
sed -i "s/##DB_PASS##/$DB_PASS/" /etc/dovecot/dovecot-sql.conf.ext
/opt/editconf.py /etc/dovecot/conf.d/15-lda.conf postmaster_address=postmaster@$HOSTNAME
/opt/mysql-check.sh
DB_EXISTS=$(mysql -u$DB_USER -p$DB_PASS -h$DB_HOST -P$DB_PORT -e "SHOW DATABASES LIKE 'servermail';" 2>&1 |grep servermail > /dev/null ; echo "$?")
if [[ DB_EXISTS -eq 1 ]]; then
echo "=> Creating database servermail"
RET=$(mysql -u$DB_USER -p$DB_PASS -h$DB_HOST -P$DB_PORT -e "CREATE DATABASE servermail")
if [[ RET -ne 0 ]]; then
echo "Cannot create database for emails"
exit RET
fi
echo "=> Loading initial database data to servermail"
RET=$(mysql -u$DB_USER -p$DB_PASS -h$DB_HOST -P$DB_PORT servermail < /init.sql)
if [[ RET -ne 0 ]]; then
echo "Cannot load initial database data for emails"
exit RET
fi
echo "=> Done!"
else
echo "=> Skipped creation of database servermail it already exists."
fi
dovecot -F
INSERT INTO `servermail`.`virtual_domains`
(`id` ,`name`)
VALUES
('1', 'example.com'),
('2', 'hostname.example.com');
INSERT INTO `servermail`.`virtual_users`
(`id`, `domain_id`, `password` , `email`)
VALUES
('1', '1', ENCRYPT('firstpassword', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'email1@example.com'),
('2', '1', ENCRYPT('secondpassword', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'email2@example.com');
INSERT INTO `servermail`.`virtual_aliases`
(`id`, `domain_id`, `source`, `destination`)
VALUES
('1', '1', 'alias@example.com', 'email1@example.com');
FROM indiepaas/base-email
RUN apt-get update && \
apt-get install -q -y \
postfix \
postfix-pcre \
postfix-mysql \
supervisor \
ca-certificates \
opendkim \
opendkim-tools \
opendmarc && \
rm -rf /var/lib/apt/lists/*
COPY install.sh /install.sh
COPY postfix_outgoing_mail_header_filters /etc/postfix/outgoing_mail_header_filters
COPY virtual-mailbox-domains.cf /etc/postfix/virtual-mailbox-domains.cf
COPY virtual-mailbox-maps.cf /etc/postfix/virtual-mailbox-maps.cf
COPY virtual-alias-maps.cf /etc/postfix/virtual-alias-maps.cf
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN \
chmod u+x /install.sh && \
/opt/editconf.py /etc/postfix/main.cf \
inet_interfaces=all \
myhostname=##HOSTNAME##\
smtpd_banner="\$myhostname ESMTP Hi, I'm a hosted by an IndieHoster (Debian/Postfix; see https://indiehosters.net/)" \
mydestination=localhost && \
/opt/editconf.py /etc/postfix/master.cf -s -w \
"submission=inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_milters=inet:127.0.0.1:8891 -o smtpd_tls_ciphers=high -o smtpd_tls_protocols=!SSLv2,!SSLv3 -o cleanup_service_name=authclean" && \
/opt/editconf.py /etc/postfix/master.cf -s -w \
"authclean=unix n - - - 0 cleanup -o header_checks=pcre:/etc/postfix/outgoing_mail_header_filters" && \
/opt/editconf.py /etc/postfix/main.cf \
smtpd_tls_security_level=may\
smtpd_tls_auth_only=yes \
smtpd_tls_cert_file=/ssl/ssl_certificate.pem \
smtpd_tls_key_file=/ssl/ssl_private_key.pem \
smtpd_tls_dh1024_param_file=/ssl/dh2048.pem \
smtpd_tls_received_header=yes && \
/opt/editconf.py /etc/postfix/main.cf \
smtpd_relay_restrictions=permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination && \
/opt/editconf.py /etc/postfix/main.cf \
smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt \
smtp_tls_loglevel=2 && \
/opt/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:[127.0.0.1]:10025 && \
/opt/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:unix:dovecot/lmtp && \
/opt/editconf.py /etc/postfix/main.cf \
smtpd_sender_restrictions="reject_non_fqdn_sender,reject_unknown_sender_domain,reject_rhsbl_sender dbl.spamhaus.org" \
smtpd_recipient_restrictions=permit_sasl_authenticated,permit_mynetworks,"reject_rbl_client zen.spamhaus.org",reject_unlisted_recipient && \
/opt/editconf.py /etc/postfix/main.cf \
message_size_limit=134217728 && \
/opt/editconf.py /etc/postfix/main.cf \
smtpd_sasl_type=dovecot \
smtpd_sasl_path=dovecot/auth \
smtpd_sasl_auth_enable=yes && \
/opt/editconf.py /etc/postfix/main.cf \
virtual_mailbox_domains=mysql:/etc/postfix/virtual-mailbox-domains.cf \
virtual_mailbox_maps=mysql:/etc/postfix/virtual-mailbox-maps.cf \
virtual_alias_maps=mysql:/etc/postfix/virtual-alias-maps.cf \
local_recipient_maps=\$virtual_mailbox_maps && \
/opt/editconf.py /etc/postfix/main.cf \
mynetworks="127.0.0.0/8 172.17.42.0/24" && \
/opt/editconf.py /etc/opendmarc.conf -s \
"Syslog=true" \
"Socket=inet:8893@[127.0.0.1]" && \
/opt/editconf.py /etc/postfix/main.cf \
"smtpd_milters=inet:127.0.0.1:8891 inet:127.0.0.1:8893"\
non_smtpd_milters=\$smtpd_milters \
milter_default_action=accept && \
echo "MinimumKeyBits 1024" >> /etc/opendkim && \
echo "ExternalIgnoreList refile:/etc/opendkim/TrustedHosts" >> /etc/opendkim && \
echo "InternalHosts refile:/etc/opendkim/TrustedHosts" >> /etc/opendkim && \
echo "KeyTable refile:/etc/opendkim/KeyTable" >> /etc/opendkim && \
echo "SigningTable refile:/etc/opendkim/SigningTable" >> /etc/opendkim && \
echo "Socket inet:8891@localhost" >> /etc/opendkim && \
echo "RequireSafeKeys false" >> /etc/opendkim
#RUN /opt/editconf.py /etc/postfix/main.cf \
#smtp_tls_security_level=dane \
#smtp_dns_support_level=dnssec
VOLUME ["/var/spool/postfix/"]
CMD /install.sh;/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
#!/bin/bash -eux
export DB_PORT=3306
export DB_HOST=db
export DB_USER=admin
echo $HOSTNAME
sed -i "s/##DB_USER##/$DB_USER/" /etc/postfix/virtual-mailbox-domains.cf
sed -i "s/##DB_PASS##/$DB_PASS/" /etc/postfix/virtual-mailbox-domains.cf
sed -i "s/##DB_USER##/$DB_USER/" /etc/postfix/virtual-mailbox-maps.cf
sed -i "s/##DB_PASS##/$DB_PASS/" /etc/postfix/virtual-mailbox-maps.cf
sed -i "s/##DB_USER##/$DB_USER/" /etc/postfix/virtual-alias-maps.cf
sed -i "s/##DB_PASS##/$DB_PASS/" /etc/postfix/virtual-alias-maps.cf
sed -i "s/##HOSTNAME##/$HOSTNAME/" /etc/postfix/virtual-alias-maps.cf
sed -i "s/##HOSTNAME##/$HOSTNAME/" /etc/postfix/main.cf
/opt/mysql-check.sh
if [ ! -f /etc/opendkim/TrustedHosts ]; then
mkdir -p /etc/opendkim
echo "127.0.0.1" > /etc/opendkim/TrustedHosts
fi
chown -R postfix:postfix /var/spool/postfix/dovecot
# Remove the first line of the Received: header. Note that we cannot fully remove the Received: header
# because OpenDKIM requires that a header be present when signing outbound mail. The first line is
# where the user's home IP address would be.
/^\s*Received:[^\n]*(.*)/ REPLACE Received: from authenticated-user (unknown [127.0.0.1])$1
# Remove other typically private information.
/^\s*User-Agent:/ IGNORE
/^\s*X-Enigmail:/ IGNORE
/^\s*X-Mailer:/ IGNORE
/^\s*X-Originating-IP:/ IGNORE
[supervisord]
nodaemon=true
[program:postfix]
process_name = postfix
command = /etc/init.d/postfix start
startsecs = 0
autorestart = false
[program:opendkim]
process_name = opendkim
command = /etc/init.d/opendkim start
startsecs = 0
autorestart = false
[program:opendmarc]
process_name = opendmarc
command = /etc/init.d/opendmarc start
startsecs = 0
autorestart = false
user = ##DB_USER##
password = ##DB_PASS##
hosts = db
dbname = servermail
query = SELECT destination FROM virtual_aliases WHERE source='%s'
user = ##DB_USER##
password = ##DB_PASS##
hosts = db
dbname = servermail
query = SELECT 1 FROM virtual_domains WHERE name='%s'
user = ##DB_USER##
password = ##DB_PASS##
hosts = db
dbname = servermail
query = SELECT 1 FROM virtual_users WHERE email='%s'
FROM debian:jessie
ENV DEBIAN_FRONTEND noninteractive
RUN \
apt-get update &&\
apt-get install -y \
wget \
ca-certificates &&\
rm -rf /var/lib/apt/lists/*
ADD confd /etc/confd
RUN \
wget https://github.com/kelseyhightower/confd/releases/download/v0.7.1/confd-0.7.1-linux-amd64 -O confd && \
chmod +x confd
RUN \
wget https://get.docker.com/builds/Linux/x86_64/docker-1.2.0 -O docker && \
chmod +x docker
VOLUME ["/etc/confd/", "/etc/haproxy"]
ENTRYPOINT ["/confd"]
CMD ["-interval=60", "-node=172.17.42.1:4001", "-watch=true", "-verbose=true"]
# Confd
The smallest confd docker image in town ;)
## Run
This image will log everything to stdout/stderr.
It was designed to work with HAproxy, but you can use it for anything! There is no configuration, you'll have to mount the config folder. There is a nice example in [indiehosters/confd git repo](https://github.com/indiehosters/dockerfiles/tree/master/server-wide/confd).
```bash
docker run\
-v /haproxy-config:/etc/haproxy/\
-v ./confd/:/etc/confd/\
-v /var/run/docker.sock:/var/run/docker.sock\
indiehosters/confd
```
It works really well with [indiehosters/haproxy](https://registry.hub.docker.com/u/indiehosters/haproxy/) to have automatic configuration of HAproxy backed by `etcd` or `consul`.
[template]
src = "crt-list.tmpl"
dest = "/etc/haproxy/crt-list"
keys = [
"/services"
]
reload_cmd = "/docker kill --signal=\"SIGUSR1\" haproxy"
[template]
src = "haproxy.cfg.tmpl"
dest = "/etc/haproxy/haproxy.cfg"
keys = [
"/services"
]
reload_cmd = "/docker kill --signal=\"SIGUSR1\" haproxy"
{{range $app := lsdir "/services"}}
{{$hostnames := printf "/services/%s/*" $app}}
{{range gets $hostnames}}
{{$hostname := .Key}}
/etc/haproxy/approved-certs/{{base $hostname}}.pem {{base $hostname}}
/etc/haproxy/approved-certs/{{base $hostname}}.pem www.{{base $hostname}}
{{end}}
{{end}}
global
log /dev/log local0 info
log /dev/log local0 notice
maxconn 4096
user haproxy
group haproxy
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
defaults
log global
mode http
option forwardfor
option httpclose
option httplog
option dontlognull
retries 3
timeout connect 5000
timeout client 50000
timeout server 50000
frontend https-in
mode http
bind *:443 ssl no-sslv3 crt-list /etc/haproxy/crt-list crt /etc/haproxy/approved-certs/default.pem
# HSTS (15768000 seconds = 6 months)
rspadd Strict-Transport-Security:\ max-age=15768000
reqadd X-Forwarded-Proto:\ https
{{range $app := lsdir "/services"}}
{{$hostnames := printf "/services/%s/*" $app}}
{{range gets $hostnames}}
{{$hostname := .Key}}
{{$data := json .Value}}
# {{base $hostname}}:
acl https_{{base $hostname}} hdr(host) -i {{base $hostname}}
acl https_{{base $hostname}} hdr(host) -i www.{{base $hostname}}
use_backend {{base $hostname}} if https_{{base $hostname}}
{{end}}
{{end}}
frontend http-in
bind *:80
redirect scheme https code 301
{{range $app := lsdir "/services"}}
{{$hostnames := printf "/services/%s/*" $app}}
{{range gets $hostnames}}
{{$hostname := .Key}}
{{$data := json .Value}}
# {{base $hostname}}:
backend {{base $hostname}}
cookie SERVERID insert nocache indirect
server Server {{$data.ip}}:{{$data.port}} cookie Server
{{end}}
{{end}}
FROM debian:jessie
ENV DEBIAN_FRONTEND noninteractive
# Install Haproxy.
RUN \
apt-get update && \
apt-get install -y haproxy && \
rm -rf /var/lib/apt/lists/*
VOLUME ["/etc/haproxy"]
ENTRYPOINT ["haproxy"]
CMD ["-f", "/etc/haproxy/haproxy.cfg"]
EXPOSE 80
EXPOSE 443
# HAproxy
The smallest HAproxy docker image in town ;)
## Run
```bash
docker run\
-v /haproxy-config:/etc/haproxy\
-p 80:80\
-p 443:443\
pierreozoux/haproxy
```
Have a look to [pierreozoux/confd](https://registry.hub.docker.com/u/pierreozoux/confd/) to have automatic configuration of HAproxy backed by `etcd` or `consul`.