Now with ansible/untested

This commit is contained in:
Aeris 2018-11-21 02:12:52 +01:00
parent 3a4ecf24f4
commit 6db9281d58
29 changed files with 1042 additions and 6 deletions

51
.env Normal file
View file

@ -0,0 +1,51 @@
# postfix/dovecot user management
DATABASE_USER=mail
DATABASE_PASSWORD=db_password
DATABASE_NAME=postfix
#postfix
MYORIGIN=creditcards.bayern
MYHOSTNAME=mail.creditcards.bayern
#imap
MAILDOMAIN=mial.creditcards.bayern
#spam
PORT=11334
PASSWORD=nichtsicher
#roundcube
ROUNDCUBEMAIL_DEFAULT_HOST=imap
ROUNDCUBEMAIL_SMTP_SERVER=smtp
ROUNDCUBEMAIL_PLUGINS=archive,zipdownload,managesieve,password
ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=100M
ROUNDCUBEMAIL_DB_TYPE=mysql
ROUNDCUBEMAIL_DB_HOST=db
ROUNDCUBEMAIL_DB_USER=mail
ROUNDCUBEMAIL_DB_PASSWORD=BGun02otSchuj3z
ROUNDCUBEMAIL_DB_NAME=postfix
#postfixadmin
DBTYPPE=mysql
DBHOST=db
DBUSER=mail
DBNAME=postfix
DBPASS=db_password
SMTPHOST=smtp
DOMAIN=creditcards.bayern
#database
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASES="postfix mailman"
MYSQL_USER=mail
MYSQL_PASSWORD=db_password
#mailman-core
DATABASE_URL=mysql://mail:db_password@db/mailman
DATABASE_TYPE=mysql
DATABASE_CLASS=mailman.database.mysql.MySQLDatabase
HYPERKITTY_API_KEY=someapikey
#mailman-web
DATABASE_URL=mysql://mail:db_password@db/mailman
DATABASE_TYPE=mysql
HYPERKITTY_API_KEY=someapikey
SECRET_KEY=thisisaverysecretkey
DYLD_LIBRARY_PATH=/usr/local/mysql/lib/
SERVE_FROM_DOMAIN=lists.creditcards.bayern
DJANGO_ALLOWED_HOSTS=mailman.creditcards.bayern
MAILMAN_ADMIN_USER=admin
MAILMAN_ADMIN_EMAIL=a3x@eris.cc
UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static

7
deploy/ansible.cfg Normal file
View file

@ -0,0 +1,7 @@
[defaults]
roles_path = roles
legacy_playbook_variable = no
nocows = 1
[ssh_connection]
pipelining = yes

1
deploy/deploy Normal file
View file

@ -0,0 +1 @@
/usr/bin/env ansible-playbook --ask-become -i inventory playbook.yml --vault-password-file vaultpass "$@"

5
deploy/playbook.yml Normal file
View file

@ -0,0 +1,5 @@
---
- hosts: servers
become: yes
roles:
- { role: mailserver }

View file

