mirror of
https://github.com/bokysan/docker-postfix.git
synced 2025-09-11 00:44:37 +08:00
Add the option to log to rsyslog as plain or JSON and restructure tests
- It's now possible to choose the logging type - either 'plain' or 'json' - The code is ready to support multiple integration tests (with different configurations) - `OPENDKIM_` and `POSTFIX_` variables are handled properly and recorded in the corresponding files. (This had a downfall that `bash` now needs to be installed, so we can probably simplify some of the shell scripts.)
This commit is contained in:
parent
9b1902c047
commit
ff2d080279
17 changed files with 211 additions and 17 deletions
|
@ -29,6 +29,8 @@ ENV MESSAGE_SIZE_LIMIT=
|
||||||
ENV INBOUND_DEBUGGING=
|
ENV INBOUND_DEBUGGING=
|
||||||
# DKIM domain selector. If not set, the default (mail) will be used
|
# DKIM domain selector. If not set, the default (mail) will be used
|
||||||
ENV DKIM_SELECTOR=
|
ENV DKIM_SELECTOR=
|
||||||
|
# Logformat. Defaults to "plain". Can be either "plain" or "json".
|
||||||
|
ENV LOG_FORMAT=
|
||||||
|
|
||||||
# Install supervisor, postfix
|
# Install supervisor, postfix
|
||||||
# Install postfix first to get the first account (101)
|
# Install postfix first to get the first account (101)
|
||||||
|
@ -37,13 +39,12 @@ RUN true && \
|
||||||
apk add --no-cache --upgrade cyrus-sasl cyrus-sasl-plain cyrus-sasl-login && \
|
apk add --no-cache --upgrade cyrus-sasl cyrus-sasl-plain cyrus-sasl-login && \
|
||||||
apk add --no-cache postfix && \
|
apk add --no-cache postfix && \
|
||||||
apk add --no-cache opendkim && \
|
apk add --no-cache opendkim && \
|
||||||
apk add --no-cache ca-certificates tzdata supervisor rsyslog && \
|
apk add --no-cache --upgrade ca-certificates tzdata supervisor rsyslog musl musl-utils bash && \
|
||||||
apk add --no-cache --upgrade musl musl-utils && \
|
|
||||||
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)
|
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)
|
||||||
|
|
||||||
# Set up configuration
|
# Set up configuration
|
||||||
COPY /configs/supervisord.conf /etc/supervisord.conf
|
COPY /configs/supervisord.conf /etc/supervisord.conf
|
||||||
COPY /configs/rsyslog.conf /etc/rsyslog.conf
|
COPY /configs/rsyslog*.conf /etc/
|
||||||
COPY /configs/opendkim.conf /etc/opendkim/opendkim.conf
|
COPY /configs/opendkim.conf /etc/opendkim/opendkim.conf
|
||||||
COPY /configs/smtp_header_checks /etc/postfix/smtp_header_checks
|
COPY /configs/smtp_header_checks /etc/postfix/smtp_header_checks
|
||||||
COPY /scripts/*.sh /
|
COPY /scripts/*.sh /
|
||||||
|
|
32
README.md
32
README.md
|
@ -219,6 +219,8 @@ This means:
|
||||||
|
|
||||||
## Extending the image
|
## Extending the image
|
||||||
|
|
||||||
|
### Using custom init scripts
|
||||||
|
|
||||||
If you need to add custom configuration to postfix or have it do something outside of the scope of this configuration, simply
|
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.
|
startup script.
|
||||||
|
@ -243,7 +245,37 @@ For example, your script could contain something like this:
|
||||||
postconf -e "address_verify_negative_cache=yes"
|
postconf -e "address_verify_negative_cache=yes"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Overriding specific postfix settings
|
||||||
|
|
||||||
|
Any Postfix [configuration option](http://www.postfix.org/postconf.5.html) can be overriden using `POSTFIX_<name>` environment variables, e.g.
|
||||||
|
`POSTFIX_allow_mail_to_commands=alias,forward,include`. Specifying no content (empty variable) will remove that variable from postfix config.
|
||||||
|
|
||||||
|
Any OpenDKIM [configuration option](http://opendkim.org/opendkim.conf.5.html) can be overriden using `OPENDKIM_<name>` environment variables, e.g.
|
||||||
|
`OPENDKIM_RequireSafeKeys=yes`. Specifying no content (empty variable) will remove that variable from OpenDKIM config.
|
||||||
|
|
||||||
|
## Log format
|
||||||
|
|
||||||
|
The image will by default output logs in human-readable (`plain`) format. If you are deploying the image to Kubernetes, it might be worth chaging
|
||||||
|
the output format to `json` as it's more easily parsable by tools such as [Prometheus](https://prometheus.io/).
|
||||||
|
|
||||||
|
To change the log format, set the (unsuprisingly named) variable `LOG_FORMAT=json`.
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
Postfix will run the master proces as `root`, because that's how it's designed. Subprocesses will run under the `postfix` account
|
Postfix will run the master proces as `root`, because that's how it's designed. Subprocesses will run under the `postfix` account
|
||||||
which will use `UID:GID` of `100:101`. `opendkim` will run under account `102:103`.
|
which will use `UID:GID` of `100:101`. `opendkim` will run under account `102:103`.
|
||||||
|
|
||||||
|
## Similar projects
|
||||||
|
|
||||||
|
There are may other project offering similar functionality. The aim of this project, however, is:
|
||||||
|
|
||||||
|
- to make it as simple as possible to run the relay, without going too much into postfix configuration details
|
||||||
|
- to make as small image as possible (hence basing on Alpine linux)
|
||||||
|
- to make the image and the corresponding code testable
|
||||||
|
|
||||||
|
The other projects are, in completely random order:
|
||||||
|
|
||||||
|
- [wader/postfix-relay](https://github.com/wader/postfix-relay)
|
||||||
|
- [catatnight/postfix](https://github.com/catatnight/docker-postfix)
|
||||||
|
- [juanluisbaptiste/docker-postfix](https://github.com/juanluisbaptiste/docker-postfix)
|
||||||
|
- [docker-mail-relay](https://github.com/alterrebe/docker-mail-relay)
|
||||||
|
|
|
@ -10,8 +10,7 @@ $Umask 0022
|
||||||
|
|
||||||
template (name="devicelog" type="string" string="/dev/stdout")
|
template (name="devicelog" type="string" string="/dev/stdout")
|
||||||
|
|
||||||
template(name="json_syslog"
|
template(name="json" type="list") {
|
||||||
type="list") {
|
|
||||||
constant(value="{")
|
constant(value="{")
|
||||||
constant(value="\"@timestamp\":\"") property(name="timereported" dateFormat="rfc3339")
|
constant(value="\"@timestamp\":\"") property(name="timereported" dateFormat="rfc3339")
|
||||||
constant(value="\",\"type\":\"syslog_json")
|
constant(value="\",\"type\":\"syslog_json")
|
||||||
|
@ -26,17 +25,29 @@ template(name="json_syslog"
|
||||||
constant(value="\",\"facility\":\"") property(name="syslogfacility")
|
constant(value="\",\"facility\":\"") property(name="syslogfacility")
|
||||||
constant(value="\",\"severity_label\":\"") property(name="syslogseverity-text")
|
constant(value="\",\"severity_label\":\"") property(name="syslogseverity-text")
|
||||||
constant(value="\",\"facility_label\":\"") property(name="syslogfacility-text")
|
constant(value="\",\"facility_label\":\"") property(name="syslogfacility-text")
|
||||||
constant(value="\",\"message\":\"") property(name="rawmsg" format="json")
|
constant(value="\",\"message\":\"") property(name="msg" format="json")
|
||||||
constant(value="\",\"end_msg\":\"")
|
constant(value="\",\"end_msg\":\"")
|
||||||
constant(value="\"}\n")
|
constant(value="\"}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template(name="plain" type="list") {
|
||||||
|
property(name="timereported" dateFormat="rfc3339")
|
||||||
|
constant(value=" ")
|
||||||
|
property(name="syslogseverity-text" caseconversion="upper" fixedwidth="on" position.to="7")
|
||||||
|
constant(value=" ")
|
||||||
|
property(name="syslogtag")
|
||||||
|
property(name="msg" spifno1stsp="on")
|
||||||
|
property(name="msg" droplastlf="on")
|
||||||
|
constant(value="\n")
|
||||||
|
}
|
||||||
|
|
||||||
if $syslogseverity <= '6' then {
|
if $syslogseverity <= '6' then {
|
||||||
# matching logs will be saved
|
# matching logs will be saved
|
||||||
action(type="omfile" DynaFile="devicelog" template="json_syslog" DirCreateMode="0755" FileCreateMode="0644")
|
action(type="omfile" DynaFile="devicelog" template="<log-format>" DirCreateMode="0755" FileCreateMode="0644")
|
||||||
# enable below to stop processing further this log
|
# enable below to stop processing further this log
|
||||||
stop
|
stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include(file="/etc/rsyslog.d/*.conf" mode="optional")
|
||||||
|
|
||||||
stop
|
stop
|
|
@ -1,3 +1,14 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
cd integration-tests
|
cd integration-tests
|
||||||
docker-compose up --build --abort-on-container-exit --exit-code-from tests
|
for i in `find -maxdepth 1 -type d`; do
|
||||||
|
i="$(basename "$i")"
|
||||||
|
if [ "$i" == "tester" ] || [ "$i" == "." ] || [ "$i" == ".." ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
(
|
||||||
|
echo "$i"
|
||||||
|
cd "$i"
|
||||||
|
docker-compose up --build --abort-on-container-exit --exit-code-from tests
|
||||||
|
)
|
||||||
|
done
|
|
@ -4,7 +4,7 @@ services:
|
||||||
hostname: "postfix"
|
hostname: "postfix"
|
||||||
image: "boky/postfix"
|
image: "boky/postfix"
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ../..
|
||||||
restart: always
|
restart: always
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD", "sh", "-c", "netstat -an | fgrep 587 | fgrep -q LISTEN" ]
|
test: [ "CMD", "sh", "-c", "netstat -an | fgrep 587 | fgrep -q LISTEN" ]
|
||||||
|
@ -21,7 +21,7 @@ services:
|
||||||
image: "boky/postfix-integration-test"
|
image: "boky/postfix-integration-test"
|
||||||
restart: "no"
|
restart: "no"
|
||||||
volumes:
|
volumes:
|
||||||
- "..:/code"
|
- "../tester:/code"
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ../tester
|
||||||
command: "/code/integration-tests/"
|
command: "/"
|
|
@ -12,7 +12,6 @@ if [ -z "$TO" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Wait for postfix to startup
|
# Wait for postfix to startup
|
||||||
echo "Waiting for startup..."
|
|
||||||
wait-for-service -q tcp://postfix_test_587:587
|
wait-for-service -q tcp://postfix_test_587:587
|
||||||
|
|
||||||
SMTP_DATA="-smtp postfix_test_587 -port 587"
|
SMTP_DATA="-smtp postfix_test_587 -port 587"
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
announce_startup() {
|
announce_startup() {
|
||||||
echo -e "******************************"
|
echo -e "******************************"
|
||||||
|
@ -21,6 +21,15 @@ setup_timezone() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rsyslog_log_format() {
|
||||||
|
local log_format="${LOG_FORMAT}"
|
||||||
|
if [[ -z "${log_format}" ]]; then
|
||||||
|
log_format="plain"
|
||||||
|
fi
|
||||||
|
echo -e "‣ $info Using ${emphasis}${log_format}${reset} log format for rsyslog."
|
||||||
|
sed -i -E "s/<log-format>/${log_format}/" /etc/rsyslog.conf
|
||||||
|
}
|
||||||
|
|
||||||
reown_folders() {
|
reown_folders() {
|
||||||
mkdir -p /var/spool/postfix/ && mkdir -p /var/spool/postfix/pid
|
mkdir -p /var/spool/postfix/ && mkdir -p /var/spool/postfix/pid
|
||||||
chown root: /var/spool/postfix/
|
chown root: /var/spool/postfix/
|
||||||
|
@ -250,6 +259,52 @@ postfix_setup_dkim() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opendkim_custom_commands() {
|
||||||
|
local setting
|
||||||
|
local key
|
||||||
|
local padded_key
|
||||||
|
local value
|
||||||
|
for setting in ${!OPENDKIM_*}; do
|
||||||
|
key="${setting:9}"
|
||||||
|
value="${!setting}"
|
||||||
|
if [ -n "${value}" ]; then
|
||||||
|
if [ "${#key}" -gt 23 ]; then
|
||||||
|
padded_key="${key} "
|
||||||
|
else
|
||||||
|
padded_key="$(printf %-24s "${key}")"
|
||||||
|
fi
|
||||||
|
if cat /etc/opendkim/opendkim.conf | egrep -q "^[[:space:]]*#?[[:space:]]*${key}"; then
|
||||||
|
echo -e "‣ $info Updating custom OpenDKIM setting: ${emphasis}${key}=${value}${reset}"
|
||||||
|
sed -i -E "s/^[ \t]*#?[ \t]*${key}[ \t]*.+$/${padded_key}${value}/" /etc/opendkim/opendkim.conf
|
||||||
|
else
|
||||||
|
echo -e "‣ $info Adding custom OpenDKIM setting: ${emphasis}${key}=${value}${reset}"
|
||||||
|
echo "Adding ${padded_key}${value}"
|
||||||
|
echo "${padded_key}${value}" >> /etc/opendkim/opendkim.conf
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "‣ $info Deleting custom OpenDKIM setting: ${emphasis}${key}${reset}"
|
||||||
|
sed -i -E "/^[ \t]*#?[ \t]*${key}[ \t]*.+$/d" /etc/opendkim/opendkim.conf
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
postfix_custom_commands() {
|
||||||
|
local setting
|
||||||
|
local key
|
||||||
|
local value
|
||||||
|
for setting in ${!POSTFIX_*}; do
|
||||||
|
key="${setting:8}"
|
||||||
|
value="${!setting}"
|
||||||
|
if [ -n "${value}" ]; then
|
||||||
|
echo -e "‣ $info Applying custom postfix setting: ${emphasis}${key}=${value}${reset}"
|
||||||
|
postconf -e "${key}=${value}"
|
||||||
|
else
|
||||||
|
echo -e "‣ $info Deleting custom postfix setting: ${emphasis}${key}${reset}"
|
||||||
|
postconf -# "${key}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
postfix_open_submission_port() {
|
postfix_open_submission_port() {
|
||||||
# Use 587 (submission)
|
# Use 587 (submission)
|
||||||
sed -i -r -e 's/^#submission/submission/' /etc/postfix/master.cf
|
sed -i -r -e 's/^#submission/submission/' /etc/postfix/master.cf
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
reset=""
|
reset=""
|
||||||
yellow=""
|
yellow=""
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
. /common.sh
|
. /common.sh
|
||||||
|
@ -6,6 +6,7 @@ set -e
|
||||||
|
|
||||||
announce_startup # Print startup banner
|
announce_startup # Print startup banner
|
||||||
setup_timezone # Check if we need to configure the container timezone
|
setup_timezone # Check if we need to configure the container timezone
|
||||||
|
rsyslog_log_format # Setup rsyslog output format
|
||||||
reown_folders # Make and reown postfix folders
|
reown_folders # Make and reown postfix folders
|
||||||
postfix_disable_utf8 # Disable SMTPUTF8, because libraries (ICU) are missing in alpine
|
postfix_disable_utf8 # Disable SMTPUTF8, because libraries (ICU) are missing in alpine
|
||||||
postfix_create_aliases # Update aliases database. It's not used, but postfix complains if the .db file is missing
|
postfix_create_aliases # Update aliases database. It's not used, but postfix complains if the .db file is missing
|
||||||
|
@ -23,6 +24,8 @@ postfix_setup_sender_domains # Configure allowed sender domains
|
||||||
postfix_setup_masquarading # Setup masquaraded domains
|
postfix_setup_masquarading # Setup masquaraded domains
|
||||||
postfix_setup_header_checks # Enable SMTP header checks, if defined
|
postfix_setup_header_checks # Enable SMTP header checks, if defined
|
||||||
postfix_setup_dkim # Configure DKIM, if enabled
|
postfix_setup_dkim # Configure DKIM, if enabled
|
||||||
|
postfix_custom_commands # Apply custom postfix settings
|
||||||
|
opendkim_custom_commands # Apply custom OpenDKIM settings
|
||||||
postfix_open_submission_port # Enable the submission port
|
postfix_open_submission_port # Enable the submission port
|
||||||
execute_post_init_scripts # Execute any scripts found in /docker-init.db/
|
execute_post_init_scripts # Execute any scripts found in /docker-init.db/
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
RUN apk add --no-cache bash bats
|
RUN true && \
|
||||||
|
apk add --no-cache bash bats && \
|
||||||
|
apk add --no-cache --upgrade cyrus-sasl cyrus-sasl-plain cyrus-sasl-login && \
|
||||||
|
apk add --no-cache postfix && \
|
||||||
|
apk add --no-cache opendkim && \
|
||||||
|
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)
|
||||||
|
|
||||||
WORKDIR /code
|
WORKDIR /code
|
||||||
ENTRYPOINT ["/usr/bin/bats"]
|
ENTRYPOINT ["/usr/bin/bats"]
|
||||||
|
|
60
unit-tests/opendkim_custom_commands.bats
Normal file
60
unit-tests/opendkim_custom_commands.bats
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load /code/scripts/common.sh
|
||||||
|
load /code/scripts/common-run.sh
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
mkdir -p /etc/opendkim/
|
||||||
|
cat > /etc/opendkim/opendkim.conf <<-EOF
|
||||||
|
AutoRestart Yes
|
||||||
|
AutoRestartRate 10/1h
|
||||||
|
UMask 002
|
||||||
|
Syslog Yes
|
||||||
|
SyslogSuccess Yes
|
||||||
|
LogWhy No
|
||||||
|
|
||||||
|
Canonicalization relaxed/simple
|
||||||
|
RequireSafeKeys no
|
||||||
|
|
||||||
|
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
|
||||||
|
InternalHosts refile:/etc/opendkim/TrustedHosts
|
||||||
|
KeyTable refile:/etc/opendkim/KeyTable
|
||||||
|
SigningTable refile:/etc/opendkim/SigningTable
|
||||||
|
|
||||||
|
Mode sv
|
||||||
|
PidFile /var/run/opendkim/opendkim.pid
|
||||||
|
SignatureAlgorithm rsa-sha256
|
||||||
|
|
||||||
|
UserID opendkim:opendkim
|
||||||
|
Socket inet:8891@localhost
|
||||||
|
|
||||||
|
SignHeaders From,Sender,To,CC,Subject,Message-Id,Date,MIME-Version,Content-Type,Reply-To
|
||||||
|
OversignHeaders From,Sender,To,CC,Subject,Message-Id,Date,MIME-Version,Content-Type,Reply-To
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
rm -f /etc/opendkim/opendkim.conf
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Make sure that opendkim_custom_commands changes lines" {
|
||||||
|
local OPENDKIM_RequireSafeKeys=yes
|
||||||
|
opendkim_custom_commands
|
||||||
|
cat /etc/opendkim/opendkim.conf | fgrep -qx "RequireSafeKeys yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Make sure that opendkim_custom_commands adds lines" {
|
||||||
|
local OPENDKIM_CaptureUnknownErrors=yes
|
||||||
|
opendkim_custom_commands
|
||||||
|
cat /etc/opendkim/opendkim.conf | fgrep -qx "CaptureUnknownErrors yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Make sure that opendkim_custom_commands removes lines" {
|
||||||
|
local OPENDKIM_SignHeaders=
|
||||||
|
opendkim_custom_commands
|
||||||
|
if cat /etc/opendkim/opendkim.conf | egrep -q "^SignHeaders"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
17
unit-tests/postfix_custom_commands.bats
Normal file
17
unit-tests/postfix_custom_commands.bats
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load /code/scripts/common.sh
|
||||||
|
load /code/scripts/common-run.sh
|
||||||
|
|
||||||
|
|
||||||
|
@test "Make sure that postfix_custom_commands adds lines" {
|
||||||
|
local POSTFIX_alias_database=hash:/etc/mail/aliases
|
||||||
|
postfix_custom_commands
|
||||||
|
cat /etc/postfix/main.cf | fgrep -qx "alias_database = hash:/etc/mail/aliases"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Make sure that postfix_custom_commands removes lines" {
|
||||||
|
local POSTFIX_readme_directory=
|
||||||
|
postfix_custom_commands
|
||||||
|
cat /etc/postfix/main.cf | egrep -q "^#readme_directory"
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue