mirror of
https://github.com/bokysan/docker-postfix.git
synced 2024-09-20 06:46:10 +08:00
Major update, better support for handling TLS connections.
This update includes a few features that make it easier to work with relay hosts. The configuration now uses built-in (Alpine) TLS certificates and allows the user to specify how the Postfix should establish a TLS connection. Configure it using `RELAYHOST_TLS_LEVEL`, if needed. Another configuration option was added, `MESSAGE_SIZE_LIMIT`, which enables you to reject messages exceeding certain limits. Especially useful with relay servers, to prevent messages getting stuck in Postfix queue. Use the `INBOUND_DEBUGGING` parameter to enable additional postfix logs for incoming messages when things go south. Use `TZ` parameter to set the timezone of the container (especially useful for logs and dates in the messages). *Notice that `ALLOWED_SENDER_DOMAINS` is now a neccessary parameter.* Turns out that due to Postfix anti-spam configuration there's no way to start it properly without specifying the domains it will relay email for. This commit also includes a few samples of running the container: - standalone - in [docker-compose](https://docs.docker.com/compose/) Further work: add a working Kubernetes / Helm chart example.
This commit is contained in:
parent
9254d5ef36
commit
aa66569ac1
13
Dockerfile
13
Dockerfile
|
@ -3,6 +3,8 @@ MAINTAINER Bojan Cekrlic - https://github.com/bokysan/docker-postfix/
|
|||
|
||||
# See README.md for details
|
||||
|
||||
# Set the timezone for the container, if needed.
|
||||
ENV TZ=
|
||||
# Postfix myhostname
|
||||
ENV HOSTNAME=
|
||||
# Host that relays your msgs
|
||||
|
@ -11,14 +13,21 @@ ENV RELAYHOST=
|
|||
ENV RELAYHOST_USERNAME=
|
||||
# An (optional) login password for the relay server
|
||||
ENV RELAYHOST_PASSWORD=
|
||||
# Define relay host TLS connection level. See http://www.postfix.org/postconf.5.html#smtp_tls_security_level for details.
|
||||
# By default, the permissive level ("may") is used, if not defined.
|
||||
ENV RELAYHOST_TLS_LEVEL=
|
||||
# Allow domains from per Network ( default 127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 )
|
||||
ENV MYNETWORKS=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
|
||||
# Allow any sender domains
|
||||
ENV ALLOWED_SENDER_DOMAINS=
|
||||
# Attachments size. 0 means unlimited. Usually needs to be set if your relay host has an attachment size limit
|
||||
ENV MESSAGE_SIZE_LIMIT=
|
||||
# Enable additional debugging for connections to postfix
|
||||
ENV INBOUND_DEBUGGING=
|
||||
|
||||
# Install supervisor, postfix and bash (because run.sh is not sh-complatible)
|
||||
# Install supervisor, postfix
|
||||
RUN true && \
|
||||
apk add --no-cache --update postfix ca-certificates supervisor rsyslog bash && \
|
||||
apk add --no-cache --update postfix ca-certificates tzdata supervisor rsyslog && \
|
||||
apk add --no-cache --upgrade musl musl-utils && \
|
||||
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)
|
||||
|
||||
|
|
38
README.md
38
README.md
|
@ -18,7 +18,7 @@ This is a _server side_ POSTFIX image, geared towards emails that need to be sen
|
|||
|
||||
To run the container, do the following:
|
||||
```
|
||||
docker run --rm --name postfix -p 1587:587 boky/postfix
|
||||
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
You can now send emails by using `localhost:1587` as your SMTP server address. **Please note that
|
||||
|
@ -76,6 +76,22 @@ If your end server requires you to authenticate with username/password, add them
|
|||
docker run --rm --name postfix -e RELAYHOST=mail.google.com -e RELAYHOST_USERNAME=hello@gmail.com -e RELAYHOST_PASSWORD=world -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
### `RELAYHOST_TLS_LEVEL`
|
||||
|
||||
Define relay host TLS connection level. See http://www.postfix.org/postconf.5.html#smtp_tls_security_level for details. By default, the permissive level ("may") is used, which basically means "use TLS if available" and should be a sane default in most cases.
|
||||
|
||||
This level defines how the postfix will connect to your upstream server.
|
||||
|
||||
### `MESSAGE_SIZE_LIMIT`
|
||||
|
||||
Define the maximum size of the message, in bytes.
|
||||
See more in [Postfix documentation](http://www.postfix.org/postconf.5.html#message_size_limit).
|
||||
|
||||
By default, this limit is set to 0 (zero), which means unlimited. Why would you want to set this? Well, this is especially useful in relation
|
||||
with `RELAYHOST` setting. If your relay host has a message limit (and usually it does), set it also here. This will help you "fail fast" --
|
||||
your message will be rejected at the time of sending instead having it stuck in the outbound queue indefenetly.
|
||||
|
||||
|
||||
### `MYNETWORKS`
|
||||
|
||||
This implementation is meant for private installations -- so that when you configure your services using _docker compose_
|
||||
|
@ -92,18 +108,23 @@ docker run --rm --name postfix -e "MYNETWORKS=10.1.2.0/24" -p 1587:587 boky/post
|
|||
|
||||
### `ALLOWED_SENDER_DOMAINS`
|
||||
|
||||
If your application is sending email from just a few domains (and most application do), it is a good practice to lock the
|
||||
POSTFIX further down and accept email from these domains only.
|
||||
Due to in-built spam protection in [Postfix](http://www.postfix.org/postconf.5.html#smtpd_relay_restrictions) you will need to specify
|
||||
sender domains -- the domains you are using to send your emails from, otherwise Postfix will refuse to start.
|
||||
|
||||
Example:
|
||||
```
|
||||
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
### `INBOUND_DEBUGGING`
|
||||
|
||||
Enable additional debugging for any connection comming from `MYNETWORKS`. Set to a non-empty string (usually "1" or "yes") to
|
||||
enable debugging.
|
||||
|
||||
## Extending the image
|
||||
|
||||
If you need to add custom configuration to postfix or have it do something outside of the scope of this configuration, simply
|
||||
add your scripts to `/docker-init.db/`. All files with the `.sh` extension will be executed automatically at the end of the
|
||||
add your scripts to `/docker-init.db/`: All files with the `.sh` extension will be executed automatically at the end of the
|
||||
startup script.
|
||||
|
||||
E.g.: create a custom `Dockerfile` like this:
|
||||
|
@ -115,6 +136,15 @@ ADD Dockerfiles/additional-config.sh /docker-init.db/
|
|||
|
||||
Build it with docker and your script will be automatically executed before Postfix starts.
|
||||
|
||||
Or -- alternately -- bind this folder in your docker config and put your scripts there. Useful if you need to add additional config
|
||||
to your postfix server or override configs created by the script.
|
||||
|
||||
For example, your script could contain something like this:
|
||||
```
|
||||
#!/bin/sh
|
||||
postconf -e "address_verify_negative_cache=yes"
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
Postfix will run the master proces as `root`, because that's how it's designed. Subprocesses will run under the `postfix` account
|
||||
|
|
139
run.sh
139
run.sh
|
@ -1,8 +1,67 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "******************************"
|
||||
echo "**** POSTFIX STARTING UP *****"
|
||||
echo "******************************"
|
||||
reset=""
|
||||
yellow=""
|
||||
yellow_bold=""
|
||||
red=""
|
||||
orange=""
|
||||
|
||||
# Returns 0 if the specified string contains the specified substring, otherwise returns 1.
|
||||
# This exercise it required because we are using the sh-compatible interpretation instead
|
||||
# of bash.
|
||||
contains() {
|
||||
string="$1"
|
||||
substring="$2"
|
||||
if test "${string#*$substring}" != "$string"
|
||||
then
|
||||
return 0 # $substring is in $string
|
||||
else
|
||||
return 1 # $substring is not in $string
|
||||
fi
|
||||
}
|
||||
|
||||
if test -t 1; then
|
||||
# Quick and dirty test for color support
|
||||
if contains "$TERM" "256" || contains "$COLORTERM" "256" || contains "$COLORTERM" "color" || contains "$COLORTERM" "24bit"; then
|
||||
reset="\033[0m"
|
||||
green="\033[38;5;46m"
|
||||
yellow="\033[38;5;178m"
|
||||
red="\033[91m"
|
||||
orange="\033[38;5;208m"
|
||||
|
||||
emphasis="\033[38;5;226m"
|
||||
elif contains "$TERM" "xterm"; then
|
||||
reset="\033[0m"
|
||||
green="\033[32m"
|
||||
yellow="\033[33m"
|
||||
red="\033[31;1m"
|
||||
orange="\033[31m"
|
||||
|
||||
emphasis="\033[33;1m"
|
||||
fi
|
||||
fi
|
||||
|
||||
info="${green}INFO:${reset}"
|
||||
notice="${yellow}NOTE:${reset}"
|
||||
warn="${orange}WARN:${reset}"
|
||||
|
||||
echo -e "******************************"
|
||||
echo -e "**** POSTFIX STARTING UP *****"
|
||||
echo -e "******************************"
|
||||
|
||||
# Check if we need to configure the container timezone
|
||||
if [ ! -z "$TZ" ]; then
|
||||
TZ_FILE="/usr/share/zoneinfo/$TZ"
|
||||
if [ -f "$TZ_FILE" ]; then
|
||||
echo -e "‣ $notice Setting container timezone to: ${emphasis}$TZ${reset}"
|
||||
ln -snf "$TZ_FILE" /etc/localtime
|
||||
echo "$TZ" > /etc/timezone
|
||||
else
|
||||
echo -e "‣ $warn Cannot set timezone to: ${emphasis}$TZ${reset} -- this timezone does not exist."
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $info Not setting any timezone for the container"
|
||||
fi
|
||||
|
||||
# Make and reown postfix folders
|
||||
mkdir -p /var/spool/postfix/ && mkdir -p /var/spool/postfix/pid
|
||||
|
@ -20,41 +79,58 @@ postconf -e mydestination=
|
|||
# Don't relay for any domains
|
||||
postconf -e relay_domains=
|
||||
|
||||
# As this is a server-based service, allow any message size -- we hope the sender knows
|
||||
# what he is doing
|
||||
postconf -e "message_size_limit=0"
|
||||
if [ ! -z "$MESSAGE_SIZE_LIMIT" ]; then
|
||||
echo -e "‣ $notice Restricting message_size_limit to: ${emphasis}$MESSAGE_SIZE_LIMIT bytes${reset}"
|
||||
postconf -e "message_size_limit=$MESSAGE_SIZE_LIMIT"
|
||||
else
|
||||
# As this is a server-based service, allow any message size -- we hope the sender knows
|
||||
# what he is doing
|
||||
echo -e "‣ $info Using ${emphasis}unlimited${reset} message size."
|
||||
postconf -e "message_size_limit=0"
|
||||
fi
|
||||
|
||||
# Reject invalid HELOs
|
||||
postconf -e smtpd_delay_reject=yes
|
||||
postconf -e smtpd_helo_required=yes
|
||||
postconf -e "smtpd_helo_restrictions=permit_mynetworks,reject_invalid_helo_hostname,permit"
|
||||
postconf -e "smtpd_sender_restrictions=permit_mynetworks"
|
||||
|
||||
# Set up host name
|
||||
if [ ! -z "$HOSTNAME" ]; then
|
||||
echo -e "‣ $notice Setting myhostname: ${emphasis}$HOSTNAME${reset}"
|
||||
postconf -e myhostname="$HOSTNAME"
|
||||
else
|
||||
postconf -# myhostname
|
||||
fi
|
||||
|
||||
if [ -z "$RELAYHOST_TLS_LEVEL" ]; then
|
||||
echo -e "‣ $info Setting smtp_tls_security_level: ${emphasis}may${reset}"
|
||||
postconf -e "smtp_tls_security_level=may"
|
||||
else
|
||||
echo -e "‣ $notice Setting smtp_tls_security_level: ${emphasis}$RELAYHOST_TLS_LEVEL${reset}"
|
||||
postconf -e "smtp_tls_security_level=$RELAYHOST_TLS_LEVEL"
|
||||
fi
|
||||
|
||||
# Set up a relay host, if needed
|
||||
if [ ! -z "$RELAYHOST" ]; then
|
||||
echo -n "- Forwarding all emails to $RELAYHOST"
|
||||
postconf -e relayhost=$RELAYHOST
|
||||
echo -en "‣ $notice Forwarding all emails to ${emphasis}$RELAYHOST${reset}"
|
||||
postconf -e "relayhost=$RELAYHOST"
|
||||
# Alternately, this could be a folder, like this:
|
||||
# smtp_tls_CApath
|
||||
postconf -e "smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
if [ -n "$RELAYHOST_USERNAME" ] && [ -n "$RELAYHOST_PASSWORD" ]; then
|
||||
echo " using username $RELAYHOST_USERNAME."
|
||||
echo -e " using username ${emphasis}$RELAYHOST_USERNAME${reset} and password ${emphasis}(redacted)${reset}."
|
||||
echo "$RELAYHOST $RELAYHOST_USERNAME:$RELAYHOST_PASSWORD" >> /etc/postfix/sasl_passwd
|
||||
postmap hash:/etc/postfix/sasl_passwd
|
||||
postconf -e "smtp_sasl_auth_enable=yes"
|
||||
postconf -e "smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd"
|
||||
postconf -e "smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt"
|
||||
postconf -e "smtp_enforce_tls=yes"
|
||||
postconf -e "smtp_sasl_security_options=noanonymous"
|
||||
else
|
||||
echo " without any authentication. Make sure your server is configured to accept emails coming from this IP."
|
||||
echo -e " without any authentication. ${emphasis}Make sure your server is configured to accept emails coming from this IP.${reset}"
|
||||
fi
|
||||
else
|
||||
echo "- Will try to deliver emails directly to the final server. Make sure your DNS is setup properly!"
|
||||
echo -e "‣ $notice Will try to deliver emails directly to the final server. ${emphasis}Make sure your DNS is setup properly!${reset}"
|
||||
postconf -# relayhost
|
||||
postconf -# smtp_sasl_auth_enable
|
||||
postconf -# smtp_sasl_password_maps
|
||||
|
@ -62,19 +138,27 @@ else
|
|||
fi
|
||||
|
||||
if [ ! -z "$MYNETWORKS" ]; then
|
||||
postconf -e mynetworks=$MYNETWORKS
|
||||
echo -e "‣ $notice Using custom allowed networks: ${emphasis}$MYNETWORKS${reset}"
|
||||
else
|
||||
postconf -e "mynetworks=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
|
||||
echo -e "‣ $info Using default private network list for trusted networks."
|
||||
MYNETWORKS="127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
|
||||
fi
|
||||
|
||||
postconf -e "mynetworks=$MYNETWORKS"
|
||||
|
||||
if [ ! -z "$INBOUND_DEBUGGING" ]; then
|
||||
echo -e "‣ $notice Enabling additional debbuging for: ${emphasis}$MYNETWORKS${reset}"
|
||||
postconf -e "debug_peer_list=$MYNETWORKS"
|
||||
fi
|
||||
|
||||
# Split with space
|
||||
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
|
||||
echo -n "- Setting up allowed SENDER domains:"
|
||||
echo -en "‣ $notice Setting up allowed SENDER domains:"
|
||||
allowed_senders=/etc/postfix/allowed_senders
|
||||
rm -f $allowed_senders $allowed_senders.db > /dev/null
|
||||
touch $allowed_senders
|
||||
for i in $ALLOWED_SENDER_DOMAINS; do
|
||||
echo -n " $i"
|
||||
echo -ne " ${emphasis}$i${reset}"
|
||||
echo -e "$i\tOK" >> $allowed_senders
|
||||
done
|
||||
echo
|
||||
|
@ -86,31 +170,28 @@ if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
|
|||
# will be able to send out emails much faster, as there will be no lookup and lockup if the target server is not responing or available.
|
||||
# postconf -e "smtpd_recipient_restrictions=reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unverified_recipient, check_sender_access hash:$allowed_senders, reject"
|
||||
postconf -e "smtpd_recipient_restrictions=reject_non_fqdn_recipient, reject_unknown_recipient_domain, check_sender_access hash:$allowed_senders, reject"
|
||||
else
|
||||
postconf -# "smtpd_restriction_classes"
|
||||
# Update: loosen up on RCPT checks. This will mean we might get some emails which are not valid, but the service connecting
|
||||
# will be able to send out emails much faster, as there will be no lookup and lockup if the target server is not responing or available.
|
||||
# postconf -e "smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,reject_unverified_recipient"
|
||||
postconf -e "smtpd_recipient_restrictions=reject_non_fqdn_recipient, reject_unknown_recipient_domain, defer_unauth_destination, permit"
|
||||
fi
|
||||
|
||||
# Since we are behind closed doors, let's just permit all relays.
|
||||
postconf -e "smtpd_relay_restrictions=permit"
|
||||
# Since we are behind closed doors, let's just permit all relays.
|
||||
postconf -e "smtpd_relay_restrictions=permit"
|
||||
else
|
||||
echo -e "ERROR: You need to specify sender domains otherwise Postfix will not run!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Use 587 (submission)
|
||||
sed -i -r -e 's/^#submission/submission/' /etc/postfix/master.cf
|
||||
|
||||
if [ -d /docker-init.db/ ]; then
|
||||
echo "- Executing any found custom scripts..."
|
||||
echo -e "‣ $notice Executing any found custom scripts..."
|
||||
for f in /docker-init.db/*; do
|
||||
case "$f" in
|
||||
*.sh) chmod +x "$f"; echo -e"\trunning $f"; . "$f" ;;
|
||||
*.sh) chmod +x "$f"; echo -e "\trunning ${emphasis}$f${reset}"; . "$f" ;;
|
||||
*) echo "$0: ignoring $f" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
echo "- Staring rsyslog and postfix"
|
||||
echo -e "‣ $notice Staring ${emphasis}rsyslog${reset} and ${emphasis}postfix${reset}"
|
||||
exec supervisord -c /etc/supervisord.conf
|
||||
|
||||
|
|
4
sample/command-line/start.sh
Executable file
4
sample/command-line/start.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
cd $(dirname $0)/../../
|
||||
docker build . -t boky/postfix && \
|
||||
docker run -it --rm --name postfix -p 1587:587 $* boky/postfix
|
9
sample/docker-compose/docker-compose.yml
Normal file
9
sample/docker-compose/docker-compose.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
version: "3.7"
|
||||
services:
|
||||
smtp-relay:
|
||||
build: ../..
|
||||
restart: always
|
||||
env_file:
|
||||
- "./sample.env"
|
||||
expose:
|
||||
- "587"
|
7
sample/docker-compose/sample.env
Normal file
7
sample/docker-compose/sample.env
Normal file
|
@ -0,0 +1,7 @@
|
|||
TZ=Europe/Amsterdam
|
||||
HOSTNAME=smtp-relay
|
||||
RELAYHOST=smtp.gmail.com:587
|
||||
RELAYHOST_USERNAME=you@gmail.com
|
||||
RELAYHOST_PASSWORD=yourgmailapppassword
|
||||
MESSAGE_SIZE_LIMIT=26214400
|
||||
ALLOWED_SENDER_DOMAINS=example.org
|
3
sample/docker-compose/start.sh
Executable file
3
sample/docker-compose/start.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd $(dirname $0)
|
||||
docker-compose up
|
Loading…
Reference in a new issue