@ -0,0 +1,184 @@
version: '3'
services:
smtp:
container_name: smtp
build: ./smtp
restart: always
depends_on:
- imap
- db
ports:
- '25:25'
- '587:587'
links:
- imap:imap
- db:db
- spam:spam
volumes:
- ./smtp/main.cf:/etc/postfix/main.cf:ro
- ./smtp/master.cf:/etc/postfix/master.cf:ro
- /data/mailserver/mailman/data:/mailman
- mails:/home/vmail
- certs:/certs
labels:
- "traefik.enable=false"
imap:
container_name: imap
build: ./imap
restart: always
depends_on:
- extractor
- db
ports:
- '993:993'
- '4190:4190'
expose:
- '24'
- '8472'
links:
- db
- spam
volumes:
- mails:/home/vmail
- certs:/certs
labels:
- "traefik.enable=false"
spam:
container_name: spam
build: ./spam
restart: always
expose:
- 11334
volumes:
- spam:/data
- /etc/localtime:/etc/localtime:ro
labels:
- "traefik.frontend.rule=Host:spam.creditcards.bayern"
- "traefik.port=11334"
webmail:
container_name: webmail
image: roundcube/roundcubemail:latest-apache
depends_on:
- smtp
- imap
- db
restart: always
links:
- imap:imap
- smtp:smtp
labels:
- "traefik.frontend.rule=Host:mail.creditcards.bayern"
- "traefik.port=80"
admin:
container_name: admin
links:
- db:db
depends_on:
- db
image: hardware/postfixadmin:latest
expose:
- "8888"
restart: always
labels:
- "traefik.frontend.rule=Host:admin.creditcards.bayern"
- "traefik.port=8888"
db:
container_name: mariadb
image: mariadb:10.3
restart: always
volumes:
- database:/var/lib/mysql
- ./docker-entrypoint.sh:/docker-entrypoint.sh
labels:
- "traefik.enable=false"
mailman-core:
image: maxking/mailman-core:latest
container_name: mailman-core
hostname: mailman-core
volumes:
- /data/mailserver/mailman/core:/opt/mailman/
- ./mailman-extra.cfg:/opt/mailman/core/mailman-extra.cfg
links:
- db
- smtp
- imap
depends_on:
- db
labels:
- "traefik.enable=false"
mailman-web:
image: maxking/mailman-web:latest
container_name: mailman-web
hostname: mailman-web
expose:
- 8000
- 8080
depends_on:
- db
links:
- mailman-core:mailman-core
- db:db
volumes:
- /data/mailserver/mailman/web:/opt/mailman-web-data
labels:
#- "traefik.frontend.rule=Host:mailman.creditcards.bayern"
#- "traefik.port=8000"
- "traefik.enable=false"
nginx:
container_name: nginx
image: nginx:mainline
restart: always
expose:
- 80
links:
- mailman-web:mailman-web
volumes:
- ./nginx/:/etc/nginx/conf.d/
- /data/mailserver/mailman/web:/opt/mailman/
labels:
- "traefik.frontend.rule=Host:mailman.creditcards.bayern"
- "traefik.port=80"
traefik:
container_name: traefik
image: traefik # The official Traefik docker image
command: --api --docker # Enables the web UI and tells Traefik to listen to docker
restart: always
ports:
- "80:80" # The HTTP port
- "443:443"
- "8080:8080" # The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
- ./traefik/:/etc/traefik
labels:
- "traefik.frontend.rule=Host:traefik.creditcards.bayern"
extractor:
container_name: extractor
image: danielhuisman/traefik-certificate-extractor
volumes:
- /data/mailserver/traefik:/app/data
- certs:/app/certs_flat
labels:
- "traefik.enable=false"
volumes:
database:
mails:
certs:
spam:

View file

@ -0,0 +1,206 @@
#!/bin/bash
set -eo pipefail
shopt -s nullglob
# if command starts with an option, prepend mysqld
if [ "${1:0:1}" = '-' ]; then
set -- mysqld "$@"
fi
# skip setup if they want an option that stops mysqld
wantHelp=
for arg; do
case "$arg" in
-'?'|--help|--print-defaults|-V|--version)
wantHelp=1
break
;;
esac
done
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
_check_config() {
toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" )
if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
cat >&2 <<-EOM
ERROR: mysqld failed while attempting to check config
command was: "${toRun[*]}"
$errors
EOM
exit 1
fi
}
# Fetch value from server config
# We use mysqld --verbose --help instead of my_print_defaults because the
# latter only show values present in config files, and not server defaults
_get_config() {
local conf="$1"; shift
"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
| awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
# match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)"
}
# allow the container to be started with `--user`
if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
_check_config "$@"
DATADIR="$(_get_config 'datadir' "$@")"
mkdir -p "$DATADIR"
find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
exec gosu mysql "$BASH_SOURCE" "$@"
fi
if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
# still need to check config, container may have started with --user
_check_config "$@"
# Get config
DATADIR="$(_get_config 'datadir' "$@")"
if [ ! -d "$DATADIR/mysql" ]; then
file_env 'MYSQL_ROOT_PASSWORD'
if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
echo >&2 'error: database is uninitialized and password option is not specified '
echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
exit 1
fi
mkdir -p "$DATADIR"
echo 'Initializing database'
# "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here)
mysql_install_db --datadir="$DATADIR" --rpm "${@:2}"
echo 'Database initialized'
SOCKET="$(_get_config 'socket' "$@")"
"$@" --skip-networking --socket="${SOCKET}" &
pid="$!"
mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
for i in {30..0}; do
if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
break
fi
echo 'MySQL init process in progress...'
sleep 1
done
if [ "$i" = 0 ]; then
echo >&2 'MySQL init process failed.'
exit 1
fi
if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
# sed is for https://bugs.mysql.com/bug.php?id=20545
mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
fi
if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
fi
rootCreate=
# default root to listen for connections from anywhere
file_env 'MYSQL_ROOT_HOST' '%'
if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
# no, we don't care if read finds a terminating character in this heredoc
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
read -r -d '' rootCreate <<-EOSQL || true
CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ;
EOSQL
fi
"${mysql[@]}" <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
${rootCreate}
DROP DATABASE IF EXISTS test ;
FLUSH PRIVILEGES ;
EOSQL
if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
fi
file_env 'MYSQL_DATABASE'
if [ "$MYSQL_DATABASE" ]; then
echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
mysql+=( "$MYSQL_DATABASE" )
fi
# create several databases using the MYSQL DATABASES env
#example: export MYSQL_DATABASES = "one two three"
file_env 'MYSQL_DATABASES'
if [ "$MYSQL_DATABASES" ]; then
for databaseName in $MYSQL_DATABASES; do
echo "CREATE DATABASE IF NOT EXISTS \`$databaseName\` ;" | "${mysql[@]}"
done
fi
file_env 'MYSQL_USER'
file_env 'MYSQL_PASSWORD'
if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
if [ "$MYSQL_DATABASE" ]; then
echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
fi
#create the permissions for the different databases created with the db user
if [ "$MYSQL_DATABASES" ]; then
for databaseName in $MYSQL_DATABASES; do
echo "GRANT ALL ON \`$databaseName\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
done
fi
fi
echo
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done
if ! kill -s TERM "$pid" || ! wait "$pid"; then
echo >&2 'MySQL init process failed.'
exit 1
fi
echo
echo 'MySQL init process done. Ready for start up.'
echo
fi
fi
exec "$@"

View file

@ -0,0 +1,12 @@
disable_plaintext_auth = yes
auth_mechanisms = plain login
passdb {
driver = sql
args = /etc/dovecot/sql.conf.ext
}
userdb {
driver = static
args = uid=vmail gid=vmail mail_home=/home/vmail/%d/%n mail_location=mbox:~/mail:INBOX=/home/vmail/%u
}

View file

@ -0,0 +1,59 @@
service imap-login {
inet_listener imap {
#port = 143
}
inet_listener imaps {
#port = 993
#ssl = yes
}
}
service pop3-login {
inet_listener pop3 {
#port = 110
}
inet_listener pop3s {
#port = 995
#ssl = yes
}
}
service imap {
}
service pop3 {
}
service auth {
inet_listener {
address = * ::
port = 8472
}
unix_listener auth-userdb {
mode = 0600
user = vmail
}
user = dovecot
}
service auth-worker {
user = vmail
}
service dict {
unix_listener dict {
}
}
service lmtp {
inet_listener lmtp {
address = * ::
port = 24
}
}
plugin {
sieve = file:~/sieve;active=~/.dovecot.sieve
}

View file

@ -0,0 +1,3 @@
protocol lmtp {
mail_plugins = $mail_plugins sieve
}

View file

@ -0,0 +1,8 @@
service managesieve-login {
}
service managesieve {
}
protocol sieve {
}

View file

@ -0,0 +1,10 @@
plugin {
sieve_extensions = +spamtest +spamtestplus
sieve_spamtest_status_type = score
sieve_spamtest_status_header = \
X-Spam-Score: (-?[[:digit:]]+\.[[:digit:]]).*
sieve_spamtest_max_value = 5.0
sieve_before = /var/lib/dovecot/sieve/global_sieves/move_to_spam_folder.sieve
}

View file

@ -0,0 +1,26 @@
FROM alpine:latest
RUN apk add --no-cache dovecot dovecot-pigeonhole-plugin
RUN adduser -u 5000 -g vmail -s /usr/bin/nologin -h /home/vmail -S vmail
RUN mkdir /etc/dovecot/sieve-filter
RUN ln -s /usr/bin/vendor_perl/spamc /etc/dovecot/sieve-filter/spamc
ADD dovecot.conf /etc/dovecot/dovecot.conf
ADD 10-auth.conf /etc/dovecot/conf.d/10-auth.conf
ADD 10-master.conf /etc/dovecot/conf.d/10-master.conf
ADD 20-lmtp.conf /etc/dovecot/conf.d/20-lmtp.conf
ADD 90-sieve.conf /etc/dovecot/conf.d/90-sieve.conf
ADD move_to_spam_folder.sieve /var/lib/dovecot/sieve/global_sieves/move_to_spam_folder.sieve
ADD ./start.sh /start.sh
RUN sievec /var/lib/dovecot/sieve/global_sieves
EXPOSE 993
ENTRYPOINT ["/start.sh"]

View file

@ -0,0 +1,12 @@
#auth_verbose = yes
#auth_debug = yes
#auth_debug_passwords = yes
#auth_verbose_passwords = yes
#mail_debug = yes
log_path = /dev/stdout
!include_try /usr/share/dovecot/protocols.d/*.protocol
!include conf.d/*.conf
protocols = imap lmtp sieve

View file

@ -0,0 +1,8 @@
require "spamtestplus";
require "fileinto";
require "relational";
require "comparator-i;ascii-numeric";
if header :contains "X-Spam-Flag" "YES" {
fileinto "Spam";
}

View file

@ -0,0 +1,16 @@
#!/bin/sh
#if [ -n "${DATABASE_NAME}" -a -n "${DATABASE_USER}" -a -n "${DATABASE_PASSWORD}" ] ; then
echo -e "driver = mysql\n \
connect = host=db dbname=${DATABASE_NAME} user=${DATABASE_USER} password=${DATABASE_PASSWORD}\n \
default_pass_scheme = SHA512-CRYPT\n \
password_query = SELECT username as user, password FROM mailbox WHERE username='%u';" > /etc/dovecot/sql.conf.ext
#fi
#if [ -n "${MAILDOMAIN}" ]; then
echo -e "ssl = yes\n \
ssl_cert = </certs/${MAILDOMAIN}.crt\n \
ssl_key = </certs/${MAILDOMAIN}.key" > /etc/dovecot/conf.d/10-ssl.conf
#fi
dovecot -F

View file

@ -0,0 +1,10 @@
# mailman-extra.cfg
[mta]
incoming: mailman.mta.postfix.LMTP
outgoing: mailman.mta.deliver.deliver
lmtp_host: imap
lmtp_port: 8472
smtp_host: smtp
smtp_port: 25
configuration: /etc/postfix-mailman.cfg

View file

@ -0,0 +1,11 @@
FROM alpine:latest
RUN adduser -u 5000 -g vmail -s /usr/bin/nologin -h /home/vmail -S vmail
RUN apk add --no-cache postfix ca-certificates
ADD ./main.cf /etc/postfix/main.cf
ADD ./master.cf /etc/postfix/master.cf
ADD ./start.sh /start.sh
ENTRYPOINT ["/start.sh"]

View file

@ -0,0 +1,90 @@
compatibility_level = 2
smtpd_banner = $myhostname ESMTP $mail_name (Hail Eris!)
biff = no
append_dot_mydomain = no
readme_directory = no
smtpd_helo_required = yes
strict_rfc821_envelopes = yes
disable_vrfy_command = yes
unknown_address_reject_code = 554
unknown_hostname_reject_code = 554
unknown_client_reject_code = 554
#smtpd_tls_key_file=/certs/privkey.pem
#smtpd_tls_cert_file=/certs/cert.pem
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtp_tls_security_level = may
smtpd_sasl_path = inet:imap:8472
smtpd_sasl_type = dovecot
smtpd_sasl_auth_enable = yes
smtputf8_enable = no
smtputf8_autodetect_classes = bounce
#smtpd_recipient_restrictions =
# permit_sasl_authenticated,
# permit_mynetworks,
# reject_unauth_destination
# Let's try ze new config stuff!
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_rbl_client ix.dnsbl.manitu.net,
reject_rbl_client bl.spamcop.net,
reject_rbl_client multi.surbl.org,
reject_rbl_client dnsbl-1.uceprotect.net,
reject_rbl_client cbl.abuseat.org,
reject_rbl_client combined.rbl.msrbl.net,
reject_rbl_client b.barracudacentral.org,
reject_invalid_hostname,
reject_non_fqdn_hostname,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unknown_sender_domain,
reject_unknown_recipient_domain,
reject_unauth_pipelining,
reject_unauth_destination,
reject_unlisted_recipient
smtpd_sender_restrictions =
# reject_sender_login_mismatch #too harsh
permit_sasl_authenticated
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.16.0.0/12
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
virtual_transport = lmtp:inet:imap:24
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
message_size_limit = 20480000
# Milter setup
smtpd_milters = inet:spam:11332
milter_default_action = accept
milter_protocol = 6
unknown_local_recipient_reject_code = 550
owner_request_special = no
transport_maps = hash:/mailman/var/data/postfix_lmtp
local_recipient_maps = hash:/mailman/var/data/postfix_lmtp
relay_domains = hash:/mailman/var/data/postfix_domains

View file

@ -0,0 +1,55 @@
#
# Postfix master process configuration file. For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line: http://www.postfix.org/master.5.html).
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (yes) (never) (100)
# ==========================================================================
smtp inet n - - - - smtpd
-o content_filter=spamassassin
submission inet n - - - - smtpd
-o syslog_name=postfix/submission
-o smtpd_sasl_auth_enable=yes
pickup unix n - - 60 1 pickup
cleanup unix n - - - 0 cleanup
qmgr unix n - n 300 1 qmgr
tlsmgr unix - - - 1000? 1 tlsmgr
rewrite unix - - - - - trivial-rewrite
bounce unix - - - - 0 bounce
defer unix - - - - 0 bounce
trace unix - - - - 0 bounce
verify unix - - - - 1 verify
flush unix n - - 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - - - - smtp
relay unix - - - - - smtp
showq unix n - - - - showq
error unix - - - - - error
retry unix - - - - - error
discard unix - - - - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - - - - lmtp
anvil unix - - - - 1 anvil
scache unix - - - - 1 scache
maildrop unix - n n - - pipe
flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}

View file

@ -0,0 +1,36 @@
#!/bin/sh
#if [ -n "${MYORIGIN}" -a -n "${MYHOSTNAME}" ]; then
echo -e "myorigin = ${MYORIGIN}\n \
myhostname = ${MYHOSTNAME} \
smtpd_tls_key_file = /certs/${MYHOSTNAME}.key \
smtpd_tls_cert_file=/certs/${MYHOSTNAME}.crt" >> /etc/postfix/main_addendum.cf
#fi
#if [ -n "${DATABASE_USER}" -a -n "${DATBASE_PASSWORD}" -a -n "${DATABASE_NAME}" ]; then
echo -e "user = ${DATABASE_USER}\n \
password = ${DATABASE_PASSWORD}\n \
hosts = db\n \
dbname = ${DATABASE_NAME}\n \
table = alias\n \
select_field = goto\n \
where_field = address" > /etc/postfix/virtual_alias_maps.cf;
echo -e "user = ${DATABASE_USER}\n \
password = ${DATABASE_PASSWORD}\n \
hosts = db\n \
dbname = ${DATABASE_NAME}\n \
table = domain\n \
select_field = domain\n \
where_field = domain" > /etc/postfix/virtual_mailbox_domains.cf;
echo -e "user = ${DATABASE_USER}\n \
password = ${DATABASE_PASSWORD}\n \
hosts = db\n \
dbname = ${DATABASE_NAME}\n \
table = mailbox\n \
select_field = maildir\n \
where_field = username" > /etc/postfix/virtual_mailbox_maps.cf;
#fi
postfix start-fg

View file

@ -0,0 +1,15 @@
FROM alpine:edge
# We have to upgrade musl, or rspamd will not work.
RUN echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \
&& apk add --no-cache rspamd rspamd-controller rsyslog ca-certificates
RUN mkdir /run/rspamd
RUN echo 'type = "console";' > /etc/rspamd/override.d/logging.inc \
&& echo 'pidfile = false;' > /etc/rspamd/override.d/options.inc
COPY start.sh /start.sh
CMD ["/start.sh"]

View file

@ -0,0 +1,14 @@
#!/bin/sh
SECURE_IP=${SECURE_IP:-"127.0.0.1"}
PASSWORD=${PASSWORD:-"mailu"}
ENABLE_PASSWORD=${ENABLE_PASSWORD:-$PASSWORD}
cat << EOF > /etc/rspamd/override.d/worker-controller.inc
bind_socket = "0.0.0.0:${PORT}";
secure_ip = "${SECURE_IP}";
password = "${PASSWORD}";
enable_password = "${PASSWORD}";
EOF
/usr/sbin/rspamd -f --insecure

View file

@ -0,0 +1,41 @@
debug = false
logLevel = "ERROR"
defaultEntryPoints = ["https","http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
minVersion = "VersionTLS11"
cipherSuites = [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
]
[api]
[retry]
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "creditcards.bayern"
watch = true
exposedByDefault = false
[acme]
email = "noc@creditcards.bayern"
storage = "/etc/traefik/acme.json"
entryPoint = "https"
onHostRule = true
KeyType = "EC256"
keyType = "EC256"
[acme.httpChallenge]
entryPoint = "http"

View file

@ -0,0 +1,66 @@
---
- name: Create project folder
file:
name: "{{ docker_project_folder }}/mailserver"
state: directory
- name: Create data folder
file:
name: "{{ docker_data_folder }}/mailserver"
state: directory
- name: Create nginx include folder
file:
name: "{{ docker_data_folder }}/mailserver/nginx"
state: directory
- name: Copy nginx config \#1
template:
src: mailman-web.conf.j2
dest: "{{ docker_project_folder }}/mailserver/nginx/mailman-web.conf"
- name: Copy env
template:
src: env.j2
dest: "{{ docker_project_folder }}/mailserver/.env"
- name: Copy smtp
copy:
src: smtp
dest: "{{ docker_project_folder }}/mailserver/smtp"
- name: Copy imap
copy:
src: imap
dest: "{{ docker_project_folder }}/mailserver/imap"
- name: Copy spam
copy:
src: spam
dest: "{{ docker_project_folder }}/mailserver/spam"
- name: Copy traefik
copy:
src: traefik
dest: "{{ docker_project_folder }}/mailserver/traefik"
- name: Copy mailman-conf
copy:
src: mailman-extra.cfg
dest: "{{ docker_project_folder }}/mailserver/mailman-extra.cfg"
- name: New Mysql EntryPoint
copy:
src: docker-entrypoint.sh
dest: "{{ docker_project_folder }}/mailserver/docker-entrypoint.sh"
- name: Copy docker-compose
copy:
src: docker-compose.yml
dest: "{{ docker_project_folder }}/mailserver/docker-compose.yml"
- name: Start mailserver
docker_service:
project_src: "{{ docker_project_folder }}/mailserver"
pull: yes

View file

@ -0,0 +1,51 @@
# postfix/dovecot user management
DATABASE_USER=mail
DATABASE_PASSWORD={{ DB_PASSWORD }}
DATABASE_NAME=postfix
#postfix
MYORIGIN={{ DOMAIN }}
MYHOSTNAME=mail.{{ DOMAIN }}
#imap
MAILDOMAIN=mail.{{ DOMAIN }}
#spam
PORT=11334
PASSWORD= {{ WEB_PASSWORD }}
#roundcube
ROUNDCUBEMAIL_DEFAULT_HOST=imap
ROUNDCUBEMAIL_SMTP_SERVER=smtp
ROUNDCUBEMAIL_PLUGINS=archive,zipdownload,managesieve,password
ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=100M
ROUNDCUBEMAIL_DB_TYPE=mysql
ROUNDCUBEMAIL_DB_HOST=db
ROUNDCUBEMAIL_DB_USER=mail
ROUNDCUBEMAIL_DB_PASSWORD={{ DB_PASSWORD }}
ROUNDCUBEMAIL_DB_NAME=postfix
#postfixadmin
DBTYPPE=mysql
DBHOST=db
DBUSER=mail
DBNAME=postfix
DBPASS={{ DB_PASSWORD }}
SMTPHOST=smtp
DOMAIN={{ DOMAIN }}
#database
MYSQL_ROOT_PASSWORD={{ DB_ROOT_PASSWORD }}
MYSQL_DATABASES="postfix mailman"
MYSQL_USER=mail
MYSQL_PASSWORD={{ DB_PASSWORD }}
#mailman-core
DATABASE_URL=mysql://mail:{{ DB_PASSWORD }}@db/mailman
DATABASE_TYPE=mysql
DATABASE_CLASS=mailman.database.mysql.MySQLDatabase
HYPERKITTY_API_KEY=someapikey
#mailman-web
DATABASE_URL=mysql://mail:{{ DB_PASSWORD }}@db/mailman
DATABASE_TYPE=mysql
HYPERKITTY_API_KEY=someapikey
SECRET_KEY={{ WEB_PASSWORD }}
DYLD_LIBRARY_PATH=/usr/local/mysql/lib/
SERVE_FROM_DOMAIN=mail.{{ DOMAIN }}
DJANGO_ALLOWED_HOSTS=mailman.{{ DOMAIN }}
MAILMAN_ADMIN_USER=admin
MAILMAN_ADMIN_EMAIL=a3x@eris.cc
UWSGI_STATIC_MAP=/static=/opt/mailman-web-data/static

View file

@ -0,0 +1,15 @@
server {
listen 80;
server_name mailman.{{ DOMAIN }};
location / {
# First attempt to serve request as file, then
uwsgi_pass mailman-web:8080;
include uwsgi_params;
uwsgi_read_timeout 300;
}
}

View file

@ -0,0 +1,13 @@
---
become_method: sudo
ansible_ask_become_pass: yes
docker_data_folder: /data
docker_project_folder: /var/docker
DOMAIN: creditcards.bayern
# vault
DB_PASSWORD: "{{ vault_db_password }}"
DB_ROOT_PASSWORD: "{{ vault_db_root_password }}"
WEB_PASSWORD: "{{ vault_web_password }}"

View file

@ -0,0 +1,11 @@
$ANSIBLE_VAULT;1.1;AES256
61343433613065613963336562373266343631613831386333316564393132373861383730383131
6136663533343330363131623433333535616230343433650a373438373732636461386362656638
32653463663136353363666230323930633866323239636238323831316235303432636332383732
6563333066313533360a353639353935316531353763663434613463303439346162373431343134
37383761616466333938393165653935653565336434303835373239643962336161613363626534
34303037363634666332396635616231383462383230656530343036346164333265636637303237
31633839383031376137376136383738656639616666326637303532613735653734396630333838
36643465636439323364646362323164333563333131333666666564646466633736633137363430
66363765376364306366353664356131623136646134613532623532346362646661346365323135
6431643536393461313632613335626532306561333836643534

View file

@ -91,7 +91,7 @@ services:
ROUNDCUBEMAIL_DB_TYPE: mysql ROUNDCUBEMAIL_DB_TYPE: mysql
ROUNDCUBEMAIL_DB_HOST: db ROUNDCUBEMAIL_DB_HOST: db
ROUNDCUBEMAIL_DB_USER: mail ROUNDCUBEMAIL_DB_USER: mail
ROUNDCUBEMAIL_DB_PASSWORD: BGun02otSchuj3z ROUNDCUBEMAIL_DB_PASSWORD: db_password
ROUNDCUBEMAIL_DB_NAME: postfix ROUNDCUBEMAIL_DB_NAME: postfix
labels: labels:
- "traefik.frontend.rule=Host:mail.creditcards.bayern" - "traefik.frontend.rule=Host:mail.creditcards.bayern"
@ -112,7 +112,7 @@ services:
DBHOST: db DBHOST: db
DBUSER: mail DBUSER: mail
DBNAME: postfix DBNAME: postfix
DBPASS: BGun02otSchuj3z DBPASS: db_password
SMTPHOST: smtp SMTPHOST: smtp
DOMAIN: creditcards.bayern DOMAIN: creditcards.bayern
labels: labels:
@ -206,15 +206,15 @@ services:
traefik: traefik:
container_name: traefik container_name: traefik
image: traefik # The official Traefik docker image image: traefik
command: --api --docker # Enables the web UI and tells Traefik to listen to docker command: --api --docker # Enables the web UI and tells Traefik to listen to docker
restart: always restart: always
ports: ports:
- "80:80" # The HTTP port - "80:80"
- "443:443" - "443:443"
- "8080:8080" # The Web UI (enabled by --api) - "8080:8080"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events - /var/run/docker.sock:/var/run/docker.sock
- ./traefik/:/etc/traefik - ./traefik/:/etc/traefik
labels: labels:
- "traefik.frontend.rule=Host:traefik.creditcards.bayern" - "traefik.frontend.rule=Host:traefik.creditcards.bayern"